mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-18 10:31:50 +08:00
refactor(server): plugin modules (#5630)
- [x] separates modules into `fundamental`, `core`, `plugins`
- [x] optional modules with `@OptionalModule` decorator to install modules with requirements met(`requires`, `if`)
- [x] `module.contributesTo` defines optional features that will be enabled if module registered
- [x] `AFFiNE.plugins.use('payment', {})` to enable a optional/plugin module
- [x] `PaymentModule` is the first plugin module
- [x] GraphQLSchema will not be generated for non-included modules
- [x] Frontend can use `ServerConfigType` query to detect which features are enabled
- [x] override existing provider globally
This commit is contained in:
@@ -0,0 +1 @@
|
||||
export * from './optional-module';
|
||||
@@ -0,0 +1,71 @@
|
||||
import {
|
||||
DynamicModule,
|
||||
Module,
|
||||
ModuleMetadata,
|
||||
Provider,
|
||||
Type,
|
||||
} from '@nestjs/common';
|
||||
import { omit } from 'lodash-es';
|
||||
|
||||
import { Config, ConfigPaths } from '../config';
|
||||
|
||||
interface OptionalModuleMetadata extends ModuleMetadata {
|
||||
/**
|
||||
* Only install module if given config paths are defined in AFFiNE config.
|
||||
*/
|
||||
requires?: ConfigPaths[];
|
||||
|
||||
/**
|
||||
* Only install module if the predication returns true.
|
||||
*/
|
||||
if?: (config: Config) => boolean;
|
||||
|
||||
/**
|
||||
* Defines which feature will be enabled if the module installed.
|
||||
*/
|
||||
contributesTo?: import('../../core/config').ServerFeature; // avoid circlar dependency
|
||||
|
||||
/**
|
||||
* Defines which providers provided by other modules will be overridden if the module installed.
|
||||
*/
|
||||
overrides?: Provider[];
|
||||
}
|
||||
|
||||
const additionalOptions = [
|
||||
'contributesTo',
|
||||
'requires',
|
||||
'if',
|
||||
'overrides',
|
||||
] as const satisfies Array<keyof OptionalModuleMetadata>;
|
||||
|
||||
type OptionalDynamicModule = DynamicModule & OptionalModuleMetadata;
|
||||
|
||||
export function OptionalModule(metadata: OptionalModuleMetadata) {
|
||||
return (target: Type) => {
|
||||
additionalOptions.forEach(option => {
|
||||
if (Object.hasOwn(metadata, option)) {
|
||||
Reflect.defineMetadata(option, metadata[option], target);
|
||||
}
|
||||
});
|
||||
|
||||
if (metadata.overrides) {
|
||||
metadata.providers = (metadata.providers ?? []).concat(
|
||||
metadata.overrides
|
||||
);
|
||||
metadata.exports = (metadata.exports ?? []).concat(metadata.overrides);
|
||||
}
|
||||
|
||||
const nestMetadata = omit(metadata, additionalOptions);
|
||||
Module(nestMetadata)(target);
|
||||
};
|
||||
}
|
||||
|
||||
export function getOptionalModuleMetadata<
|
||||
T extends keyof OptionalModuleMetadata,
|
||||
>(target: Type | OptionalDynamicModule, key: T): OptionalModuleMetadata[T] {
|
||||
if ('module' in target) {
|
||||
return target[key];
|
||||
} else {
|
||||
return Reflect.getMetadata(key, target);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user