chore: add tsdoc (#3788)

* chore: tsdoc

* chore: tsdoc

* fix: error

* chore: code format

* chore: code format
This commit is contained in:
chenos 2024-03-26 17:08:45 +08:00 committed by GitHub
parent b6ae528d80
commit c4aa8b78c2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 540 additions and 173 deletions

View File

@ -45,14 +45,40 @@ interface CanArgs {
}
export class ACL extends EventEmitter {
/**
* @internal
*/
public availableStrategy = new Map<string, ACLAvailableStrategy>();
/**
* @internal
*/
public allowManager = new AllowManager(this);
/**
* @internal
*/
public snippetManager = new SnippetManager();
/**
* @internal
*/
roles = new Map<string, ACLRole>();
/**
* @internal
*/
actionAlias = new Map<string, string>();
/**
* @internal
*/
configResources: string[] = [];
protected availableActions = new Map<string, ACLAvailableAction>();
protected fixedParamsManager = new FixedParamsManager();
protected middlewares: Toposort<any>;
constructor() {
@ -114,14 +140,23 @@ export class ACL extends EventEmitter {
return this.roles.delete(name);
}
/**
* @internal
*/
registerConfigResources(names: string[]) {
names.forEach((name) => this.registerConfigResource(name));
}
/**
* @internal
*/
registerConfigResource(name: string) {
this.configResources.push(name);
}
/**
* @internal
*/
isConfigResource(name: string) {
return this.configResources.includes(name);
}
@ -227,6 +262,9 @@ export class ACL extends EventEmitter {
return null;
}
/**
* @internal
*/
public resolveActionAlias(action: string) {
return this.actionAlias.get(action) ? this.actionAlias.get(action) : action;
}
@ -242,6 +280,9 @@ export class ACL extends EventEmitter {
return this.skip(resourceName, actionNames, condition);
}
/**
* @deprecated
*/
skip(resourceName: string, actionNames: string[] | string, condition?: string | ConditionFunc) {
if (!Array.isArray(actionNames)) {
actionNames = [actionNames];
@ -252,6 +293,9 @@ export class ACL extends EventEmitter {
}
}
/**
* @internal
*/
async parseJsonTemplate(json: any, ctx: any) {
if (json.filter) {
ctx.logger?.info?.('parseJsonTemplate.raw', JSON.parse(JSON.stringify(json.filter)));
@ -295,6 +339,9 @@ export class ACL extends EventEmitter {
};
}
/**
* @internal
*/
async getActionParams(ctx) {
const roleName = ctx.state.currentRole || 'anonymous';
const { resourceName, actionName } = ctx.action;
@ -322,6 +369,9 @@ export class ACL extends EventEmitter {
this.snippetManager.register(snippet);
}
/**
* @internal
*/
filterParams(ctx, resourceName, params) {
if (params?.filter?.createdById) {
const collection = ctx.db.getCollection(resourceName);

View File

@ -11,7 +11,7 @@ export const ADMIN_SETTINGS_PATH = '/admin/settings/';
export const SNIPPET_PREFIX = 'pm.';
export interface PluginSettingOptions {
title: string | React.ReactElement;
title: any;
/**
* @default Outlet
*/

View File

@ -1,10 +1,9 @@
import { assign, MergeStrategies, prePerfHooksWrap, requireModule } from '@nocobase/utils';
import { assign, MergeStrategies, requireModule } from '@nocobase/utils';
import compose from 'koa-compose';
import _ from 'lodash';
import Middleware, { MiddlewareType } from './middleware';
import Resource from './resource';
import { HandlerType } from './resourcer';
import { RecordableHistogram, performance } from 'perf_hooks';
export type ActionType = string | HandlerType | ActionOptions;
@ -161,27 +160,38 @@ export interface ActionParams {
*/
values?: any;
/**
* Model
* This method is deprecated and should not be used.
* Use {@link action.resourceName.split(',')[0]} instead.
* @deprecated
*/
resourceName?: string;
/**
*
* This method is deprecated and should not be used.
* Use {@link filterByTk} instead.
* @deprecated
*/
resourceIndex?: string;
/**
*
* This method is deprecated and should not be used.
* Use {@link action.resourceName.split(',')[1]} instead.
* @deprecated
*/
associatedName?: string;
/**
*
* This method is deprecated and should not be used.
* Use {@link action.sourceId} instead.
* @deprecated
*/
associatedIndex?: string;
/**
*
* This method is deprecated and should not be used.
* @deprecated
*/
associated?: any;
/**
*
* This method is deprecated and should not be used.
* Use {@link action.actionName} instead.
* @deprecated
*/
actionName?: string;
/**
@ -204,11 +214,23 @@ export class Action {
public params: ActionParams = {};
public actionName: string;
public resourceName: string;
/**
* This method is deprecated and should not be used.
* Use {@link this.sourceId} instead.
* @deprecated
*/
public resourceOf: any;
public sourceId: any;
public readonly middlewares: Array<Middleware> = [];
/**
* @internal
*/
constructor(options: ActionOptions) {
options = requireModule(options);
if (typeof options === 'function') {
@ -221,15 +243,22 @@ export class Action {
this.mergeParams(params);
}
/**
* @internal
*/
toJSON() {
return {
actionName: this.actionName,
resourceName: this.resourceName,
resourceOf: this.resourceOf,
resourceOf: this.sourceId,
sourceId: this.sourceId,
params: this.params,
};
}
/**
* @internal
*/
clone() {
const options = _.cloneDeep(this.options);
delete options.middleware;
@ -241,6 +270,9 @@ export class Action {
return action;
}
/**
* @internal
*/
setContext(context: any) {
this.context = context;
}
@ -266,34 +298,55 @@ export class Action {
});
}
/**
* @internal
*/
setResource(resource: Resource) {
this.resource = resource;
return this;
}
/**
* @internal
*/
getResource() {
return this.resource;
}
/**
* @internal
*/
getOptions(): ActionOptions {
return this.options;
}
/**
* @internal
*/
setName(name: ActionName) {
this.name = name;
return this;
}
/**
* @internal
*/
getName() {
return this.name;
}
/**
* @internal
*/
getMiddlewareHandlers() {
return this.middlewares
.filter((middleware) => middleware.canAccess(this.name))
.map((middleware) => middleware.getHandler());
}
/**
* @internal
*/
getHandler() {
const handler = requireModule(this.handler || this.resource.resourcer.getRegisteredHandler(this.name));
if (typeof handler !== 'function') {
@ -303,6 +356,9 @@ export class Action {
return handler;
}
/**
* @internal
*/
getHandlers() {
const handlers = [
...this.resource.resourcer.getMiddlewares(),
@ -315,10 +371,16 @@ export class Action {
return handlers;
}
/**
* @internal
*/
async execute(context: any, next?: any) {
return await compose(this.getHandlers())(context, next);
}
/**
* @internal
*/
static toInstanceMap(actions: object, resource?: Resource) {
return new Map(
Object.entries(actions).map(([key, options]) => {

View File

@ -150,6 +150,9 @@ export interface ImportOptions {
}
export class Resourcer {
/**
* @internal
*/
public readonly options: ResourcerOptions;
protected resources = new Map<string, Resource>();
/**
@ -173,6 +176,7 @@ export class Resourcer {
* @param {object} [options]
* @param {string} [options.directory]
* @param {array} [options.extensions = ['js', 'ts', 'json']]
*
*/
public async import(options: ImportOptions): Promise<Map<string, Resource>> {
const { extensions = ['js', 'ts', 'json'], directory } = options;
@ -206,14 +210,27 @@ export class Resourcer {
return this.resources.has(name);
}
/**
* @internal
*/
removeResource(name) {
return this.resources.delete(name);
}
/**
* This method is deprecated and should not be used.
* Use {@link this.registerActionHandler()} instead.
* @deprecated
*/
registerAction(name: ActionName, handler: HandlerType) {
this.registerActionHandler(name, handler);
}
/**
* This method is deprecated and should not be used.
* Use {@link this.registerActionHandlers()} instead.
* @deprecated
*/
registerActions(handlers: Handlers) {
this.registerActionHandlers(handlers);
}
@ -233,14 +250,23 @@ export class Resourcer {
this.actionHandlers.set(name, handler);
}
/**
* @internal
*/
getRegisteredHandler(name: ActionName) {
return this.actionHandlers.get(name);
}
/**
* @internal
*/
getRegisteredHandlers() {
return this.actionHandlers;
}
/**
* @internal
*/
getResource(name: string): Resource {
if (!this.resources.has(name)) {
throw new Error(`${name} resource does not exist`);
@ -248,6 +274,9 @@ export class Resourcer {
return this.resources.get(name);
}
/**
* @internal
*/
getAction(name: string, action: ActionName): Action {
// 支持注册局部 action
if (this.actionHandlers.has(`${name}:${action}`)) {
@ -256,6 +285,9 @@ export class Resourcer {
return this.getResource(name).getAction(action);
}
/**
* @internal
*/
getMiddlewares() {
return this.middlewares.nodes;
}
@ -264,6 +296,11 @@ export class Resourcer {
this.middlewares.add(middlewares, options);
}
/**
* This method is deprecated and should not be used.
* Use {@link this.middleware()} instead.
* @deprecated
*/
restApiMiddleware({ prefix, accessors, skipIfDataSourceExists = false }: KoaMiddlewareOptions = {}) {
return async (ctx: ResourcerContext, next: () => Promise<any>) => {
if (skipIfDataSourceExists) {
@ -317,6 +354,7 @@ export class Resourcer {
ctx.action.setContext(ctx);
ctx.action.actionName = params.actionName;
ctx.action.sourceId = params.associatedIndex;
ctx.action.resourceOf = params.associatedIndex;
ctx.action.resourceName = params.associatedName
? `${params.associatedName}.${params.resourceName}`
@ -349,11 +387,7 @@ export class Resourcer {
}
/**
* API
*
* @param options
* @param context
* @param next
* @internal
*/
async execute(options: ExecuteOptions, context: ResourcerContext = {}, next?: any) {
const { resource, action } = options;

View File

@ -139,19 +139,41 @@ export type MaintainingCommandStatus = {
};
export class Application<StateT = DefaultState, ContextT = DefaultContext> extends Koa implements AsyncEmitter {
/**
* @internal
*/
declare middleware: any;
/**
* @internal
*/
stopped = false;
/**
* @internal
*/
ready = false;
declare emitAsync: (event: string | symbol, ...args: any[]) => Promise<boolean>;
/**
* @internal
*/
public rawOptions: ApplicationOptions;
/**
* @internal
*/
public activatedCommand: {
name: string;
} = null;
/**
* @internal
*/
public running = false;
/**
* @internal
*/
public perfHistograms = new Map<string, RecordableHistogram>();
protected plugins = new Map<string, Plugin>();
protected _appSupervisor: AppSupervisor = AppSupervisor.getInstance();
protected _started: boolean;
protected _logger: SystemLogger;
private _authenticated = false;
private _maintaining = false;
private _maintainingCommandStatus: MaintainingCommandStatus;
@ -169,12 +191,18 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
protected _loaded: boolean;
/**
* @internal
*/
get loaded() {
return this._loaded;
}
private _maintainingMessage: string;
/**
* @internal
*/
get maintainingMessage() {
return this._maintainingMessage;
}
@ -198,8 +226,6 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
return this.mainDataSource.collectionManager.db;
}
protected _logger: SystemLogger;
get logger() {
return this._logger;
}
@ -220,6 +246,9 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
return this._cache;
}
/**
* @internal
*/
set cache(cache: Cache) {
this._cache = cache;
}
@ -254,6 +283,11 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
protected _locales: Locale;
/**
* This method is deprecated and should not be used.
* Use {@link #localeManager} instead.
* @deprecated
*/
get locales() {
return this._locales;
}
@ -288,10 +322,16 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
return this._dataSourceManager;
}
/**
* @internal
*/
getMaintaining() {
return this._maintainingCommandStatus;
}
/**
* @internal
*/
setMaintaining(_maintainingCommandStatus: MaintainingCommandStatus) {
this._maintainingCommandStatus = _maintainingCommandStatus;
@ -305,6 +345,9 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
this._maintaining = true;
}
/**
* @internal
*/
setMaintainingMessage(message: string) {
this._maintainingMessage = message;
@ -314,11 +357,18 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
});
}
/**
* This method is deprecated and should not be used.
* Use {@link #this.version.get()} instead.
* @deprecated
*/
getVersion() {
return packageJson.version;
}
/**
* This method is deprecated and should not be used.
* Use {@link #this.pm.addPreset()} instead.
* @deprecated
*/
plugin<O = any>(pluginClass: any, options?: O) {
@ -335,6 +385,9 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
return this;
}
/**
* @internal
*/
callback() {
const fn = compose(this.middleware.nodes);
@ -348,14 +401,29 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
};
}
/**
* This method is deprecated and should not be used.
* Use {@link #this.db.collection()} instead.
* @deprecated
*/
collection(options: CollectionOptions) {
return this.db.collection(options);
}
/**
* This method is deprecated and should not be used.
* Use {@link #this.resourcer.define()} instead.
* @deprecated
*/
resource(options: ResourceOptions) {
return this.resourcer.define(options);
}
/**
* This method is deprecated and should not be used.
* Use {@link #this.resourcer.registerActions()} instead.
* @deprecated
*/
actions(handlers: any, options?: ActionsOptions) {
return this.resourcer.registerActions(handlers);
}
@ -368,11 +436,9 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
return (this.cli as any)._findCommand(name);
}
async preload() {
// load core collections
// load plugin commands
}
/**
* @internal
*/
async reInit() {
if (!this._loaded) {
return;
@ -476,12 +542,19 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
}
/**
* This method is deprecated and should not be used.
* Use {@link this.pm.get()} instead.
* @deprecated
*/
getPlugin<P extends Plugin>(name: string | typeof Plugin) {
return this.pm.get(name) as P;
}
/**
* This method is deprecated and should not be used.
* Use {@link this.runAsCLI()} instead.
* @deprecated
*/
async parse(argv = process.argv) {
return this.runAsCLI(argv);
}
@ -504,7 +577,7 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
return await this.runAsCLI([command, ...args], { from: 'user', throwError: true });
}
createCli() {
protected createCLI() {
const command = new AppCommand('nocobase')
.usage('[command] [options]')
.hook('preAction', async (_, actionCommand) => {
@ -544,6 +617,9 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
return command;
}
/**
* @internal
*/
async loadMigrations(options) {
const { directory, context, namespace } = options;
const migrations = {
@ -570,6 +646,9 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
return migrations;
}
/**
* @internal
*/
async loadCoreMigrations() {
const migrations = await this.loadMigrations({
directory: resolve(__dirname, 'migrations'),
@ -600,11 +679,17 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
};
}
/**
* @internal
*/
async loadPluginCommands() {
this.log.debug('load plugin commands');
await this.pm.loadCommands();
}
/**
* @internal
*/
async runAsCLI(argv = process.argv, options?: ParseOptions & { throwError?: boolean; reqId?: string }) {
if (this.activatedCommand) {
return;
@ -689,6 +774,9 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
this.stopped = false;
}
/**
* @internal
*/
async emitStartedEvent(options: StartOptions = {}) {
await this.emitAsync('__started', this, {
maintainingStatus: lodash.cloneDeep(this._maintainingCommandStatus),
@ -700,6 +788,9 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
return this._started;
}
/**
* @internal
*/
async tryReloadOrRestart(options: StartOptions = {}) {
if (this._started) {
await this.restart(options);
@ -898,6 +989,9 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
};
}
/**
* @internal
*/
reInitEvents() {
for (const eventName of this.eventNames()) {
for (const listener of this.listeners(eventName)) {
@ -943,7 +1037,7 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
this._cronJobManager = new CronJobManager(this);
this._cli = this.createCli();
this._cli = this.createCLI();
this._i18n = createI18n(options);
this.context.db = this.db;

View File

@ -3,12 +3,21 @@ import lodash from 'lodash';
import { PluginManager } from './plugin-manager';
export class PluginManagerRepository extends Repository {
/**
* @internal
*/
pm: PluginManager;
/**
* @internal
*/
setPluginManager(pm: PluginManager) {
this.pm = pm;
}
/**
* @deprecated
*/
async remove(name: string | string[]) {
await this.destroy({
filter: {
@ -17,6 +26,9 @@ export class PluginManagerRepository extends Repository {
});
}
/**
* @deprecated
*/
async enable(name: string | string[]) {
const pluginNames = lodash.castArray(name);
const plugins = pluginNames.map((name) => this.pm.get(name));
@ -56,6 +68,9 @@ export class PluginManagerRepository extends Repository {
}
}
/**
* @deprecated
*/
async disable(name: string | string[]) {
name = lodash.cloneDeep(name);

View File

@ -45,12 +45,39 @@ export interface InstallOptions {
export class AddPresetError extends Error {}
export class PluginManager {
/**
* @internal
*/
app: Application;
/**
* @internal
*/
collection: Collection;
/**
* @internal
*/
pluginInstances = new Map<typeof Plugin, Plugin>();
/**
* @internal
*/
pluginAliases = new Map<string, Plugin>();
/**
* @internal
*/
server: net.Server;
/**
* @internal
*/
_repository: PluginManagerRepository;
/**
* @internal
*/
constructor(public options: PluginManagerOptions) {
this.app = options.app;
this.app.db.registerRepositories({
@ -76,18 +103,22 @@ export class PluginManager {
this.app.resourcer.use(uploadMiddleware);
}
_repository: PluginManagerRepository;
get repository() {
return this.app.db.getRepository('applicationPlugins') as PluginManagerRepository;
}
/**
* @internal
*/
static async getPackageJson(packageName: string) {
const file = await fs.promises.realpath(resolve(process.env.NODE_MODULES_PATH, packageName, 'package.json'));
const data = await fs.promises.readFile(file, { encoding: 'utf-8' });
return JSON.parse(data);
}
/**
* @internal
*/
static async getPackageName(name: string) {
const prefixes = this.getPluginPkgPrefix();
for (const prefix of prefixes) {
@ -100,12 +131,18 @@ export class PluginManager {
throw new Error(`${name} plugin does not exist`);
}
/**
* @internal
*/
static getPluginPkgPrefix() {
return (process.env.PLUGIN_PACKAGE_PREFIX || '@nocobase/plugin-,@nocobase/preset-,@nocobase/plugin-pro-').split(
',',
);
}
/**
* @internal
*/
static async findPackage(name: string) {
try {
const packageName = this.getPackageName(name);
@ -130,6 +167,9 @@ export class PluginManager {
throw new Error(`No available packages found, ${name} plugin does not exist`);
}
/**
* @internal
*/
static clearCache(packageName: string) {
return;
const packageNamePath = packageName.replace('/', sep);
@ -140,6 +180,9 @@ export class PluginManager {
});
}
/**
* @internal
*/
static async resolvePlugin(pluginName: string | typeof Plugin, isUpgrade = false, isPkg = false) {
if (typeof pluginName === 'string') {
const packageName = isPkg ? pluginName : await this.getPackageName(pluginName);
@ -296,11 +339,17 @@ export class PluginManager {
await instance.afterAdd();
}
/**
* @internal
*/
async initPlugins() {
await this.initPresetPlugins();
await this.initOtherPlugins();
}
/**
* @internal
*/
async loadCommands() {
this.app.log.debug('load commands');
const items = await this.repository.find({
@ -619,6 +668,9 @@ export class PluginManager {
await execa('yarn', ['nocobase', 'refresh']);
}
/**
* @deprecated
*/
async loadOne(plugin: Plugin) {
this.app.setMaintainingMessage(`loading plugin ${plugin.name}...`);
if (plugin.state.loaded || !plugin.enabled) {
@ -637,6 +689,9 @@ export class PluginManager {
this.app.setMaintainingMessage(`loaded plugin ${plugin.name}`);
}
/**
* @internal
*/
async addViaCLI(urlOrName: string, options?: PluginData) {
if (isURL(urlOrName)) {
await this.addByCompressedFileUrl({
@ -679,6 +734,9 @@ export class PluginManager {
await execa('yarn', ['nocobase', 'postinstall']);
}
/**
* @internal
*/
async addByNpm(options: { packageName: string; name?: string; registry: string; authToken?: string }) {
let { name = '', registry, packageName, authToken } = options;
name = name.trim();
@ -693,6 +751,9 @@ export class PluginManager {
return this.addByCompressedFileUrl({ name, compressedFileUrl, registry, authToken, type: 'npm' });
}
/**
* @internal
*/
async addByFile(options: { file: string; registry?: string; authToken?: string; type?: string; name?: string }) {
const { file, authToken } = options;
@ -708,6 +769,9 @@ export class PluginManager {
return this.add(name, { packageName }, true);
}
/**
* @internal
*/
async addByCompressedFileUrl(options: {
compressedFileUrl: string;
registry?: string;
@ -748,6 +812,9 @@ export class PluginManager {
await this.app.upgrade();
}
/**
* @internal
*/
async upgradeByNpm(values: PluginData) {
const name = values.name;
const plugin = this.get(name);
@ -769,6 +836,9 @@ export class PluginManager {
return this.upgradeByCompressedFileUrl({ compressedFileUrl, name, version, registry, authToken });
}
/**
* @internal
*/
async upgradeByCompressedFileUrl(options: PluginData) {
const { name, compressedFileUrl, authToken } = options;
const data = await this.repository.findOne({ filter: { name } });
@ -780,6 +850,9 @@ export class PluginManager {
await this.add(name, { version, packageName: data.packageName }, true, true);
}
/**
* @internal
*/
getNameByPackageName(packageName: string) {
const prefixes = PluginManager.getPluginPkgPrefix();
const prefix = prefixes.find((prefix) => packageName.startsWith(prefix));
@ -808,12 +881,18 @@ export class PluginManager {
);
}
/**
* @internal
*/
async getNpmVersionList(name: string) {
const plugin = this.get(name);
const npmInfo = await getNpmInfo(plugin.options.packageName, plugin.options.registry, plugin.options.authToken);
return Object.keys(npmInfo.versions);
}
/**
* @internal
*/
async loadPresetMigrations() {
const migrations = {
beforeLoad: [],
@ -854,6 +933,9 @@ export class PluginManager {
};
}
/**
* @internal
*/
async loadOtherMigrations() {
const migrations = {
beforeLoad: [],
@ -897,6 +979,9 @@ export class PluginManager {
};
}
/**
* @internal
*/
async loadPresetPlugins() {
await this.initPresetPlugins();
await this.load();
@ -931,6 +1016,9 @@ export class PluginManager {
});
}
/**
* @internal
*/
async initOtherPlugins() {
if (this['_initOtherPlugins']) {
return;
@ -939,6 +1027,9 @@ export class PluginManager {
this['_initOtherPlugins'] = true;
}
/**
* @internal
*/
async initPresetPlugins() {
if (this['_initPresetPlugins']) {
return;

View File

@ -33,9 +33,22 @@ export interface PluginOptions {
export abstract class Plugin<O = any> implements PluginInterface {
options: any;
app: Application;
/**
* @deprecated
*/
model: Model;
/**
* @internal
*/
state: any = {};
/**
* @internal
*/
private _sourceDir: string;
constructor(app: Application, options?: any) {
this.app = app;
this.setOptions(options);
@ -80,10 +93,6 @@ export abstract class Plugin<O = any> implements PluginInterface {
return this.options.isPreset;
}
setOptions(options: any) {
this.options = options || {};
}
getName() {
return (this.options as any).name;
}
@ -92,64 +101,6 @@ export abstract class Plugin<O = any> implements PluginInterface {
return this.app.createLogger(options);
}
protected _sourceDir: string;
protected async getSourceDir() {
if (this._sourceDir) {
return this._sourceDir;
}
if (await this.isDev()) {
return (this._sourceDir = 'src');
}
if (basename(__dirname) === 'src') {
return (this._sourceDir = 'src');
}
return (this._sourceDir = this.isPreset ? 'lib' : 'dist');
}
async loadCommands() {
const extensions = ['js', 'ts'];
const directory = resolve(
process.env.NODE_MODULES_PATH,
this.options.packageName,
await this.getSourceDir(),
'server/commands',
);
const patten = `${directory}/*.{${extensions.join(',')}}`;
const files = glob.sync(patten, {
ignore: ['**/*.d.ts'],
});
for (const file of files) {
let filename = basename(file);
filename = filename.substring(0, filename.lastIndexOf('.')) || filename;
const callback = await importModule(file);
callback(this.app);
}
if (files.length) {
this.app.log.debug(`load commands [${this.name}]`);
}
}
async loadMigrations() {
this.app.log.debug(`load plugin migrations [${this.name}]`);
if (!this.options.packageName) {
return { beforeLoad: [], afterSync: [], afterLoad: [] };
}
const directory = resolve(
process.env.NODE_MODULES_PATH,
this.options.packageName,
await this.getSourceDir(),
'server/migrations',
);
return await this.app.loadMigrations({
directory,
namespace: this.options.packageName,
context: {
plugin: this,
},
});
}
afterAdd() {}
beforeLoad() {}
@ -172,13 +123,86 @@ export abstract class Plugin<O = any> implements PluginInterface {
async afterRemove() {}
async importCollections(collectionsPath: string) {
// await this.db.import({
// directory: collectionsPath,
// from: `plugin:${this.getName()}`,
// });
/**
* @deprecated
*/
async importCollections(collectionsPath: string) {}
/**
* @internal
*/
setOptions(options: any) {
this.options = options || {};
}
/**
* @internal
*/
protected async getSourceDir() {
if (this._sourceDir) {
return this._sourceDir;
}
if (await this.isDev()) {
return (this._sourceDir = 'src');
}
if (basename(__dirname) === 'src') {
return (this._sourceDir = 'src');
}
return (this._sourceDir = this.isPreset ? 'lib' : 'dist');
}
/**
* @internal
*/
async loadCommands() {
const extensions = ['js', 'ts'];
const directory = resolve(
process.env.NODE_MODULES_PATH,
this.options.packageName,
await this.getSourceDir(),
'server/commands',
);
const patten = `${directory}/*.{${extensions.join(',')}}`;
const files = glob.sync(patten, {
ignore: ['**/*.d.ts'],
});
for (const file of files) {
let filename = basename(file);
filename = filename.substring(0, filename.lastIndexOf('.')) || filename;
const callback = await importModule(file);
callback(this.app);
}
if (files.length) {
this.app.log.debug(`load commands [${this.name}]`);
}
}
/**
* @internal
*/
async loadMigrations() {
this.app.log.debug(`load plugin migrations [${this.name}]`);
if (!this.options.packageName) {
return { beforeLoad: [], afterSync: [], afterLoad: [] };
}
const directory = resolve(
process.env.NODE_MODULES_PATH,
this.options.packageName,
await this.getSourceDir(),
'server/migrations',
);
return await this.app.loadMigrations({
directory,
namespace: this.options.packageName,
context: {
plugin: this,
},
});
}
/**
* @internal
*/
async loadCollections() {
if (!this.options.packageName) {
return;
@ -197,6 +221,9 @@ export abstract class Plugin<O = any> implements PluginInterface {
}
}
/**
* @deprecated
*/
requiredPlugins() {
return [];
}
@ -205,6 +232,9 @@ export abstract class Plugin<O = any> implements PluginInterface {
return this.app.i18n.t(text, { ns: this.options['packageName'], ...(options as any) });
}
/**
* @internal
*/
protected async isDev() {
if (!this.options.packageName) {
return false;
@ -218,6 +248,9 @@ export abstract class Plugin<O = any> implements PluginInterface {
return false;
}
/**
* @experimental
*/
async toJSON(options: any = {}) {
const { locale = 'en-US' } = options;
const { name, packageName, packageJson } = this.options;

View File

@ -2,12 +2,12 @@ import { Plugin } from '@nocobase/client';
import { RolesManagement } from './RolesManagement';
import { RolesManager } from './roles-manager';
class ACLPlugin extends Plugin {
export class PluginACLClient extends Plugin {
rolesManager = new RolesManager();
async load() {
this.app.pluginSettingsManager.add('users-permissions.roles', {
title: '{{t("Roles & Permissions")}}',
this.pluginSettingsManager.add('users-permissions.roles', {
title: this.t('Roles & Permissions'),
icon: 'LockOutlined',
Component: RolesManagement,
aclSnippet: 'pm.acl.roles',
@ -16,5 +16,5 @@ class ACLPlugin extends Plugin {
}
}
export default ACLPlugin;
export { RolesManagerContext } from './RolesManagerProvider';
export default PluginACLClient;

View File

@ -1,5 +1,5 @@
import { useTranslation } from 'react-i18next';
export function useACLTranslation() {
return useTranslation(['acl', 'client'], { nsMode: 'fallback' });
return useTranslation(['@nocobase/plugin-acl', 'client'], { nsMode: 'fallback' });
}

View File

@ -1,6 +1,6 @@
import { ISchema, useForm } from '@formily/react';
import {
CollectionContext,
CollectionProvider,
CollectionProvider_deprecated,
ResourceActionContext,
SchemaComponent,
@ -8,17 +8,14 @@ import {
useActionContext,
useFilterFieldOptions,
useFilterFieldProps,
useCollectionRecord,
useRecord,
useRequest,
useResourceActionContext,
} from '@nocobase/client';
import React, { useContext, useEffect } from 'react';
import { roleCollectionsSchema } from '../schemas/roles';
import { RolesManagerContext } from '../RolesManagerProvider';
import { ISchema } from '@formily/react';
import { roleCollectionsSchema } from '../schemas/roles';
import { RolesResourcesActions } from './RolesResourcesActions';
import { useForm } from '@formily/react';
const collection = {
name: 'collections',

View File

@ -2,5 +2,10 @@
"The current user has no roles. Please try another account.": "当前用户没有角色,请使用其他账号。",
"The user role does not exist. Please try signing in again": "用户角色不存在,请尝试重新登录。",
"New role": "新建角色",
"Permissions": "权限"
"Permissions": "权限",
"Roles & Permissions": "角色和权限",
"General": "通用",
"Menu": "菜单",
"Plugin settings": "插件设置",
"Data sources": "数据源"
}

View File

@ -1,5 +1,6 @@
export { default } from './server';
export * from './middlewares/setCurrentRole';
export * from './middlewares/with-acl-meta';
export { RoleResourceActionModel } from './model/RoleResourceActionModel';
export { RoleResourceModel } from './model/RoleResourceModel';
export * from './middlewares/with-acl-meta';
export * from './middlewares/setCurrentRole';
export { default } from './server';

View File

@ -1,20 +1,20 @@
import { Plugin, useCollection_deprecated } from '@nocobase/client';
import { bulkEditActionSettings, deprecatedBulkEditActionSettings } from './BulkEditAction.Settings';
import { BulkEditFormItemInitializers_deprecated, bulkEditFormItemInitializers } from './BulkEditFormItemInitializers';
import { BulkEditActionInitializer } from './BulkEditActionInitializer';
import {
CreateFormBulkEditBlockInitializers,
BulkEditBlockInitializers_deprecated,
CreateFormBulkEditBlockInitializers,
bulkEditBlockInitializers,
} from './BulkEditBlockInitializers';
import {
BulkEditFormActionInitializers_deprecated,
bulkEditFormActionInitializers,
} from './BulkEditFormActionInitializers';
import { BulkEditActionInitializer } from './BulkEditActionInitializer';
import { BulkEditFormItemInitializers_deprecated, bulkEditFormItemInitializers } from './BulkEditFormItemInitializers';
import { bulkEditFormItemSettings } from './bulkEditFormItemSettings';
import { BulkEditField } from './component/BulkEditField';
import { useCustomizeBulkEditActionProps } from './utils';
export class BulkEditPlugin extends Plugin {
export class PluginActionBulkEditClient extends Plugin {
async load() {
this.app.addComponents({ BulkEditField });
this.app.addScopes({ useCustomizeBulkEditActionProps });
@ -61,4 +61,4 @@ export class BulkEditPlugin extends Plugin {
}
}
export default BulkEditPlugin;
export default PluginActionBulkEditClient;

View File

@ -1,10 +1,7 @@
import { i18n } from '@nocobase/client';
import { useTranslation } from 'react-i18next';
export const NAMESPACE = 'bulk-edit';
// i18n.addResources('zh-CN', NAMESPACE, zhCN);
// i18n.addResources('en-US', NAMESPACE, enUS);
export const NAMESPACE = '@nocobase/plugin-bulk-edit';
export function lang(key: string) {
return i18n.t(key, { ns: NAMESPACE });

View File

@ -1,9 +1,9 @@
import { Plugin, useCollection_deprecated } from '@nocobase/client';
import { bulkUpdateActionSettings, deprecatedBulkUpdateActionSettings } from './BulkUpdateAction.Settings';
import { BulkUpdateActionInitializer } from './BulkUpdateActionInitializer';
import { CustomizeActionInitializer } from './CustomizeActionInitializer';
import { useCustomizeBulkUpdateActionProps } from './utils';
import { BulkUpdateActionInitializer } from './BulkUpdateActionInitializer';
export class PluginBulkUpdateClient extends Plugin {
export class PluginActionBulkUpdateClient extends Plugin {
async load() {
this.app.addComponents({ CustomizeActionInitializer });
this.app.addScopes({ useCustomizeBulkUpdateActionProps });
@ -31,4 +31,4 @@ export class PluginBulkUpdateClient extends Plugin {
}
}
export default PluginBulkUpdateClient;
export default PluginActionBulkUpdateClient;

View File

@ -3,9 +3,6 @@ import { useTranslation } from 'react-i18next';
export const NAMESPACE = 'bulk-update';
// i18n.addResources('zh-CN', NAMESPACE, zhCN);
// i18n.addResources('en-US', NAMESPACE, enUS);
export function lang(key: string) {
return i18n.t(key, { ns: NAMESPACE });
}

View File

@ -4,7 +4,7 @@ import { deprecatedDuplicateActionSettings, duplicateActionSettings } from './Du
import { DuplicateActionInitializer } from './DuplicateActionInitializer';
import { DuplicatePluginProvider } from './DuplicatePluginProvider';
export class PluginDuplicateClient extends Plugin {
export class PluginActionDuplicateClient extends Plugin {
async load() {
this.app.use(DuplicatePluginProvider);
this.app.addComponents({
@ -64,5 +64,5 @@ export class PluginDuplicateClient extends Plugin {
}
}
export default PluginDuplicateClient;
export default PluginActionDuplicateClient;
export * from './DuplicateAction';

View File

@ -1,7 +1,7 @@
import { Plugin } from '@nocobase/client';
import { deprecatedPrintActionSettings, printActionSettings } from './PrintAction.Settings';
import { PrintActionPluginProvider } from './PrintActionPluginProvider';
export class PrintPlugin extends Plugin {
export class PluginActionPrintClient extends Plugin {
async load() {
this.app.use(PrintActionPluginProvider);
this.app.schemaSettingsManager.add(deprecatedPrintActionSettings);
@ -23,4 +23,4 @@ export class PrintPlugin extends Plugin {
}
}
export default PrintPlugin;
export default PluginActionPrintClient;

View File

@ -37,7 +37,7 @@ const SCDocumentation = () => {
);
};
export class APIDocumentationPlugin extends Plugin {
export class PluginAPIDocClient extends Plugin {
async load() {
this.app.pluginSettingsManager.add(NAMESPACE, {
title: `{{t("API documentation", { ns: "${NAMESPACE}" })}}`,
@ -53,4 +53,4 @@ export class APIDocumentationPlugin extends Plugin {
}
}
export default APIDocumentationPlugin;
export default PluginAPIDocClient;

View File

@ -2,7 +2,7 @@ import { Context } from '@nocobase/actions';
import { Plugin } from '@nocobase/server';
import { SwaggerManager } from './swagger';
export default class APIDoc extends Plugin {
export class PluginAPIDocServer extends Plugin {
swagger: SwaggerManager;
constructor(app, options) {
super(app, options);
@ -10,7 +10,7 @@ export default class APIDoc extends Plugin {
}
async beforeLoad() {}
async load() {
this.app.resource({
this.app.resourcer.define({
name: 'swagger',
type: 'single',
actions: {
@ -46,3 +46,5 @@ export default class APIDoc extends Plugin {
});
}
}
export default PluginAPIDocServer;

View File

@ -2,15 +2,15 @@ import { Plugin } from '@nocobase/client';
import { NAMESPACE } from '../constants';
import { Configuration } from './Configuration';
class APIKeysPlugin extends Plugin {
export class PluginAPIKeysClient extends Plugin {
async load() {
this.app.pluginSettingsManager.add(NAMESPACE, {
this.pluginSettingsManager.add(NAMESPACE, {
icon: 'KeyOutlined',
title: '{{t("API keys", {"ns": "api-keys"})}}',
title: this.t('API keys'),
Component: Configuration,
aclSnippet: 'pm.api-keys.configuration',
});
}
}
export default APIKeysPlugin;
export default PluginAPIKeysClient;

View File

@ -1 +1 @@
export const NAMESPACE = 'api-keys';
export const NAMESPACE = '@nocobase/plugin-api-keys';

View File

@ -1,24 +1,11 @@
import { Plugin } from '@nocobase/server';
import { resolve } from 'path';
import { NAMESPACE } from '../constants';
import { create, destroy } from './actions/api-keys';
import { enUS, zhCN } from './locale';
export interface ApiKeysPluginConfig {
name?: string;
}
export default class ApiKeysPlugin extends Plugin<ApiKeysPluginConfig> {
export class PluginAPIKeysServer extends Plugin {
resourceName = 'apiKeys';
constructor(app, options) {
super(app, options);
}
async beforeLoad() {
this.app.i18n.addResources('zh-CN', NAMESPACE, zhCN);
this.app.i18n.addResources('en-US', NAMESPACE, enUS);
await this.app.resourcer.define({
this.app.resourcer.define({
name: this.resourceName,
actions: {
create,
@ -34,11 +21,9 @@ export default class ApiKeysPlugin extends Plugin<ApiKeysPluginConfig> {
}
async load() {
await this.importCollections(resolve(__dirname, '../collections'));
this.app.resourcer.use(async (ctx, next) => {
const { resourceName, actionName } = ctx.action.params;
if (resourceName == this.resourceName && ['list', 'destroy'].includes(actionName)) {
const { resourceName, actionName } = ctx.action;
if (resourceName === this.resourceName && ['list', 'destroy'].includes(actionName)) {
ctx.action.mergeParams({
filter: {
createdById: ctx.auth.user.id,
@ -49,3 +34,5 @@ export default class ApiKeysPlugin extends Plugin<ApiKeysPluginConfig> {
});
}
}
export default PluginAPIKeysServer;

View File

@ -1,13 +1,13 @@
import { Plugin } from '@nocobase/client';
import { AuthProvider } from './AuthProvider';
import { NAMESPACE } from './locale';
import { Authenticator } from './settings/Authenticator';
import { AuthLayout, SignInPage, SignUpPage } from './pages';
import { ComponentType } from 'react';
import { Registry } from '@nocobase/utils/client';
import { ComponentType } from 'react';
import { presetAuthType } from '../preset';
import { SignInForm, SignUpForm, Options } from './basic';
import { AuthProvider } from './AuthProvider';
import { Authenticator as AuthenticatorType } from './authenticator';
import { Options, SignInForm, SignUpForm } from './basic';
import { NAMESPACE } from './locale';
import { AuthLayout, SignInPage, SignUpPage } from './pages';
import { Authenticator } from './settings/Authenticator';
export type AuthOptions = {
components: Partial<{
@ -18,7 +18,7 @@ export type AuthOptions = {
}>;
};
export class AuthPlugin extends Plugin {
export class PluginAuthClient extends Plugin {
authTypes = new Registry<AuthOptions>();
registerType(authType: string, options: AuthOptions) {
@ -63,7 +63,8 @@ export class AuthPlugin extends Plugin {
}
}
export default AuthPlugin;
export { useSignIn } from './basic';
export { useAuthenticator, AuthenticatorsContext } from './authenticator';
export { AuthenticatorsContext, useAuthenticator } from './authenticator';
export type { Authenticator } from './authenticator';
export { useSignIn } from './basic';
export default PluginAuthClient;

View File

@ -1,17 +1,17 @@
import { Cache } from '@nocobase/cache';
import { Model } from '@nocobase/database';
import { InstallOptions, Plugin } from '@nocobase/server';
import { resolve } from 'path';
import { namespace, presetAuthenticator, presetAuthType } from '../preset';
import { namespace, presetAuthType, presetAuthenticator } from '../preset';
import authActions from './actions/auth';
import authenticatorsActions from './actions/authenticators';
import { BasicAuth } from './basic-auth';
import { enUS, zhCN } from './locale';
import { AuthModel } from './model/authenticator';
import { TokenBlacklistService } from './token-blacklist';
import { Cache } from '@nocobase/cache';
import { Storer } from './storer';
import { TokenBlacklistService } from './token-blacklist';
export class AuthPlugin extends Plugin {
export class PluginAuthServer extends Plugin {
cache: Cache;
afterAdd() {}
@ -105,4 +105,4 @@ export class AuthPlugin extends Plugin {
async remove() {}
}
export default AuthPlugin;
export default PluginAuthServer;

View File

@ -2,11 +2,12 @@ import { Plugin } from '@nocobase/client';
import { BackupAndRestoreList } from './Configuration';
import { DuplicatorProvider } from './DuplicatorProvider';
import { NAMESPACE } from './locale';
export class DuplicatorPlugin extends Plugin {
export class PluginBackupRestoreClient extends Plugin {
async load() {
this.app.use(DuplicatorProvider);
this.app.pluginSettingsManager.add(NAMESPACE, {
title: `{{t("Backup & Restore", { ns: "${NAMESPACE}" })}}`,
title: this.t('Backup & Restore'),
icon: 'CloudServerOutlined',
Component: BackupAndRestoreList,
aclSnippet: 'pm.backup.restore',
@ -14,4 +15,4 @@ export class DuplicatorPlugin extends Plugin {
}
}
export default DuplicatorPlugin;
export default PluginBackupRestoreClient;

View File

@ -1,7 +1,7 @@
import { Plugin } from '@nocobase/server';
import backupFilesResourcer from './resourcers/backup-files';
export default class Duplicator extends Plugin {
export default class PluginBackupRestoreServer extends Plugin {
beforeLoad() {
this.app.acl.registerSnippet({
name: `pm.${this.name}`,