mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-13 21:05:19 +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 { AuthModule } from '../auth';
|
||||||
import { ServerConfigModule, ServerService } from '../config';
|
import { ServerConfigModule } from '../config';
|
||||||
import { UserModule } from '../user';
|
import { UserModule } from '../user';
|
||||||
import { CustomSetupController } from './controller';
|
import { CustomSetupController } from './controller';
|
||||||
|
import { SetupMiddleware } from './setup';
|
||||||
@Injectable()
|
import { StaticFilesResolver } from './static';
|
||||||
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();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [AuthModule, UserModule, ServerConfigModule],
|
imports: [AuthModule, UserModule, ServerConfigModule],
|
||||||
providers: [SetupMiddleware],
|
providers: [SetupMiddleware, StaticFilesResolver],
|
||||||
controllers: [CustomSetupController],
|
controllers: [CustomSetupController],
|
||||||
})
|
})
|
||||||
export class SelfhostModule implements OnModuleInit {
|
export class SelfhostModule {}
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
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