From 1aff9de9814ffb43eafbcddf4ac910f0df4a6739 Mon Sep 17 00:00:00 2001 From: forehalo Date: Fri, 6 Dec 2024 09:23:22 +0000 Subject: [PATCH] refactor(server): cleanup selfhost module (#9038) --- .../backend/server/src/core/selfhost/index.ts | 163 +----------------- .../backend/server/src/core/selfhost/setup.ts | 33 ++++ .../server/src/core/selfhost/static.ts | 125 ++++++++++++++ 3 files changed, 164 insertions(+), 157 deletions(-) create mode 100644 packages/backend/server/src/core/selfhost/setup.ts create mode 100644 packages/backend/server/src/core/selfhost/static.ts diff --git a/packages/backend/server/src/core/selfhost/index.ts b/packages/backend/server/src/core/selfhost/index.ts index 004edaf218..11282bac9a 100644 --- a/packages/backend/server/src/core/selfhost/index.ts +++ b/packages/backend/server/src/core/selfhost/index.ts @@ -1,166 +1,15 @@ -import { join } from 'node:path'; +import { Module } from '@nestjs/common'; -import { - Injectable, - Module, - NestMiddleware, - OnModuleInit, -} from '@nestjs/common'; -import { HttpAdapterHost } from '@nestjs/core'; -import type { Application, Request, Response } from 'express'; -import { static as serveStatic } from 'express'; -import isMobile from 'is-mobile'; - -import { Config } from '../../fundamentals'; import { AuthModule } from '../auth'; -import { ServerConfigModule, ServerService } from '../config'; +import { ServerConfigModule } from '../config'; import { UserModule } from '../user'; import { CustomSetupController } from './controller'; - -@Injectable() -export class SetupMiddleware implements NestMiddleware { - constructor(private readonly server: ServerService) {} - - use = (req: Request, res: Response, next: (error?: Error | any) => void) => { - // never throw - this.server - .initialized() - .then(initialized => { - // Redirect to setup page if not initialized - if (!initialized && req.path !== '/admin/setup') { - res.redirect('/admin/setup'); - return; - } - - // redirect to admin page if initialized - if (initialized && req.path === '/admin/setup') { - res.redirect('/admin'); - return; - } - - next(); - }) - .catch(() => { - next(); - }); - }; -} +import { SetupMiddleware } from './setup'; +import { StaticFilesResolver } from './static'; @Module({ imports: [AuthModule, UserModule, ServerConfigModule], - providers: [SetupMiddleware], + providers: [SetupMiddleware, StaticFilesResolver], controllers: [CustomSetupController], }) -export class SelfhostModule implements OnModuleInit { - constructor( - private readonly config: Config, - private readonly adapterHost: HttpAdapterHost, - private readonly check: SetupMiddleware - ) {} - - onModuleInit() { - // in command line mode - if (!this.adapterHost.httpAdapter) { - return; - } - - const app = this.adapterHost.httpAdapter.getInstance(); - // for example, '/affine' in host [//host.com/affine] - const basePath = this.config.server.path; - const staticPath = join(this.config.projectRoot, 'static'); - - // web => { - // affine: 'static/index.html', - // selfhost: 'static/selfhost.html' - // } - // admin => { - // affine: 'static/admin/index.html', - // selfhost: 'static/admin/selfhost.html' - // } - // mobile => { - // affine: 'static/mobile/index.html', - // selfhost: 'static/mobile/selfhost.html' - // } - // NOTE(@forehalo): - // the order following routes should be respected, - // otherwise the app won't work properly. - - // START REGION: /admin - // do not allow '/index.html' url, redirect to '/' - app.get(basePath + '/admin/index.html', (_req, res) => { - return res.redirect(basePath + '/admin'); - }); - - // serve all static files - app.use( - basePath, - serveStatic(join(staticPath, 'admin'), { - redirect: false, - index: false, - fallthrough: true, - }) - ); - - // fallback all unknown routes - app.get( - [basePath + '/admin', basePath + '/admin/*'], - this.check.use, - (_req, res) => { - res.sendFile( - join( - staticPath, - 'admin', - this.config.isSelfhosted ? 'selfhost.html' : 'index.html' - ) - ); - } - ); - // END REGION - - // START REGION: /mobile - // serve all static files - app.use( - basePath, - serveStatic(join(staticPath, 'mobile'), { - redirect: false, - index: false, - fallthrough: true, - }) - ); - // END REGION - - // START REGION: / - // do not allow '/index.html' url, redirect to '/' - app.get(basePath + '/index.html', (_req, res) => { - return res.redirect(basePath); - }); - - // serve all static files - app.use( - basePath, - serveStatic(staticPath, { - redirect: false, - index: false, - fallthrough: true, - }) - ); - - // fallback all unknown routes - app.get([basePath, basePath + '/*'], this.check.use, (req, res) => { - const mobile = - this.config.AFFINE_ENV === 'dev' && - isMobile({ - ua: req.headers['user-agent'] ?? undefined, - }); - - return res.sendFile( - join( - staticPath, - mobile ? 'mobile' : '', - this.config.isSelfhosted ? 'selfhost.html' : 'index.html' - ) - ); - }); - // END REGION - } -} +export class SelfhostModule {} diff --git a/packages/backend/server/src/core/selfhost/setup.ts b/packages/backend/server/src/core/selfhost/setup.ts new file mode 100644 index 0000000000..c04e5e033e --- /dev/null +++ b/packages/backend/server/src/core/selfhost/setup.ts @@ -0,0 +1,33 @@ +import { Injectable, NestMiddleware } from '@nestjs/common'; +import type { Request, Response } from 'express'; + +import { ServerService } from '../config'; + +@Injectable() +export class SetupMiddleware implements NestMiddleware { + constructor(private readonly server: ServerService) {} + + use = (req: Request, res: Response, next: (error?: Error | any) => void) => { + // never throw + this.server + .initialized() + .then(initialized => { + // Redirect to setup page if not initialized + if (!initialized && req.path !== '/admin/setup') { + res.redirect('/admin/setup'); + return; + } + + // redirect to admin page if initialized + if (initialized && req.path === '/admin/setup') { + res.redirect('/admin'); + return; + } + + next(); + }) + .catch(() => { + next(); + }); + }; +} diff --git a/packages/backend/server/src/core/selfhost/static.ts b/packages/backend/server/src/core/selfhost/static.ts new file mode 100644 index 0000000000..9542c2dd5b --- /dev/null +++ b/packages/backend/server/src/core/selfhost/static.ts @@ -0,0 +1,125 @@ +import { join } from 'node:path'; + +import { Injectable, OnModuleInit } from '@nestjs/common'; +import { HttpAdapterHost } from '@nestjs/core'; +import type { Application } from 'express'; +import { static as serveStatic } from 'express'; +import isMobile from 'is-mobile'; + +import { Config } from '../../fundamentals'; +import { SetupMiddleware } from './setup'; + +@Injectable() +export class StaticFilesResolver implements OnModuleInit { + constructor( + private readonly config: Config, + private readonly adapterHost: HttpAdapterHost, + private readonly check: SetupMiddleware + ) {} + + onModuleInit() { + // in command line mode + if (!this.adapterHost.httpAdapter) { + return; + } + + const app = this.adapterHost.httpAdapter.getInstance(); + // for example, '/affine' in host [//host.com/affine] + const basePath = this.config.server.path; + const staticPath = join(this.config.projectRoot, 'static'); + + // web => { + // affine: 'static/index.html', + // selfhost: 'static/selfhost.html' + // } + // admin => { + // affine: 'static/admin/index.html', + // selfhost: 'static/admin/selfhost.html' + // } + // mobile => { + // affine: 'static/mobile/index.html', + // selfhost: 'static/mobile/selfhost.html' + // } + // NOTE(@forehalo): + // the order following routes should be respected, + // otherwise the app won't work properly. + + // START REGION: /admin + // do not allow '/index.html' url, redirect to '/' + app.get(basePath + '/admin/index.html', (_req, res) => { + return res.redirect(basePath + '/admin'); + }); + + // serve all static files + app.use( + basePath, + serveStatic(join(staticPath, 'admin'), { + redirect: false, + index: false, + fallthrough: true, + }) + ); + + // fallback all unknown routes + app.get( + [basePath + '/admin', basePath + '/admin/*'], + this.check.use, + (_req, res) => { + res.sendFile( + join( + staticPath, + 'admin', + this.config.isSelfhosted ? 'selfhost.html' : 'index.html' + ) + ); + } + ); + // END REGION + + // START REGION: /mobile + // serve all static files + app.use( + basePath, + serveStatic(join(staticPath, 'mobile'), { + redirect: false, + index: false, + fallthrough: true, + }) + ); + // END REGION + + // START REGION: / + // do not allow '/index.html' url, redirect to '/' + app.get(basePath + '/index.html', (_req, res) => { + return res.redirect(basePath); + }); + + // serve all static files + app.use( + basePath, + serveStatic(staticPath, { + redirect: false, + index: false, + fallthrough: true, + }) + ); + + // fallback all unknown routes + app.get([basePath, basePath + '/*'], this.check.use, (req, res) => { + const mobile = + this.config.AFFINE_ENV === 'dev' && + isMobile({ + ua: req.headers['user-agent'] ?? undefined, + }); + + return res.sendFile( + join( + staticPath, + mobile ? 'mobile' : '', + this.config.isSelfhosted ? 'selfhost.html' : 'index.html' + ) + ); + }); + // END REGION + } +}