refactor: plugin manager (#965)

* feat: improve code

* chore: update version

* feat: api service

* fix: api services

* feat: improve code

* feat: improve code

* feat: improve code

* feat: pm socket

* fix: test errors

* feat: add built-in plugins before upgrade

* feat: update docs

* feat: improve code

* fix: after load
This commit is contained in:
chenos 2022-10-27 13:00:16 +08:00 committed by GitHub
parent 2c690a39e0
commit 249dff16d3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
132 changed files with 893 additions and 910 deletions

View File

@ -60,10 +60,7 @@ export default {
title: 'Getting started',
'title.zh-CN': '快速开始',
type: 'group',
children: [
'/development/index',
'/development/your-fisrt-plugin',
],
children: ['/development/index', '/development/your-fisrt-plugin'],
},
{
title: 'Extension Guides',
@ -192,7 +189,7 @@ export default {
type: 'subMenu',
children: [
'/api/server/application',
//'/api/server/plugin-manager',
// '/api/server/plugin-manager',
'/api/server/plugin',
],
},

View File

@ -180,28 +180,9 @@ NocoBase 默认对 context 注入了以下成员,可以在请求处理函数
## 事件
**示例**
```ts
app.on('beforeStart', async () => {
console.log('app before start');
});
```
### beforeLoadAll
### afterLoadAll
### beforeLoadPlugin
### afterLoadPlugin
### beforeInstallPlugin
### afterInstallPlugin
### beforeInstall
### afterInstall
### beforeUpgrade
### afterUpgrade
### beforeStart
### afterStart
### beforeStop
### afterStop
### beforeDestroy
### afterDestroy
### `'beforeLoad'` / `'afterLoad'`
### `'beforeInstall'` / `'afterInstall'`
### `'beforeUpgrade'` / `'afterUpgrade'`
### `'beforeStart'` / `'afterStart'`
### `'beforeStop'` / `'afterStop'`
### `'beforeDestroy'` / `'afterDestroy'`

View File

@ -6,16 +6,54 @@
### `create()`
在本地创建一个插件脚手架
**签名**
```ts
create(name, options): void;
```
### `addStatic()`
**签名**
```ts
addStatic(plugin: any, options?: PluginOptions): Plugin;
```
**示例**
```ts
pm.addStatic('nocobase');
```
### `add()`
**签名**
```ts
async add(plugin: any, options?: PluginOptions): Promise<Plugin>;
async add(plugin: string[], options?: PluginOptions): Promise<Plugin[]>;
```
**示例**
```ts
await pm.add(['test'], {
builtIn: true,
enabled: true,
});
```
### `get()`
### `install()`
### `upgrade()`
获取插件实例
### `enable()`
### `disable()`
### `remove()`
### `upgrade()`

View File

@ -1,5 +1,23 @@
# Plugin
## 示例
```ts
const app = new Application();
class MyPlugin extends Plugin {
afterAdd() {}
beforeLoad() {}
load() {}
install() {}
afterEnable() {}
afterDisable() {}
remove() {}
}
app.plugin(MyPlugin, { name: 'my-plugin' });
```
## 属性
### `options`
@ -10,20 +28,32 @@
插件标识,只读
### `model`
插件模型数据
## 实例方法
### `afterAdd()`
插件 add/addStatic 之后
### `beforeLoad()`
加载前,如事件或类注册
插件加载前,如事件或类注册
### `load()`
加载配置
加载插件,配置之类
### `install()`
插件安装逻辑
插件安装逻辑,如初始化数据
### `afterEnable()`
插件激活之后的逻辑
### `afterDisable()`
插件禁用之后的逻辑
### `remove()`
用于实现插件删除逻辑

View File

@ -1,5 +1,5 @@
{
"version": "0.7.4-alpha.7",
"version": "0.8.0-alpha.1",
"npmClient": "yarn",
"useWorkspaces": true,
"npmClientArgs": [

View File

@ -1,9 +1,9 @@
{
"name": "@nocobase/app-client",
"version": "0.7.4-alpha.7",
"version": "0.8.0-alpha.1",
"license": "MIT",
"devDependencies": {
"@nocobase/client": "0.7.4-alpha.7"
"@nocobase/client": "0.8.0-alpha.1"
},
"repository": {
"type": "git",

View File

@ -1,12 +1,12 @@
{
"name": "@nocobase/app-server",
"version": "0.7.4-alpha.7",
"version": "0.8.0-alpha.1",
"description": "",
"license": "MIT",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
"dependencies": {
"@nocobase/preset-nocobase": "0.7.4-alpha.7"
"@nocobase/preset-nocobase": "0.8.0-alpha.1"
},
"repository": {
"type": "git",

View File

@ -1,3 +1,3 @@
import { PluginConfiguration } from '@nocobase/server';
export default ['@nocobase/preset-nocobase'] as PluginConfiguration[];
export default ['nocobase'] as PluginConfiguration[];

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/acl",
"version": "0.7.4-alpha.7",
"version": "0.8.0-alpha.1",
"description": "",
"license": "Apache-2.0",
"licenses": [
@ -12,7 +12,7 @@
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
"dependencies": {
"@nocobase/resourcer": "0.7.4-alpha.7",
"@nocobase/resourcer": "0.8.0-alpha.1",
"json-templates": "^4.2.0"
},
"repository": {

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/actions",
"version": "0.7.4-alpha.7",
"version": "0.8.0-alpha.1",
"description": "",
"license": "Apache-2.0",
"licenses": [
@ -12,9 +12,9 @@
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
"dependencies": {
"@nocobase/database": "0.7.4-alpha.7",
"@nocobase/resourcer": "0.7.4-alpha.7",
"@nocobase/cache": "0.7.4-alpha.7"
"@nocobase/cache": "0.8.0-alpha.1",
"@nocobase/database": "0.8.0-alpha.1",
"@nocobase/resourcer": "0.8.0-alpha.1"
},
"repository": {
"type": "git",

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/build",
"version": "0.7.4-alpha.7",
"version": "0.8.0-alpha.1",
"description": "Library build tool based on rollup.",
"main": "lib/index.js",
"bin": {

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/cache",
"version": "0.7.4-alpha.7",
"version": "0.8.0-alpha.1",
"description": "",
"license": "Apache-2.0",
"licenses": [

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/cli",
"version": "0.7.4-alpha.7",
"version": "0.8.0-alpha.1",
"description": "",
"license": "Apache-2.0",
"licenses": [
@ -23,7 +23,7 @@
"serve": "^13.0.2"
},
"devDependencies": {
"@nocobase/devtools": "0.7.4-alpha.7"
"@nocobase/devtools": "0.8.0-alpha.1"
},
"repository": {
"type": "git",

View File

@ -1,9 +1,6 @@
import { InstallOptions, Plugin } from '@nocobase/server';
export class {{{pascalCaseName}}}Plugin extends Plugin {
getName(): string {
return this.getPackageName(__dirname);
}
beforeLoad() {
// TODO

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/client",
"version": "0.7.4-alpha.7",
"version": "0.8.0-alpha.1",
"license": "Apache-2.0",
"licenses": [
{
@ -19,8 +19,8 @@
"@formily/antd": "2.0.20",
"@formily/core": "2.0.20",
"@formily/react": "2.0.20",
"@nocobase/sdk": "0.7.4-alpha.7",
"@nocobase/utils": "0.7.4-alpha.7",
"@nocobase/sdk": "0.8.0-alpha.1",
"@nocobase/utils": "0.8.0-alpha.1",
"ahooks": "^3.0.5",
"antd": "~4.19.5",
"axios": "^0.26.1",

View File

@ -4,7 +4,11 @@ import { notification } from 'antd';
import React from 'react';
export class APIClient extends APIClientSDK {
services: Record<string, Result<any, any>>;
services: Record<string, Result<any, any>> = {};
service(uid: string) {
return this.services[uid];
}
interceptors() {
super.interceptors();

View File

@ -1,5 +1,5 @@
import { Field } from '@formily/core';
import { useField } from '@formily/react';
import { useField, useFieldSchema } from '@formily/react';
import { useRequest } from 'ahooks';
import template from 'lodash/template';
import React, { createContext, useContext } from 'react';
@ -10,7 +10,7 @@ import { useRecordIndex } from '../record-provider';
export const BlockResourceContext = createContext(null);
export const BlockAssociationContext = createContext(null);
const BlockRequestContext = createContext(null);
const BlockRequestContext = createContext<any>(null);
export const useBlockResource = () => {
return useContext(BlockResourceContext);
@ -80,6 +80,8 @@ export const useResourceAction = (props, opts = {}) => {
const { fields } = useCollection();
const appends = fields?.filter((field) => field.target).map((field) => field.name);
const params = useActionParams(props);
const api = useAPIClient();
const fieldSchema = useFieldSchema();
if (!Object.keys(params).includes('appends') && appends?.length) {
params['appends'] = appends;
}
@ -96,6 +98,12 @@ export const useResourceAction = (props, opts = {}) => {
},
{
...opts,
onSuccess(data, params) {
opts?.['onSuccess']?.(data, params);
if (fieldSchema['x-uid']) {
api.services[fieldSchema['x-uid']] = result;
}
},
defaultParams: [params],
refreshDeps: [JSON.stringify(params.appends)],
},

View File

@ -1,6 +1,6 @@
{
"name": "create-nocobase-app",
"version": "0.7.4-alpha.7",
"version": "0.8.0-alpha.1",
"main": "src/index.js",
"license": "Apache-2.0",
"licenses": [

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/database",
"version": "0.7.4-alpha.7",
"version": "0.8.0-alpha.1",
"description": "",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
@ -12,7 +12,7 @@
}
],
"dependencies": {
"@nocobase/utils": "0.7.4-alpha.7",
"@nocobase/utils": "0.8.0-alpha.1",
"async-mutex": "^0.3.2",
"cron-parser": "4.4.0",
"deepmerge": "^4.2.2",

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/devtools",
"version": "0.7.4-alpha.7",
"version": "0.8.0-alpha.1",
"description": "",
"license": "Apache-2.0",
"licenses": [
@ -11,7 +11,7 @@
],
"main": "./src/index.js",
"dependencies": {
"@nocobase/build": "0.7.4-alpha.7",
"@nocobase/build": "0.8.0-alpha.1",
"@testing-library/react": "^12.1.2",
"@types/jest": "^26.0.0",
"@types/koa": "^2.13.4",

View File

@ -1,6 +1,6 @@
{
"name": "dumi-theme-nocobase",
"version": "0.7.4-alpha.7",
"version": "0.8.0-alpha.1",
"private": true,
"files": [
"es",

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/resourcer",
"version": "0.7.4-alpha.7",
"version": "0.8.0-alpha.1",
"description": "",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
@ -12,7 +12,7 @@
}
],
"dependencies": {
"@nocobase/utils": "0.7.4-alpha.7",
"@nocobase/utils": "0.8.0-alpha.1",
"deepmerge": "^4.2.2",
"koa-compose": "^4.1.0",
"lodash": "^4.17.21",

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/sdk",
"version": "0.7.4-alpha.7",
"version": "0.8.0-alpha.1",
"license": "Apache-2.0",
"licenses": [
{

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/server",
"version": "0.7.4-alpha.7",
"version": "0.8.0-alpha.1",
"main": "lib/index.js",
"types": "./lib/index.d.ts",
"license": "Apache-2.0",
@ -14,10 +14,10 @@
"@hapi/topo": "^6.0.0",
"@koa/cors": "^3.1.0",
"@koa/router": "^9.4.0",
"@nocobase/acl": "0.7.4-alpha.7",
"@nocobase/actions": "0.7.4-alpha.7",
"@nocobase/database": "0.7.4-alpha.7",
"@nocobase/resourcer": "0.7.4-alpha.7",
"@nocobase/acl": "0.8.0-alpha.1",
"@nocobase/actions": "0.8.0-alpha.1",
"@nocobase/database": "0.8.0-alpha.1",
"@nocobase/resourcer": "0.8.0-alpha.1",
"chalk": "^4.1.1",
"commander": "^9.2.0",
"find-package-json": "^1.2.0",

View File

@ -38,6 +38,7 @@ export interface ApplicationOptions {
i18n?: i18n | InitOptions;
plugins?: PluginConfiguration[];
acl?: boolean;
pmSock?: string;
}
export interface DefaultState extends KoaDefaultState {
@ -259,20 +260,17 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
return packageJson.version;
}
plugin<O = any>(pluginClass: any, options?: O): Plugin<O> {
return this.pm.add(pluginClass, options);
plugin<O = any>(pluginClass: any, options?: O): Plugin {
return this.pm.addStatic(pluginClass, options);
}
loadPluginConfig(pluginsConfigurations: PluginConfiguration[]) {
for (let pluginConfiguration of pluginsConfigurations) {
if (typeof pluginConfiguration == 'string') {
pluginConfiguration = [pluginConfiguration, {}];
this.plugin(pluginConfiguration);
} else {
this.plugin(...pluginConfiguration);
}
const plugin = PluginManager.resolvePlugin(pluginConfiguration[0]);
const pluginOptions = pluginConfiguration[1];
this.plugin(plugin, pluginOptions);
}
}
@ -319,13 +317,20 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
return (<any>this.cli)._findCommand(name);
}
async load() {
await this.pm.load();
async load(options?: any) {
if (options?.reload) {
this.init();
}
await this.emitAsync('beforeLoad', this, options);
await this.pm.load(options);
await this.emitAsync('afterLoad', this, options);
}
async reload() {
this.init();
await this.pm.load();
async reload(options?: any) {
await this.load({
...options,
reload: true,
});
}
getPlugin<P extends Plugin>(name: string) {
@ -337,9 +342,9 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
}
async runAsCLI(argv = process.argv, options?: ParseOptions) {
if (argv?.[2] !== 'install') {
await this.load();
}
await this.load({
method: argv?.[2],
});
return this.cli.parseAsync(argv, options);
}
@ -352,6 +357,7 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
await this.emitAsync('beforeStart', this, options);
if (options?.listen?.port) {
const pmServer = await this.pm.listen();
const listen = () =>
new Promise((resolve, reject) => {
const Server = this.listen(options?.listen, () => {
@ -361,6 +367,10 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
Server.on('error', (err) => {
reject(err);
});
Server.on('close', () => {
pmServer.close();
});
});
try {
@ -385,7 +395,9 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
try {
// close database connection
// silent if database already closed
if (!this.db.closed()) {
await this.db.close();
}
} catch (e) {
console.log(e);
}
@ -419,11 +431,11 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
if (options?.clean) {
await this.db.clean(isBoolean(options.clean) ? { drop: options.clean } : options.clean);
await this.reload({ method: 'install' });
}
await this.emitAsync('beforeInstall', this, options);
await this.load();
await this.db.sync(options?.sync);
await this.pm.install(options);
await this.version.update();

View File

@ -1,5 +1,3 @@
import axios from 'axios';
import { resolve } from 'path';
import Application from '../application';
export default (app: Application) => {
@ -8,101 +6,6 @@ export default (app: Application) => {
.argument('<method>')
.arguments('<plugins...>')
.action(async (method, plugins, options, ...args) => {
const { APP_PORT, API_BASE_PATH = '/api/', API_BASE_URL } = process.env;
const baseURL = API_BASE_URL || `http://localhost:${APP_PORT}${API_BASE_PATH}`;
let started = true;
try {
await axios.get(`${baseURL}app:getLang`);
} catch (error) {
started = false;
}
const pm = {
async create() {
const name = plugins[0];
const { run } = require('@nocobase/cli/src/util');
const { PluginGenerator } = require('@nocobase/cli/src/plugin-generator');
const generator = new PluginGenerator({
cwd: resolve(process.cwd(), name),
args: options,
context: {
name,
},
});
await generator.run();
await run('yarn', ['install']);
},
async add() {
try {
if (started) {
const res = await axios.get(`${baseURL}pm:add/${plugins.join(',')}`);
} else {
await app.pm.add(plugins);
}
} catch (error) {}
const fs = require('fs/promises');
await Promise.all(
plugins.map((plugin) => {
const file = resolve(
process.cwd(),
'packages',
process.env.APP_PACKAGE_ROOT || 'app',
'client/src/plugins',
`${plugin}.ts`,
);
console.log(file);
return fs.writeFile(file, `export { default } from '@nocobase/plugin-${plugin}/client';`);
}),
);
const { run } = require('@nocobase/cli/src/util');
await run('yarn', ['nocobase', 'postinstall']);
},
async enable() {
if (started) {
const res = await axios.get(`${baseURL}pm:enable/${plugins.join(',')}`);
console.log(res.data);
return;
}
const repository = app.db.getRepository('applicationPlugins');
await repository.update({
filter: {
'name.$in': plugins,
},
values: {
enabled: true,
},
});
},
async disable() {
if (started) {
const res = await axios.get(`${baseURL}pm:disable/${plugins.join(',')}`);
console.log(res.data);
return;
}
const repository = app.db.getRepository('applicationPlugins');
await repository.update({
filter: {
'name.$in': plugins,
},
values: {
enabled: false,
},
});
},
async remove() {
if (started) {
const res = await axios.get(`${baseURL}pm:disable/${plugins.join(',')}`);
console.log(res.data);
return;
}
const repository = app.db.getRepository('applicationPlugins');
await repository.destroy({
filter: {
'name.$in': plugins,
},
});
plugins.map((name) => app.pm.remove(name));
},
};
await pm[method]();
app.pm.clientWrite({ method, plugins });
});
};

View File

@ -1,289 +0,0 @@
import { CleanOptions, Collection, Model, Repository, SyncOptions } from '@nocobase/database';
import Application from './application';
import { Plugin } from './plugin';
interface PluginManagerOptions {
app: Application;
}
export interface InstallOptions {
cliArgs?: any[];
clean?: CleanOptions | boolean;
sync?: SyncOptions;
}
class PluginManagerRepository extends Repository {
getInstance(): Plugin {
return;
}
async add(name: string | string[], options) {}
async enable(name: string | string[], options) {}
async disable(name: string | string[], options) {}
async remove(name: string | string[], options) {}
async upgrade(name: string | string[], options) {}
}
export class PluginManager {
app: Application;
collection: Collection;
repository: PluginManagerRepository;
plugins = new Map<string, Plugin>();
constructor(options: PluginManagerOptions) {
this.app = options.app;
this.collection = this.app.db.collection({
name: 'applicationPlugins',
fields: [
{ type: 'string', name: 'name', unique: true },
{ type: 'string', name: 'version' },
{ type: 'boolean', name: 'enabled' },
{ type: 'boolean', name: 'installed' },
{ type: 'boolean', name: 'builtIn' },
{ type: 'json', name: 'options' },
],
});
const app = this.app;
const pm = this;
this.repository = this.collection.repository as PluginManagerRepository;
this.app.resourcer.define({
name: 'pm',
actions: {
async add(ctx, next) {
const { filterByTk } = ctx.action.params;
if (!filterByTk) {
ctx.throw(400, 'null');
}
await pm.add(filterByTk);
ctx.body = filterByTk;
await next();
},
async enable(ctx, next) {
const { filterByTk } = ctx.action.params;
if (!filterByTk) {
ctx.throw(400, 'filterByTk invalid');
}
const name = pm.getPackageName(filterByTk);
const plugin = pm.get(name);
if (plugin.model) {
plugin.model.set('enabled', true);
await plugin.model.save();
}
if (!plugin) {
ctx.throw(400, 'plugin invalid');
}
await app.reload();
if (plugin.model && !plugin.model.get('installed')) {
await app.db.sync();
await plugin.install();
plugin.model.set('installed', true);
await plugin.model.save();
}
await app.start();
ctx.body = 'ok';
await next();
},
async disable(ctx, next) {
const { filterByTk } = ctx.action.params;
if (!filterByTk) {
ctx.throw(400, 'filterByTk invalid');
}
const name = pm.getPackageName(filterByTk);
const plugin = pm.get(name);
if (plugin.model) {
plugin.model.set('enabled', false);
await plugin.model.save();
}
if (!plugin) {
ctx.throw(400, 'plugin invalid');
}
await app.reload();
await app.start();
ctx.body = 'ok';
await next();
},
async upgrade(ctx, next) {
ctx.body = 'ok';
await next();
},
async remove(ctx, next) {
const { filterByTk } = ctx.action.params;
if (!filterByTk) {
ctx.throw(400, 'filterByTk invalid');
}
const name = pm.getPackageName(filterByTk);
console.log(name, pm.plugins.keys());
const plugin = pm.get(name);
if (plugin?.model) {
await plugin.model.destroy();
pm.remove(name);
} else {
await pm.repository.destroy({
filter: {
name: filterByTk,
},
});
}
await app.reload();
await app.start();
ctx.body = 'ok';
await next();
},
},
});
this.app.acl.use(async (ctx, next) => {
if (ctx.action.resourceName === 'pm') {
ctx.permission = {
skip: true,
};
}
await next();
});
this.app.on('beforeInstall', async () => {
await this.collection.sync();
});
this.app.on('beforeLoadAll', async (options) => {
await this.collection.sync();
const exists = await this.app.db.collectionExistsInDb('applicationPlugins');
if (!exists) {
return;
}
const items = await this.repository.find();
for (const item of items) {
await this.add(item);
}
});
}
getPackageName(name: string) {
if (name.includes('@nocobase/plugin-')) {
return name;
}
if (name.includes('/')) {
return `@${name}`;
}
return `@nocobase/plugin-${name}`;
}
private addByModel(model) {
try {
const packageName = this.getPackageName(model.get('name'));
require.resolve(packageName);
const cls = require(packageName).default;
const instance = new cls(this.app, {
...model.get('options'),
name: model.get('name'),
version: model.get('version'),
enabled: model.get('enabled'),
});
instance.setModel(model);
this.plugins.set(packageName, instance);
return instance;
} catch (error) {}
}
getPlugins() {
return this.plugins;
}
get(name: string) {
return this.plugins.get(name);
}
remove(name: string) {
return this.plugins.delete(name);
}
add<P = Plugin, O = any>(pluginClass: any, options?: O) {
if (Array.isArray(pluginClass)) {
const addMultiple = async () => {
for (const plugin of pluginClass) {
await this.add(plugin);
}
};
return addMultiple();
}
if (typeof pluginClass === 'string') {
const packageName = this.getPackageName(pluginClass);
try {
require.resolve(packageName);
} catch (error) {
throw new Error(`${pluginClass} plugin does not exist`);
}
const packageJson = require(`${packageName}/package.json`);
const addNew = async () => {
let model = await this.repository.findOne({
filter: { name: pluginClass },
});
if (model) {
throw new Error(`${pluginClass} plugin already exists`);
}
model = await this.repository.create({
values: {
name: pluginClass,
version: packageJson.version,
enabled: false,
options: {},
},
});
return this.addByModel(model);
};
return addNew();
}
if (pluginClass instanceof Model) {
return this.addByModel(pluginClass);
}
const instance = new pluginClass(this.app, {
...options,
enabled: true,
});
const name = instance.getName();
if (this.plugins.has(name)) {
throw new Error(`plugin name [${name}] exists`);
}
this.plugins.set(name, instance);
return instance;
}
async load() {
await this.app.emitAsync('beforeLoadAll');
for (const [name, plugin] of this.plugins) {
if (!plugin.enabled) {
continue;
}
await plugin.beforeLoad();
}
for (const [name, plugin] of this.plugins) {
if (!plugin.enabled) {
continue;
}
await this.app.emitAsync('beforeLoadPlugin', plugin);
await plugin.load();
await this.app.emitAsync('afterLoadPlugin', plugin);
}
await this.app.emitAsync('afterLoadAll');
}
async install(options: InstallOptions = {}) {
for (const [name, plugin] of this.plugins) {
if (!plugin.enabled) {
continue;
}
await this.app.emitAsync('beforeInstallPlugin', plugin, options);
await plugin.install(options);
await this.app.emitAsync('afterInstallPlugin', plugin, options);
}
}
static resolvePlugin(pluginName: string) {
return require(pluginName).default;
}
}

View File

@ -0,0 +1,310 @@
import { CleanOptions, Collection, SyncOptions } from '@nocobase/database';
import { requireModule } from '@nocobase/utils';
import execa from 'execa';
import fs from 'fs';
import net from 'net';
import { resolve } from 'path';
import Application from '../application';
import { Plugin } from '../plugin';
import collectionOptions from './options/collection';
import resourceOptions from './options/resource';
import { PluginManagerRepository } from './PluginManagerRepository';
export interface PluginManagerOptions {
app: Application;
}
export interface InstallOptions {
cliArgs?: any[];
clean?: CleanOptions | boolean;
sync?: SyncOptions;
}
export class PluginManager {
app: Application;
collection: Collection;
repository: PluginManagerRepository;
plugins = new Map<string, Plugin>();
server: net.Server;
pmSock: string;
constructor(options: PluginManagerOptions) {
this.app = options.app;
const f = resolve(process.cwd(), 'storage', 'pm.sock');
this.pmSock = this.app.options.pmSock || f;
this.app.db.registerRepositories({
PluginManagerRepository,
});
this.collection = this.app.db.collection(collectionOptions);
this.repository = this.collection.repository as PluginManagerRepository;
this.repository.setPluginManager(this);
this.app.resourcer.define(resourceOptions);
this.app.acl.use(async (ctx, next) => {
if (ctx.action.resourceName === 'pm') {
ctx.permission = {
skip: true,
};
}
await next();
});
this.server = net.createServer((socket) => {
socket.on('data', async (data) => {
const { method, plugins } = JSON.parse(data.toString());
try {
console.log(method, plugins);
await this[method](plugins);
} catch (error) {
console.error(error.message);
}
});
socket.pipe(socket);
});
this.app.on('beforeLoad', async (app, options) => {
if (options?.method && ['install', 'upgrade'].includes(options.method)) {
await this.collection.sync();
}
const exists = await this.app.db.collectionExistsInDb('applicationPlugins');
if (!exists) {
return;
}
if (options?.method !== 'install' || options.reload) {
await this.repository.load();
}
});
}
getPlugins() {
return this.plugins;
}
get(name: string) {
return this.plugins.get(name);
}
clientWrite(data: any) {
const { method, plugins } = data;
const client = new net.Socket();
client.connect(this.pmSock, () => {
client.write(JSON.stringify(data));
});
client.on('error', async () => {
try {
console.log(method, plugins);
await this[method](plugins);
} catch (error) {
console.error(error.message);
}
});
}
async listen(): Promise<net.Server> {
if (fs.existsSync(this.pmSock)) {
await fs.promises.unlink(this.pmSock);
}
return new Promise((resolve) => {
this.server.listen(this.pmSock, () => {
resolve(this.server);
});
});
}
async create(name: string) {
const { run } = require('@nocobase/cli/src/util');
const { PluginGenerator } = require('@nocobase/cli/src/plugin-generator');
const generator = new PluginGenerator({
cwd: resolve(process.cwd(), name),
args: {},
context: {
name,
},
});
await generator.run();
await run('yarn', ['install']);
}
addStatic(plugin?: any, options?: any) {
let name: string;
if (typeof plugin === 'string') {
name = plugin;
plugin = PluginManager.resolvePlugin(plugin);
} else {
name = plugin.name;
if (!name) {
throw new Error(`plugin name invalid`);
}
}
const instance = new plugin(this.app, {
name,
enabled: true,
...options,
});
const pluginName = instance.getName();
if (this.plugins.has(pluginName)) {
throw new Error(`plugin name [${pluginName}] exists`);
}
this.plugins.set(pluginName, instance);
return instance;
}
async add(plugin: any, options: any = {}) {
if (Array.isArray(plugin)) {
return Promise.all(plugin.map((p) => this.add(p, options)));
}
// console.log(`adding ${plugin} plugin`);
const packageName = await PluginManager.findPackage(plugin);
const packageJson = require(`${packageName}/package.json`);
const instance = this.addStatic(plugin, options);
let model = await this.repository.findOne({
filter: { name: plugin },
});
if (model) {
throw new Error(`${plugin} plugin already exists`);
}
const { enabled, builtIn, installed, ...others } = options;
await this.repository.create({
values: {
name: plugin,
version: packageJson.version,
enabled: !!enabled,
builtIn: !!builtIn,
installed: !!installed,
options: {
...others,
},
},
});
const file = resolve(
process.cwd(),
'packages',
process.env.APP_PACKAGE_ROOT || 'app',
'client/src/plugins',
`${plugin}.ts`,
);
if (!fs.existsSync(file)) {
try {
require.resolve(`${packageName}/client`);
await fs.promises.writeFile(file, `export { default } from '${packageName}/client';`);
const { run } = require('@nocobase/cli/src/util');
await run('yarn', ['nocobase', 'postinstall']);
} catch (error) {}
}
return instance;
}
async load(options: any = {}) {
for (const [name, plugin] of this.plugins) {
if (!plugin.enabled) {
continue;
}
await plugin.beforeLoad();
}
for (const [name, plugin] of this.plugins) {
if (!plugin.enabled) {
continue;
}
await this.app.emitAsync('beforeLoadPlugin', plugin, options);
await plugin.load();
await this.app.emitAsync('afterLoadPlugin', plugin, options);
}
}
async install(options: InstallOptions = {}) {
for (const [name, plugin] of this.plugins) {
if (!plugin.enabled) {
continue;
}
await this.app.emitAsync('beforeInstallPlugin', plugin, options);
await plugin.install(options);
await this.app.emitAsync('afterInstallPlugin', plugin, options);
}
}
async enable(name: string | string[]) {
try {
const pluginNames = await this.repository.enable(name);
await this.app.reload();
await this.app.db.sync();
for (const pluginName of pluginNames) {
const plugin = this.app.getPlugin(pluginName);
if (!plugin) {
throw new Error(`${name} plugin does not exist`);
}
await plugin.install();
await plugin.afterEnable();
}
} catch (error) {
throw error;
}
}
async disable(name: string | string[]) {
try {
const pluginNames = await this.repository.disable(name);
await this.app.reload();
for (const pluginName of pluginNames) {
const plugin = this.app.getPlugin(pluginName);
if (!plugin) {
throw new Error(`${name} plugin does not exist`);
}
await plugin.afterDisable();
}
} catch (error) {
throw error;
}
}
async remove(name: string | string[]) {
const pluginNames = typeof name === 'string' ? [name] : name;
for (const pluginName of pluginNames) {
const plugin = this.app.getPlugin(pluginName);
if (!plugin) {
throw new Error(`${name} plugin does not exist`);
}
await plugin.remove();
}
await this.repository.remove(name);
this.app.reload();
}
static getPackageName(name: string) {
const prefixes = (process.env.PLUGIN_PACKAGE_PREFIX || '@nocobase/plugin-,@nocobase/preset-').split(',');
for (const prefix of prefixes) {
try {
require.resolve(`${prefix}${name}`);
return `${prefix}${name}`;
} catch (error) {
continue;
}
}
throw new Error(`${name} plugin does not exist`);
}
static async findPackage(name: string) {
try {
const packageName = this.getPackageName(name);
return packageName;
} catch (error) {
const prefixes = (process.env.PLUGIN_PACKAGE_PREFIX || '@nocobase/plugin-,@nocobase/preset-').split(',');
for (const prefix of prefixes) {
try {
const packageName = `${prefix}${name}`;
await execa('npm', ['v', packageName, 'versions']);
console.log(`${packageName} is downloading...`);
await execa('yarn', ['add', packageName, '-W']);
return packageName;
} catch (error) {
continue;
}
}
}
throw new Error(`${name} plugin does not exist`);
}
static resolvePlugin(pluginName: string) {
const packageName = this.getPackageName(pluginName);
return requireModule(packageName);
}
}
export default PluginManager;

View File

@ -0,0 +1,58 @@
import { Repository } from '@nocobase/database';
import { PluginManager } from './PluginManager';
export class PluginManagerRepository extends Repository {
pm: PluginManager;
setPluginManager(pm: PluginManager) {
this.pm = pm;
}
async remove(name: string | string[]) {
await this.destroy({
filter: {
name,
},
});
}
async enable(name: string | string[]) {
const pluginNames = typeof name === 'string' ? [name] : name;
await this.update({
filter: {
name,
},
values: {
enabled: true,
installed: true,
},
});
return pluginNames;
}
async disable(name: string | string[]) {
const pluginNames = typeof name === 'string' ? [name] : name;
await this.update({
filter: {
name,
},
values: {
enabled: false,
installed: false,
},
});
return pluginNames;
}
async load() {
const items = await this.find();
for (const item of items) {
await this.pm.addStatic(item.get('name'), {
...item.get('options'),
name: item.get('name'),
version: item.get('version'),
enabled: item.get('enabled'),
});
}
}
}

View File

@ -0,0 +1,2 @@
export * from './PluginManager';

View File

@ -0,0 +1,12 @@
export default {
name: 'applicationPlugins',
repository: 'PluginManagerRepository',
fields: [
{ type: 'string', name: 'name', unique: true },
{ type: 'string', name: 'version' },
{ type: 'boolean', name: 'enabled' },
{ type: 'boolean', name: 'installed' },
{ type: 'boolean', name: 'builtIn' },
{ type: 'json', name: 'options' },
],
};

View File

@ -0,0 +1,49 @@
export default {
name: 'pm',
actions: {
async add(ctx, next) {
const pm = ctx.app.pm;
const { filterByTk } = ctx.action.params;
if (!filterByTk) {
ctx.throw(400, 'plugin name invalid');
}
await pm.add(filterByTk);
ctx.body = filterByTk;
await next();
},
async enable(ctx, next) {
const pm = ctx.app.pm;
const { filterByTk } = ctx.action.params;
if (!filterByTk) {
ctx.throw(400, 'plugin name invalid');
}
await pm.enable(filterByTk);
ctx.body = filterByTk;
await next();
},
async disable(ctx, next) {
const pm = ctx.app.pm;
const { filterByTk } = ctx.action.params;
if (!filterByTk) {
ctx.throw(400, 'plugin name invalid');
}
await pm.disable(filterByTk);
ctx.body = filterByTk;
await next();
},
async upgrade(ctx, next) {
ctx.body = 'ok';
await next();
},
async remove(ctx, next) {
const pm = ctx.app.pm;
const { filterByTk } = ctx.action.params;
if (!filterByTk) {
ctx.throw(400, 'plugin name invalid');
}
await pm.remove(filterByTk);
ctx.body = filterByTk;
await next();
},
},
};

View File

@ -1,9 +1,7 @@
import { Database, Model } from '@nocobase/database';
import finder from 'find-package-json';
import { Database } from '@nocobase/database';
import { Application } from './application';
import { InstallOptions } from './plugin-manager';
export interface PluginInterface {
beforeLoad?: () => void;
load();
@ -25,60 +23,46 @@ export interface PluginOptions {
export type PluginType = typeof Plugin;
export abstract class Plugin<O = any> implements PluginInterface {
options: O;
options: any;
app: Application;
db: Database;
model: Model;
constructor(app: Application, options?: O) {
constructor(app: Application, options?: any) {
this.app = app;
this.db = app.db;
this.setOptions(options);
this.initialize();
}
setOptions(options: O) {
this.options = options || ({} as any);
}
setModel(model) {
this.model = model;
this.afterAdd();
}
get enabled() {
return (this.options as any).enabled;
return this.options.enabled;
}
public abstract getName(): string;
set enabled(value) {
this.options.enabled = value;
}
initialize() {}
setOptions(options: any) {
this.options = options || {};
}
getName() {
return (this.options as any).name;
}
afterAdd() {}
beforeLoad() {}
async load() {}
async install(options?: InstallOptions) {}
async load() {
const collectionPath = this.collectionPath();
if (collectionPath) {
await this.db.import({
directory: collectionPath,
});
}
}
async afterEnable() {}
async disable() {
async afterDisable() {}
}
collectionPath() {
return null;
}
protected getPackageName(dirname: string) {
const f = finder(dirname);
const packageObj = f.next().value;
return packageObj['name'];
}
async remove() {}
}
export default Plugin;

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/test",
"version": "0.7.4-alpha.7",
"version": "0.8.0-alpha.1",
"main": "lib/index.js",
"types": "./lib/index.d.ts",
"license": "Apache-2.0",
@ -11,7 +11,7 @@
}
],
"dependencies": {
"@nocobase/server": "0.7.4-alpha.7",
"@nocobase/server": "0.8.0-alpha.1",
"@types/supertest": "^2.0.11",
"mockjs": "^1.1.0",
"mysql2": "^2.3.3",

View File

@ -56,6 +56,7 @@ interface Resource {
export class MockServer extends Application {
async loadAndInstall(options: any = {}) {
await this.load();
await this.install({
...options,
sync: {

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/utils",
"version": "0.7.4-alpha.7",
"version": "0.8.0-alpha.1",
"main": "lib/index.js",
"types": "./lib/index.d.ts",
"license": "Apache-2.0",

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/plugin-acl",
"version": "0.7.4-alpha.7",
"version": "0.8.0-alpha.1",
"description": "",
"license": "Apache-2.0",
"licenses": [
@ -12,10 +12,10 @@
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
"dependencies": {
"@nocobase/acl": "0.7.4-alpha.7",
"@nocobase/database": "0.7.4-alpha.7",
"@nocobase/plugin-users": "0.7.4-alpha.7",
"@nocobase/server": "0.7.4-alpha.7"
"@nocobase/acl": "0.8.0-alpha.1",
"@nocobase/database": "0.8.0-alpha.1",
"@nocobase/plugin-users": "0.8.0-alpha.1",
"@nocobase/server": "0.8.0-alpha.1"
},
"repository": {
"type": "git",

View File

@ -30,7 +30,7 @@ describe('acl', () => {
},
});
const userPlugin = app.getPlugin('@nocobase/plugin-users') as UsersPlugin;
const userPlugin = app.getPlugin('users') as UsersPlugin;
adminAgent = app.agent().auth(
userPlugin.jwtService.sign({
@ -483,7 +483,7 @@ describe('acl', () => {
},
});
const userPlugin = app.getPlugin('@nocobase/plugin-users') as UsersPlugin;
const userPlugin = app.getPlugin('users') as UsersPlugin;
const userAgent = app.agent().auth(
userPlugin.jwtService.sign({
userId: user.get('id'),

View File

@ -48,7 +48,7 @@ describe('association field acl', () => {
},
});
const userPlugin = app.getPlugin('@nocobase/plugin-users') as UsersPlugin;
const userPlugin = app.getPlugin('users') as UsersPlugin;
userAgent = app.agent().auth(
userPlugin.jwtService.sign({
userId: user.get('id'),

View File

@ -45,7 +45,7 @@ describe('configuration', () => {
}
});
const userPlugin = app.getPlugin('@nocobase/plugin-users') as UsersPlugin;
const userPlugin = app.getPlugin('users') as UsersPlugin;
adminAgent = app.agent().auth(userPlugin.jwtService.sign({
userId: admin.get('id'),
}), { type: 'bearer' });

View File

@ -1,7 +1,7 @@
import { ACL } from '@nocobase/acl';
import { Database, Model } from '@nocobase/database';
import { MockServer } from '@nocobase/test';
import UsersPlugin from '@nocobase/plugin-users';
import { MockServer } from '@nocobase/test';
import { prepareApp } from './prepare';
describe('middleware', () => {
@ -30,7 +30,7 @@ describe('middleware', () => {
}
});
const userPlugin = app.getPlugin('@nocobase/plugin-users') as UsersPlugin;
const userPlugin = app.getPlugin('users') as UsersPlugin;
adminAgent = app.agent().auth(userPlugin.jwtService.sign({
userId: admin.get('id'),
}), { type: 'bearer' });

View File

@ -67,7 +67,7 @@ describe('own test', () => {
admin = await db.getRepository('users').findOne();
pluginUser = app.getPlugin('@nocobase/plugin-users');
pluginUser = app.getPlugin('users');
adminToken = pluginUser.jwtService.sign({ userId: admin.get('id') });

View File

@ -1,7 +1,3 @@
import PluginCollectionManager from '@nocobase/plugin-collection-manager';
import PluginErrorHandler from '@nocobase/plugin-error-handler';
import PluginUiSchema from '@nocobase/plugin-ui-schema-storage';
import PluginUsers from '@nocobase/plugin-users';
import { mockServer } from '@nocobase/test';
import PluginACL from '../server';
@ -9,15 +5,14 @@ export async function prepareApp() {
const app = mockServer({
registerActions: true,
acl: true,
plugins: ['error-handler', 'users', 'ui-schema-storage', 'collection-manager'],
});
await app.cleanDb();
app.plugin(PluginUsers);
app.plugin(PluginUiSchema);
app.plugin(PluginErrorHandler);
app.plugin(PluginCollectionManager);
app.plugin(PluginACL);
app.plugin(PluginACL, {
name: 'acl',
});
await app.loadAndInstall();
await app.db.sync();

View File

@ -1,6 +1,6 @@
import { MockServer } from '@nocobase/test';
import { Database } from '@nocobase/database';
import UsersPlugin from '@nocobase/plugin-users';
import { MockServer } from '@nocobase/test';
import { prepareApp } from './prepare';
@ -28,7 +28,7 @@ describe('role check action', () => {
roles: ['test']
}
});
const userPlugin = app.getPlugin('@nocobase/plugin-users') as UsersPlugin;
const userPlugin = app.getPlugin('users') as UsersPlugin;
const agent = app.agent().auth(userPlugin.jwtService.sign({
userId: user.get('id'),
}), { type: 'bearer' });

View File

@ -1,6 +1,6 @@
import { Database, Model } from '@nocobase/database';
import UsersPlugin from '@nocobase/plugin-users';
import { CollectionRepository } from '@nocobase/plugin-collection-manager';
import UsersPlugin from '@nocobase/plugin-users';
import { MockServer } from '@nocobase/test';
import { prepareApp } from './prepare';
@ -32,7 +32,7 @@ describe('role resource api', () => {
}
});
const userPlugin = app.getPlugin('@nocobase/plugin-users') as UsersPlugin;
const userPlugin = app.getPlugin('users') as UsersPlugin;
adminAgent = app.agent().auth(userPlugin.jwtService.sign({
userId: admin.get('id'),
}), { type: 'bearer' });

View File

@ -12,12 +12,12 @@ describe('role', () => {
beforeEach(async () => {
api = mockServer();
await api.cleanDb();
api.plugin(UsersPlugin);
api.plugin(PluginACL);
api.plugin(UsersPlugin, { name: 'users' });
api.plugin(PluginACL, { name: 'acl' });
await api.loadAndInstall();
db = api.db;
usersPlugin = api.getPlugin('@nocobase/plugin-users');
usersPlugin = api.getPlugin('users');
});
afterEach(async () => {

View File

@ -1,6 +1,6 @@
import { MockServer } from '@nocobase/test';
import { Database, Model } from '@nocobase/database';
import UsersPlugin from '@nocobase/plugin-users';
import { MockServer } from '@nocobase/test';
import { prepareApp } from './prepare';
@ -36,7 +36,7 @@ describe('role api', () => {
},
});
const userPlugin = app.getPlugin('@nocobase/plugin-users') as UsersPlugin;
const userPlugin = app.getPlugin('users') as UsersPlugin;
adminAgent = app.agent().auth(
userPlugin.jwtService.sign({
userId: admin.get('id'),

View File

@ -1,7 +1,7 @@
import { prepareApp } from './prepare';
import { MockServer } from '@nocobase/test';
import { Database } from '@nocobase/database';
import UsersPlugin from '@nocobase/plugin-users';
import { MockServer } from '@nocobase/test';
import { prepareApp } from './prepare';
describe('scope api', () => {
let app: MockServer;
@ -25,7 +25,7 @@ describe('scope api', () => {
}
});
const userPlugin = app.getPlugin('@nocobase/plugin-users') as UsersPlugin;
const userPlugin = app.getPlugin('users') as UsersPlugin;
adminAgent = app.agent().auth(userPlugin.jwtService.sign({
userId: admin.get('id'),
}), { type: 'bearer' });

View File

@ -15,7 +15,7 @@ describe('role', () => {
api = await prepareApp();
db = api.db;
usersPlugin = api.getPlugin('@nocobase/plugin-users');
usersPlugin = api.getPlugin('users');
ctx = {
db,

View File

@ -18,7 +18,7 @@ describe('actions', () => {
app = await prepareApp();
db = app.db;
pluginUser = app.getPlugin('@nocobase/plugin-users');
pluginUser = app.getPlugin('users');
adminUser = await db.getRepository('users').findOne({
filter: {
email: process.env.INIT_ROOT_EMAIL

View File

@ -442,9 +442,6 @@ export class PluginACL extends Plugin {
});
}
getName(): string {
return this.getPackageName(__dirname);
}
}
export default PluginACL;

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/plugin-audit-logs",
"version": "0.7.4-alpha.7",
"version": "0.8.0-alpha.1",
"main": "./lib/server/index.js",
"types": "./lib/server/index.d.ts",
"license": "Apache-2.0",
@ -11,11 +11,11 @@
}
],
"dependencies": {
"@nocobase/client": "0.7.4-alpha.7",
"@nocobase/server": "0.7.4-alpha.7"
"@nocobase/client": "0.8.0-alpha.1",
"@nocobase/server": "0.8.0-alpha.1"
},
"devDependencies": {
"@nocobase/test": "0.7.4-alpha.7"
"@nocobase/test": "0.8.0-alpha.1"
},
"gitHead": "b17d1ecae5dd0b9d4e7170dcf9663bb225598990"
}

View File

@ -8,8 +8,7 @@ describe('hook', () => {
beforeEach(async () => {
api = mockServer();
// api.plugin(require('@nocobase/plugin-users').default);
api.plugin(logPlugin);
api.plugin(logPlugin, { name: 'audit-logs' });
await api.load();
db = api.db;
db.collection({

View File

@ -21,8 +21,4 @@ export default class PluginActionLogs extends Plugin {
},
});
}
getName(): string {
return this.getPackageName(__dirname);
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/plugin-china-region",
"version": "0.7.4-alpha.7",
"version": "0.8.0-alpha.1",
"main": "lib/index.js",
"license": "Apache-2.0",
"licenses": [
@ -10,12 +10,12 @@
}
],
"dependencies": {
"@nocobase/client": "0.7.4-alpha.7",
"@nocobase/server": "0.7.4-alpha.7",
"@nocobase/client": "0.8.0-alpha.1",
"@nocobase/server": "0.8.0-alpha.1",
"china-division": "^2.4.0"
},
"devDependencies": {
"@nocobase/test": "0.7.4-alpha.7"
"@nocobase/test": "0.8.0-alpha.1"
},
"gitHead": "b17d1ecae5dd0b9d4e7170dcf9663bb225598990"
}

View File

@ -64,9 +64,6 @@ export class PluginChinaRegion extends Plugin {
// console.log(`${count} rows of region data imported in ${(Date.now() - timer) / 1000}s`);
}
getName(): string {
return this.getPackageName(__dirname);
}
}
export default PluginChinaRegion;

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/plugin-client",
"version": "0.7.4-alpha.7",
"version": "0.8.0-alpha.1",
"main": "lib/index.js",
"license": "Apache-2.0",
"licenses": [
@ -10,10 +10,10 @@
}
],
"dependencies": {
"@nocobase/server": "0.7.4-alpha.7"
"@nocobase/server": "0.8.0-alpha.1"
},
"devDependencies": {
"@nocobase/test": "0.7.4-alpha.7"
"@nocobase/test": "0.8.0-alpha.1"
},
"gitHead": "b17d1ecae5dd0b9d4e7170dcf9663bb225598990"
}

View File

@ -108,10 +108,6 @@ export class ClientPlugin extends Plugin {
});
}
}
getName(): string {
return this.getPackageName(__dirname);
}
}
export default ClientPlugin;

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/plugin-collection-manager",
"version": "0.7.4-alpha.7",
"version": "0.8.0-alpha.1",
"main": "lib/index.js",
"license": "Apache-2.0",
"licenses": [
@ -10,13 +10,13 @@
}
],
"dependencies": {
"@nocobase/database": "0.7.4-alpha.7",
"@nocobase/plugin-error-handler": "0.7.4-alpha.7",
"@nocobase/server": "0.7.4-alpha.7",
"@nocobase/database": "0.8.0-alpha.1",
"@nocobase/plugin-error-handler": "0.8.0-alpha.1",
"@nocobase/server": "0.8.0-alpha.1",
"sequelize": "^6.9.0"
},
"devDependencies": {
"@nocobase/test": "0.7.4-alpha.7"
"@nocobase/test": "0.8.0-alpha.1"
},
"gitHead": "b17d1ecae5dd0b9d4e7170dcf9663bb225598990"
}

View File

@ -46,7 +46,9 @@ describe('field indexes', () => {
},
});
expect(response3.status).toBe(400);
console.log(response3.body);
expect(response3.status).toBe(500);
const response4 = await agent.resource(tableName).create({
values: { title: 't1' },
@ -82,7 +84,7 @@ describe('field indexes', () => {
title: 't1',
},
});
expect(response2.status).toBe(400);
expect(response2.status).toBe(500);
// update field to remove unique constraint
await agent.resource('fields').update({
@ -109,7 +111,7 @@ describe('field indexes', () => {
},
});
expect(response4.status).toBe(400);
expect(response4.status).toBe(500);
// remove a duplicated record
await agent.resource(tableName).destroy({

View File

@ -13,12 +13,11 @@ export async function createApp(options = {}) {
await app.cleanDb();
}
app.plugin(PluginErrorHandler);
app.plugin(Plugin);
app.plugin(PluginUiSchema);
app.plugin(PluginErrorHandler, { name: 'error-handler' });
app.plugin(Plugin, { name: 'collection-manager' });
app.plugin(PluginUiSchema, { name: 'ui-schema-storage' });
// await app.load();
await app.install({ clean: true });
await app.loadAndInstall({ clean: true });
await app.start();
return app;
}

View File

@ -10,11 +10,10 @@ describe('collections repository', () => {
},
acl: false,
});
await app1.cleanDb();
app1.plugin(PluginErrorHandler);
app1.plugin(Plugin);
await app1.install({ clean: true });
await app1.start();
app1.plugin(PluginErrorHandler, { name: 'error-handler' });
app1.plugin(Plugin, { name: 'collection-manager' });
await app1.loadAndInstall({ clean: true });
await app1
.agent()
.resource('collections')
@ -121,10 +120,9 @@ describe('collections repository', () => {
tablePrefix: 'through_',
},
});
app2.plugin(PluginErrorHandler);
app2.plugin(Plugin);
app2.plugin(PluginErrorHandler, { name: 'error-handler' });
app2.plugin(Plugin, { name: 'collection-manager' });
await app2.load();
await app2.start();
await app2.db.sync({
force: true,

View File

@ -11,11 +11,12 @@ import {
afterCreateForReverseField,
beforeCreateForChildrenCollection,
beforeCreateForReverseField,
beforeInitOptions,
beforeInitOptions
} from './hooks';
import { CollectionModel, FieldModel } from './models';
export class CollectionManagerPlugin extends Plugin {
async beforeLoad() {
this.app.db.registerModels({
CollectionModel,
@ -116,22 +117,10 @@ export class CollectionManagerPlugin extends Plugin {
await model.remove(options);
});
this.app.on('beforeStart', async () => {
await this.app.db.getRepository<CollectionRepository>('collections').load();
});
this.app.on('beforeUpgrade', async () => {
await this.app.db.getRepository<CollectionRepository>('collections').load();
});
this.app.on('cli.beforeMigrator', async () => {
const exists = await this.app.db.collectionExistsInDb('collections');
if (exists) {
await this.app.db.getRepository<CollectionRepository>('collections').load();
this.app.on('afterLoad', async (app, options) => {
if (options?.method === 'install') {
return;
}
});
this.app.on('cli.beforeDbSync', async () => {
const exists = await this.app.db.collectionExistsInDb('collections');
if (exists) {
await this.app.db.getRepository<CollectionRepository>('collections').load();
@ -195,7 +184,7 @@ export class CollectionManagerPlugin extends Plugin {
directory: path.resolve(__dirname, './collections'),
});
const errorHandlerPlugin = <PluginErrorHandler>this.app.getPlugin('@nocobase/plugin-error-handler');
const errorHandlerPlugin = <PluginErrorHandler>this.app.getPlugin('error-handler');
errorHandlerPlugin.errorHandler.register(
(err) => {
return err instanceof UniqueConstraintError;
@ -205,10 +194,6 @@ export class CollectionManagerPlugin extends Plugin {
},
);
}
getName(): string {
return this.getPackageName(__dirname);
}
}
export default CollectionManagerPlugin;

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/plugin-error-handler",
"version": "0.7.4-alpha.7",
"version": "0.8.0-alpha.1",
"description": "",
"license": "Apache-2.0",
"licenses": [
@ -13,7 +13,7 @@
"types": "./lib/index.d.ts",
"dependencies": {
"@formily/json-schema": "^2.0.15",
"@nocobase/server": "0.7.4-alpha.7"
"@nocobase/server": "0.8.0-alpha.1"
},
"repository": {
"type": "git",

View File

@ -9,14 +9,14 @@ describe('create with exception', () => {
acl: false,
});
await app.cleanDb();
app.plugin(PluginErrorHandler);
app.plugin(PluginErrorHandler, { name: 'error-handler' });
});
afterEach(async () => {
await app.destroy();
});
it.only('should handle not null error', async () => {
it('should handle not null error', async () => {
app.collection({
name: 'users',
fields: [

View File

@ -7,9 +7,6 @@ import enUS from './locale/en_US';
import zhCN from './locale/zh_CN';
export class PluginErrorHandler extends Plugin {
getName(): string {
return this.getPackageName(__dirname);
}
errorHandler: ErrorHandler = new ErrorHandler();
i18nNs: string = 'error-handler';

View File

@ -1,10 +1,10 @@
{
"name": "@nocobase/plugin-export",
"version": "0.7.4-alpha.7",
"version": "0.8.0-alpha.1",
"main": "lib/server/index.js",
"dependencies": {
"@nocobase/client": "0.7.4-alpha.7",
"@nocobase/server": "0.7.4-alpha.7",
"@nocobase/client": "0.8.0-alpha.1",
"@nocobase/server": "0.8.0-alpha.1",
"node-xlsx": "^0.16.1"
},
"devDependencies": {

View File

@ -2,10 +2,6 @@ import { InstallOptions, Plugin } from '@nocobase/server';
import { exportXlsx } from './actions';
export class ExportPlugin extends Plugin {
getName(): string {
return this.getPackageName(__dirname);
}
beforeLoad() {}
async load() {

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/plugin-file-manager",
"version": "0.7.4-alpha.7",
"version": "0.8.0-alpha.1",
"main": "lib/index.js",
"license": "Apache-2.0",
"licenses": [
@ -11,7 +11,7 @@
],
"dependencies": {
"@koa/multer": "^3.0.0",
"@nocobase/server": "0.7.4-alpha.7",
"@nocobase/server": "0.8.0-alpha.1",
"aws-sdk": "^2.2.32",
"koa-static": "^5.0.0",
"mime-match": "^1.0.2",
@ -21,7 +21,7 @@
"multer-s3": "^2.10.0"
},
"devDependencies": {
"@nocobase/test": "0.7.4-alpha.7",
"@nocobase/test": "0.8.0-alpha.1",
"@types/koa-multer": "^1.0.1",
"@types/multer": "^1.4.5"
},

View File

@ -39,7 +39,4 @@ export default class PluginFileManager extends Plugin {
this.app.acl.allow('attachments', 'upload', 'loggedIn');
}
getName(): string {
return this.getPackageName(__dirname);
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/plugin-multi-app-manager",
"version": "0.7.4-alpha.7",
"version": "0.8.0-alpha.1",
"main": "lib/index.js",
"license": "Apache-2.0",
"licenses": [
@ -10,7 +10,7 @@
}
],
"dependencies": {
"@nocobase/server": "0.7.4-alpha.7"
"@nocobase/server": "0.8.0-alpha.1"
},
"gitHead": "b17d1ecae5dd0b9d4e7170dcf9663bb225598990"
}

View File

@ -71,7 +71,7 @@ describe('test with start', () => {
values: {
name,
options: {
plugins: ['@nocobase/plugin-ui-schema-storage'],
plugins: ['ui-schema-storage'],
},
},
});

View File

@ -57,7 +57,7 @@ describe('multiple apps create', () => {
values: {
name,
options: {
plugins: [['@nocobase/plugin-ui-schema-storage', { test: 'B' }]],
plugins: [['ui-schema-storage', { test: 'B' }]],
},
},
});
@ -65,7 +65,7 @@ describe('multiple apps create', () => {
const miniApp = app.appManager.applications.get(name);
expect(miniApp).toBeDefined();
const plugin = miniApp.pm.get('@nocobase/plugin-ui-schema-storage');
const plugin = miniApp.pm.get('ui-schema-storage');
expect(plugin).toBeDefined();
expect(plugin.options).toMatchObject({
@ -79,7 +79,7 @@ describe('multiple apps create', () => {
values: {
name,
options: {
plugins: ['@nocobase/plugin-ui-schema-storage'],
plugins: ['ui-schema-storage'],
},
},
});
@ -106,7 +106,7 @@ describe('multiple apps create', () => {
values: {
name,
options: {
plugins: ['@nocobase/plugin-ui-schema-storage'],
plugins: ['ui-schema-storage'],
},
},
});

View File

@ -17,10 +17,9 @@ export class ApplicationModel extends Model {
}
static async handleAppStart(app: Application, options: registerAppOptions) {
await app.load();
if (!options?.skipInstall) {
await app.install();
} else {
await app.load();
}
await app.start();
}

View File

@ -3,9 +3,6 @@ import { resolve } from 'path';
import { ApplicationModel } from './models/application';
export class PluginMultiAppManager extends Plugin {
getName(): string {
return this.getPackageName(__dirname);
}
async install(options?: InstallOptions) {
const repo = this.db.getRepository<any>('collections');

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/plugin-notifications",
"version": "0.7.4-alpha.7",
"version": "0.8.0-alpha.1",
"main": "lib/index.js",
"license": "Apache-2.0",
"licenses": [
@ -13,7 +13,7 @@
"nodemailer": "^6.6.1"
},
"devDependencies": {
"@nocobase/test": "0.7.4-alpha.7",
"@nocobase/test": "0.8.0-alpha.1",
"@types/nodemailer": "6.4.4",
"nodemailer-mock": "^1.5.11"
},

View File

@ -1,5 +1,5 @@
import path from 'path';
import { Plugin } from '@nocobase/server';
import path from 'path';
export default class PluginNotifications extends Plugin {
async load() {
@ -7,8 +7,4 @@ export default class PluginNotifications extends Plugin {
directory: path.resolve(__dirname, 'collections'),
});
}
getName(): string {
return this.getPackageName(__dirname);
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/plugin-system-settings",
"version": "0.7.4-alpha.7",
"version": "0.8.0-alpha.1",
"main": "lib/index.js",
"license": "Apache-2.0",
"licenses": [
@ -10,7 +10,7 @@
}
],
"devDependencies": {
"@nocobase/test": "0.7.4-alpha.7"
"@nocobase/test": "0.8.0-alpha.1"
},
"gitHead": "b17d1ecae5dd0b9d4e7170dcf9663bb225598990"
}

View File

@ -42,10 +42,6 @@ export class SystemSettingsPlugin extends Plugin {
}),
);
}
getName(): string {
return this.getPackageName(__dirname);
}
}
export default SystemSettingsPlugin;

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/plugin-ui-routes-storage",
"version": "0.7.4-alpha.7",
"version": "0.8.0-alpha.1",
"main": "lib/index.js",
"license": "Apache-2.0",
"licenses": [
@ -13,7 +13,7 @@
"flat-to-nested": "^1.1.1"
},
"devDependencies": {
"@nocobase/test": "0.7.4-alpha.7"
"@nocobase/test": "0.8.0-alpha.1"
},
"gitHead": "b17d1ecae5dd0b9d4e7170dcf9663bb225598990"
}

View File

@ -5,9 +5,6 @@ import { resolve } from 'path';
import { getAccessible } from './actions/getAccessible';
export class UiRoutesStoragePlugin extends Plugin {
getName(): string {
return this.getPackageName(__dirname);
}
async install() {
const repository = this.app.db.getRepository('uiRoutes');

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/plugin-ui-schema-storage",
"version": "0.7.4-alpha.7",
"version": "0.8.0-alpha.1",
"main": "lib/index.js",
"license": "Apache-2.0",
"licenses": [
@ -11,7 +11,7 @@
],
"devDependencies": {
"@formily/json-schema": "2.0.20",
"@nocobase/test": "0.7.4-alpha.7"
"@nocobase/test": "0.8.0-alpha.1"
},
"gitHead": "b17d1ecae5dd0b9d4e7170dcf9663bb225598990"
}

View File

@ -16,7 +16,7 @@ describe('action test', () => {
const queryInterface = db.sequelize.getQueryInterface();
await queryInterface.dropAllTables();
app.plugin(PluginUiSchema);
app.plugin(PluginUiSchema, { name: 'ui-schema-storage' });
await app.load();
await db.sync({

View File

@ -1,9 +1,9 @@
import { BelongsToManyRepository, Database } from '@nocobase/database';
import PluginUsers from '@nocobase/plugin-users';
import PluginACL from '@nocobase/plugin-acl';
import PluginErrorHandler from '@nocobase/plugin-error-handler';
import PluginCollectionManager from '@nocobase/plugin-collection-manager';
import PluginErrorHandler from '@nocobase/plugin-error-handler';
import UiSchemaStoragePlugin, { UiSchemaRepository } from '@nocobase/plugin-ui-schema-storage';
import PluginUsers from '@nocobase/plugin-users';
import { mockServer, MockServer } from '@nocobase/test';
describe('server hooks', () => {
@ -24,17 +24,17 @@ describe('server hooks', () => {
await app.cleanDb();
db = app.db;
app.plugin(UiSchemaStoragePlugin);
app.plugin(PluginErrorHandler);
app.plugin(PluginCollectionManager);
app.plugin(PluginUsers);
app.plugin(PluginACL);
app.plugin(UiSchemaStoragePlugin, { name: 'ui-schema-storage' });
app.plugin(PluginErrorHandler, { name: 'error-handler' });
app.plugin(PluginCollectionManager, { name: 'collection-manager' });
app.plugin(PluginUsers, { name: 'users' });
app.plugin(PluginACL, { name: 'acl' });
await app.loadAndInstall();
uiSchemaRepository = db.getRepository('uiSchemas');
uiSchemaPlugin = app.getPlugin<UiSchemaStoragePlugin>('@nocobase/plugin-ui-schema-storage');
uiSchemaPlugin = app.getPlugin<UiSchemaStoragePlugin>('ui-schema-storage');
});
it('should clean row struct', async () => {

View File

@ -1,6 +1,6 @@
import { Database } from '@nocobase/database';
import PluginErrorHandler from '@nocobase/plugin-error-handler';
import PluginCollectionManager from '@nocobase/plugin-collection-manager';
import PluginErrorHandler from '@nocobase/plugin-error-handler';
import UiSchemaStoragePlugin, { UiSchemaRepository } from '@nocobase/plugin-ui-schema-storage';
import { mockServer, MockServer } from '@nocobase/test';
@ -62,16 +62,16 @@ describe('server hooks', () => {
await app.cleanDb();
db = app.db;
app.plugin(UiSchemaStoragePlugin);
app.plugin(PluginErrorHandler);
app.plugin(PluginCollectionManager);
app.plugin(UiSchemaStoragePlugin, { name: 'ui-schema-storage' });
app.plugin(PluginErrorHandler, { name: 'error-handler' });
app.plugin(PluginCollectionManager, { name: 'collection-manager' });
await app.loadAndInstall();
uiSchemaRepository = db.getRepository('uiSchemas');
await uiSchemaRepository.insert(schema);
uiSchemaPlugin = app.getPlugin<UiSchemaStoragePlugin>('@nocobase/plugin-ui-schema-storage');
uiSchemaPlugin = app.getPlugin<UiSchemaStoragePlugin>('ui-schema-storage');
});
it('should call server hooks onFieldDestroy', async () => {

View File

@ -22,7 +22,7 @@ describe('ui schema model', () => {
const queryInterface = db.sequelize.getQueryInterface();
await queryInterface.dropAllTables();
app.plugin(PluginUiSchema);
app.plugin(PluginUiSchema, { name: 'ui-schema-storage' });
await app.load();

View File

@ -1,5 +1,5 @@
import { Database } from '@nocobase/database';
import { Cache } from '@nocobase/cache';
import { Database } from '@nocobase/database';
import { mockServer, MockServer } from '@nocobase/test';
import UiSchemaRepository, { GetJsonSchemaOptions, GetPropertiesOptions } from '../repository';
import PluginUiSchema from '../server';
@ -25,7 +25,7 @@ describe('ui_schema repository with cache', () => {
await db.sequelize.getQueryInterface().dropAllTables();
app.plugin(PluginUiSchema);
app.plugin(PluginUiSchema, { name: 'ui-schema-storage' });
await app.load();
await db.sync({

View File

@ -25,7 +25,7 @@ describe('ui_schema repository', () => {
const queryInterface = db.sequelize.getQueryInterface();
await queryInterface.dropAllTables();
app.plugin(PluginUiSchema);
app.plugin(PluginUiSchema, { name: 'ui-schema-storage' });
await app.load();
await db.sync({

View File

@ -22,7 +22,7 @@ describe('ui-schema', () => {
await db.sequelize.getQueryInterface().dropAllTables();
app.plugin(PluginUiSchema);
app.plugin(PluginUiSchema, { name: 'ui-schema-storage' });
await app.loadAndInstall();
uiSchemaRepository = db.getCollection('uiSchemas').repository as UiSchemaRepository;

View File

@ -70,10 +70,6 @@ export class UiSchemaStoragePlugin extends Plugin {
directory: path.resolve(__dirname, 'collections'),
});
}
getName(): string {
return this.getPackageName(__dirname);
}
}
export default UiSchemaStoragePlugin;

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/plugin-users",
"version": "0.7.4-alpha.7",
"version": "0.8.0-alpha.1",
"main": "lib/index.js",
"license": "Apache-2.0",
"licenses": [
@ -10,16 +10,16 @@
}
],
"dependencies": {
"@nocobase/actions": "0.7.4-alpha.7",
"@nocobase/database": "0.7.4-alpha.7",
"@nocobase/resourcer": "0.7.4-alpha.7",
"@nocobase/server": "0.7.4-alpha.7",
"@nocobase/utils": "0.7.4-alpha.7",
"jsonwebtoken": "^8.5.1",
"json-templates": "^4.2.0"
"@nocobase/actions": "0.8.0-alpha.1",
"@nocobase/database": "0.8.0-alpha.1",
"@nocobase/resourcer": "0.8.0-alpha.1",
"@nocobase/server": "0.8.0-alpha.1",
"@nocobase/utils": "0.8.0-alpha.1",
"json-templates": "^4.2.0",
"jsonwebtoken": "^8.5.1"
},
"devDependencies": {
"@nocobase/test": "0.7.4-alpha.7",
"@nocobase/test": "0.8.0-alpha.1",
"@types/jsonwebtoken": "^8.5.8"
},
"gitHead": "b17d1ecae5dd0b9d4e7170dcf9663bb225598990"

View File

@ -22,7 +22,7 @@ describe('actions', () => {
await app.loadAndInstall();
db = app.db;
pluginUser = app.getPlugin('@nocobase/plugin-users');
pluginUser = app.getPlugin('users');
adminUser = await db.getRepository('users').findOne({
filter: {
email: process.env.INIT_ROOT_EMAIL

View File

@ -10,9 +10,8 @@ describe('createdBy/updatedBy', () => {
beforeEach(async () => {
api = mockServer();
api.plugin(UsersPlugin, userPluginConfig);
api.plugin(PluginACL);
await api.cleanDb();
await api.loadAndInstall();
api.plugin(PluginACL, { name: 'acl' });
await api.loadAndInstall({ clean: true });
db = api.db;
});
@ -63,7 +62,7 @@ describe('createdBy/updatedBy', () => {
await db.sync();
const user1 = await db.getCollection('users').model.create();
const user2 = await db.getCollection('users').model.create();
const p1 = await Post.repository.create<any>({
const p1 = await Post.repository.create({
context: {
state: {
currentUser: user1,

View File

@ -1,6 +1,7 @@
import { UserPluginConfig } from '../server';
export const userPluginConfig: UserPluginConfig = {
name: 'users',
jwt: {
secret: '09f26e402586e2faa8da4c98a35f1b20d6b033c60',
},

View File

@ -15,7 +15,7 @@ export async function check(ctx: Context, next: Next) {
}
export async function signin(ctx: Context, next: Next) {
const { authenticators, jwtService } = ctx.app.getPlugin('@nocobase/plugin-users');
const { authenticators, jwtService } = ctx.app.getPlugin('users');
const branches = {};
for (const [name, authenticator] of authenticators.getEntities()) {
branches[name] = authenticator;

View File

@ -13,7 +13,7 @@ async function findUserByToken(ctx: Context) {
if (!token) {
return null;
}
const { jwtService } = ctx.app.getPlugin('@nocobase/plugin-users');
const { jwtService } = ctx.app.getPlugin('users');
try {
const { userId } = await jwtService.decode(token);
const collection = ctx.db.getCollection('users');

View File

@ -14,6 +14,7 @@ import { enUS, zhCN } from './locale';
import { parseToken } from './middlewares';
export interface UserPluginConfig {
name?: string;
jwt: JwtOptions;
}
@ -117,7 +118,7 @@ export default class UsersPlugin extends Plugin<UserPluginConfig> {
initAuthenticators(this);
// TODO(module): should move to preset
const verificationPlugin = this.app.getPlugin('@nocobase/plugin-verification') as any;
const verificationPlugin = this.app.getPlugin('verification') as any;
if (verificationPlugin && process.env.DEFAULT_SMS_VERIFY_CODE_PROVIDER) {
verificationPlugin.interceptors.register('users:signin', {
manual: true,
@ -220,8 +221,4 @@ export default class UsersPlugin extends Plugin<UserPluginConfig> {
await repo.db2cm('users');
}
}
getName(): string {
return this.getPackageName(__dirname);
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@nocobase/plugin-verification",
"version": "0.7.4-alpha.7",
"version": "0.8.0-alpha.1",
"main": "lib/index.js",
"license": "Apache-2.0",
"licenses": [
@ -13,13 +13,13 @@
"@alicloud/dysmsapi20170525": "2.0.17",
"@alicloud/openapi-client": "0.4.1",
"@alicloud/tea-util": "1.4.4",
"@nocobase/actions": "0.7.4-alpha.7",
"@nocobase/resourcer": "0.7.4-alpha.7",
"@nocobase/server": "0.7.4-alpha.7",
"@nocobase/utils": "0.7.4-alpha.7"
"@nocobase/actions": "0.8.0-alpha.1",
"@nocobase/resourcer": "0.8.0-alpha.1",
"@nocobase/server": "0.8.0-alpha.1",
"@nocobase/utils": "0.8.0-alpha.1"
},
"devDependencies": {
"@nocobase/test": "0.7.4-alpha.7"
"@nocobase/test": "0.8.0-alpha.1"
},
"gitHead": "7e9556e489007577fc0ed89063b3a9ce2f9aae53"
}

View File

@ -74,10 +74,6 @@ export default class VerificationPlugin extends Plugin {
});
};
getName(): string {
return this.getPackageName(__dirname);
}
async install() {
const {
DEFAULT_SMS_VERIFY_CODE_PROVIDER,

View File

@ -1,5 +1,5 @@
import { MockServer } from '@nocobase/test';
import Database from '@nocobase/database';
import { MockServer } from '@nocobase/test';
import Plugin, { Provider } from '..';
@ -21,7 +21,7 @@ describe('verification > Plugin', () => {
app = await getApp();
agent = app.agent();
db = app.db;
plugin = <Plugin>app.getPlugin('@nocobase/plugin-verification');
plugin = <Plugin>app.getPlugin('verification');
VerificationModel = db.getCollection('verifications').model;
AuthorModel = db.getCollection('authors').model;
AuthorRepo = db.getCollection('authors').repository;

Some files were not shown because too many files have changed in this diff Show More