mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-14 16:46:22 +08:00
refactor(server): config system (#11081)
This commit is contained in:
@@ -1,27 +1,19 @@
|
||||
import {
|
||||
DynamicModule,
|
||||
ExecutionContext,
|
||||
ForwardReference,
|
||||
Logger,
|
||||
Module,
|
||||
} from '@nestjs/common';
|
||||
import { DynamicModule, ExecutionContext } from '@nestjs/common';
|
||||
import { ScheduleModule } from '@nestjs/schedule';
|
||||
import { ClsPluginTransactional } from '@nestjs-cls/transactional';
|
||||
import { TransactionalAdapterPrisma } from '@nestjs-cls/transactional-adapter-prisma';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import { Request, Response } from 'express';
|
||||
import { get } from 'lodash-es';
|
||||
import { ClsModule } from 'nestjs-cls';
|
||||
|
||||
import { AppController } from './app.controller';
|
||||
import {
|
||||
getOptionalModuleMetadata,
|
||||
getRequestIdFromHost,
|
||||
getRequestIdFromRequest,
|
||||
ScannerModule,
|
||||
} from './base';
|
||||
import { CacheModule } from './base/cache';
|
||||
import { AFFiNEConfig, ConfigModule, mergeConfigOverride } from './base/config';
|
||||
import { ConfigModule } from './base/config';
|
||||
import { ErrorModule } from './base/error';
|
||||
import { EventModule } from './base/event';
|
||||
import { GqlModule } from './base/graphql';
|
||||
@@ -32,12 +24,11 @@ import { MetricsModule } from './base/metrics';
|
||||
import { MutexModule } from './base/mutex';
|
||||
import { PrismaModule } from './base/prisma';
|
||||
import { RedisModule } from './base/redis';
|
||||
import { RuntimeModule } from './base/runtime';
|
||||
import { StorageProviderModule } from './base/storage';
|
||||
import { RateLimiterModule } from './base/throttler';
|
||||
import { WebSocketModule } from './base/websocket';
|
||||
import { AuthModule } from './core/auth';
|
||||
import { ADD_ENABLED_FEATURES, ServerConfigModule } from './core/config';
|
||||
import { ServerConfigModule, ServerConfigResolverModule } from './core/config';
|
||||
import { DocStorageModule } from './core/doc';
|
||||
import { DocRendererModule } from './core/doc-renderer';
|
||||
import { DocServiceModule } from './core/doc-service';
|
||||
@@ -52,10 +43,16 @@ import { SyncModule } from './core/sync';
|
||||
import { UserModule } from './core/user';
|
||||
import { VersionModule } from './core/version';
|
||||
import { WorkspaceModule } from './core/workspaces';
|
||||
import { Env } from './env';
|
||||
import { ModelsModule } from './models';
|
||||
import { REGISTERED_PLUGINS } from './plugins';
|
||||
import { CaptchaModule } from './plugins/captcha';
|
||||
import { CopilotModule } from './plugins/copilot';
|
||||
import { CustomerIoModule } from './plugins/customerio';
|
||||
import { GCloudModule } from './plugins/gcloud';
|
||||
import { LicenseModule } from './plugins/license';
|
||||
import { ENABLED_PLUGINS } from './plugins/registry';
|
||||
import { OAuthModule } from './plugins/oauth';
|
||||
import { PaymentModule } from './plugins/payment';
|
||||
import { WorkerModule } from './plugins/worker';
|
||||
|
||||
export const FunctionalityModules = [
|
||||
ClsModule.forRoot({
|
||||
@@ -91,126 +88,64 @@ export const FunctionalityModules = [
|
||||
}),
|
||||
],
|
||||
}),
|
||||
ConfigModule.forRoot(),
|
||||
RuntimeModule,
|
||||
LoggerModule,
|
||||
ScannerModule,
|
||||
PrismaModule,
|
||||
EventModule,
|
||||
ConfigModule,
|
||||
RedisModule,
|
||||
CacheModule,
|
||||
MutexModule,
|
||||
PrismaModule,
|
||||
MetricsModule,
|
||||
RateLimiterModule,
|
||||
StorageProviderModule,
|
||||
HelpersModule,
|
||||
ErrorModule,
|
||||
LoggerModule,
|
||||
WebSocketModule,
|
||||
JobModule.forRoot(),
|
||||
ModelsModule,
|
||||
];
|
||||
|
||||
function filterOptionalModule(
|
||||
config: AFFiNEConfig,
|
||||
module: AFFiNEModule | Promise<DynamicModule> | ForwardReference<any>
|
||||
) {
|
||||
// can't deal with promise or forward reference
|
||||
if (module instanceof Promise || 'forwardRef' in module) {
|
||||
return module;
|
||||
}
|
||||
|
||||
const requirements = getOptionalModuleMetadata(module, 'requires');
|
||||
// if condition not set or condition met, include the module
|
||||
if (requirements?.length) {
|
||||
const nonMetRequirements = requirements.filter(c => {
|
||||
const value = get(config, c);
|
||||
return (
|
||||
value === undefined ||
|
||||
value === null ||
|
||||
(typeof value === 'string' && value.trim().length === 0)
|
||||
);
|
||||
});
|
||||
|
||||
if (nonMetRequirements.length) {
|
||||
const name = 'module' in module ? module.module.name : module.name;
|
||||
if (!config.node.test) {
|
||||
new Logger(name).warn(
|
||||
`${name} is not enabled because of the required configuration is not satisfied.`,
|
||||
'Unsatisfied configuration:',
|
||||
...nonMetRequirements.map(config => ` AFFiNE.${config}`)
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const predicator = getOptionalModuleMetadata(module, 'if');
|
||||
if (predicator && !predicator(config)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const contribution = getOptionalModuleMetadata(module, 'contributesTo');
|
||||
if (contribution) {
|
||||
ADD_ENABLED_FEATURES(contribution);
|
||||
}
|
||||
|
||||
const subModules = getOptionalModuleMetadata(module, 'imports');
|
||||
const filteredSubModules = subModules
|
||||
?.map(subModule => filterOptionalModule(config, subModule))
|
||||
.filter(Boolean);
|
||||
Reflect.defineMetadata('imports', filteredSubModules, module);
|
||||
|
||||
return module;
|
||||
}
|
||||
|
||||
export class AppModuleBuilder {
|
||||
private readonly modules: AFFiNEModule[] = [];
|
||||
constructor(private readonly config: AFFiNEConfig) {}
|
||||
|
||||
use(...modules: AFFiNEModule[]): this {
|
||||
modules.forEach(m => {
|
||||
const result = filterOptionalModule(this.config, m);
|
||||
if (result) {
|
||||
this.modules.push(m);
|
||||
}
|
||||
this.modules.push(m);
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
useIf(
|
||||
predicator: (config: AFFiNEConfig) => boolean,
|
||||
...modules: AFFiNEModule[]
|
||||
): this {
|
||||
if (predicator(this.config)) {
|
||||
useIf(predicator: () => boolean, ...modules: AFFiNEModule[]): this {
|
||||
if (predicator()) {
|
||||
this.use(...modules);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
compile() {
|
||||
@Module({
|
||||
imports: this.modules,
|
||||
controllers: [AppController],
|
||||
})
|
||||
compile(): DynamicModule {
|
||||
class AppModule {}
|
||||
|
||||
return AppModule;
|
||||
return {
|
||||
module: AppModule,
|
||||
imports: this.modules,
|
||||
controllers: [AppController],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function buildAppModule() {
|
||||
AFFiNE = mergeConfigOverride(AFFiNE);
|
||||
const factor = new AppModuleBuilder(AFFiNE);
|
||||
export function buildAppModule(env: Env) {
|
||||
const factor = new AppModuleBuilder();
|
||||
|
||||
factor
|
||||
// basic
|
||||
.use(...FunctionalityModules)
|
||||
.use(ModelsModule)
|
||||
|
||||
// enable schedule module on graphql server and doc service
|
||||
.useIf(
|
||||
config => config.flavor.graphql || config.flavor.doc,
|
||||
() => env.flavors.graphql || env.flavors.doc,
|
||||
ScheduleModule.forRoot()
|
||||
)
|
||||
|
||||
@@ -219,46 +154,41 @@ export function buildAppModule() {
|
||||
|
||||
// business modules
|
||||
.use(
|
||||
ServerConfigModule,
|
||||
FeatureModule,
|
||||
QuotaModule,
|
||||
DocStorageModule,
|
||||
NotificationModule,
|
||||
MailModule
|
||||
)
|
||||
|
||||
// renderer server only
|
||||
.useIf(() => env.flavors.renderer, DocRendererModule)
|
||||
// sync server only
|
||||
.useIf(config => config.flavor.sync, SyncModule)
|
||||
|
||||
.useIf(() => env.flavors.sync, SyncModule)
|
||||
// graphql server only
|
||||
.useIf(
|
||||
config => config.flavor.graphql,
|
||||
VersionModule,
|
||||
() => env.flavors.graphql,
|
||||
GqlModule,
|
||||
VersionModule,
|
||||
StorageModule,
|
||||
ServerConfigModule,
|
||||
ServerConfigResolverModule,
|
||||
WorkspaceModule,
|
||||
LicenseModule
|
||||
LicenseModule,
|
||||
PaymentModule,
|
||||
CopilotModule,
|
||||
CaptchaModule,
|
||||
OAuthModule,
|
||||
CustomerIoModule
|
||||
)
|
||||
|
||||
// doc service only
|
||||
.useIf(config => config.flavor.doc, DocServiceModule)
|
||||
|
||||
.useIf(() => env.flavors.doc, DocServiceModule)
|
||||
// self hosted server only
|
||||
.useIf(config => config.isSelfhosted, SelfhostModule)
|
||||
.useIf(config => config.flavor.renderer, DocRendererModule);
|
||||
.useIf(() => env.selfhosted, WorkerModule, SelfhostModule)
|
||||
|
||||
// plugin modules
|
||||
ENABLED_PLUGINS.forEach(name => {
|
||||
const plugin = REGISTERED_PLUGINS.get(name);
|
||||
if (!plugin) {
|
||||
new Logger('AppBuilder').warn(`Unknown plugin ${name}`);
|
||||
return;
|
||||
}
|
||||
|
||||
factor.use(plugin);
|
||||
});
|
||||
// gcloud
|
||||
.useIf(() => env.gcp, GCloudModule);
|
||||
|
||||
return factor.compile();
|
||||
}
|
||||
|
||||
export const AppModule = buildAppModule();
|
||||
export const AppModule = buildAppModule(env);
|
||||
|
||||
Reference in New Issue
Block a user