mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-12 12:28:42 +00:00
refactor(server): cleanup selfhost module (#9038)
This commit is contained in:
@@ -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<Application>();
|
||||
// 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 {}
|
||||
|
||||
33
packages/backend/server/src/core/selfhost/setup.ts
Normal file
33
packages/backend/server/src/core/selfhost/setup.ts
Normal file
@@ -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();
|
||||
});
|
||||
};
|
||||
}
|
||||
125
packages/backend/server/src/core/selfhost/static.ts
Normal file
125
packages/backend/server/src/core/selfhost/static.ts
Normal file
@@ -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<Application>();
|
||||
// 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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user