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: 'Getting started',
'title.zh-CN': '快速开始', 'title.zh-CN': '快速开始',
type: 'group', type: 'group',
children: [ children: ['/development/index', '/development/your-fisrt-plugin'],
'/development/index',
'/development/your-fisrt-plugin',
],
}, },
{ {
title: 'Extension Guides', title: 'Extension Guides',
@ -191,8 +188,8 @@ export default {
title: '@nocobase/server', title: '@nocobase/server',
type: 'subMenu', type: 'subMenu',
children: [ children: [
'/api/server/application', '/api/server/application',
//'/api/server/plugin-manager', // '/api/server/plugin-manager',
'/api/server/plugin', '/api/server/plugin',
], ],
}, },

View File

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

View File

@ -6,16 +6,54 @@
### `create()` ### `create()`
在本地创建一个插件脚手架
**签名**
```ts
create(name, options): void;
```
### `addStatic()`
**签名**
```ts
addStatic(plugin: any, options?: PluginOptions): Plugin;
```
**示例**
```ts
pm.addStatic('nocobase');
```
### `add()` ### `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()` ### `get()`
### `install()` 获取插件实例
### `upgrade()`
### `enable()` ### `enable()`
### `disable()` ### `disable()`
### `remove()` ### `remove()`
### `upgrade()`

View File

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

View File

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

View File

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

View File

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

View File

@ -1,3 +1,3 @@
import { PluginConfiguration } from '@nocobase/server'; 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", "name": "@nocobase/acl",
"version": "0.7.4-alpha.7", "version": "0.8.0-alpha.1",
"description": "", "description": "",
"license": "Apache-2.0", "license": "Apache-2.0",
"licenses": [ "licenses": [
@ -12,7 +12,7 @@
"main": "./lib/index.js", "main": "./lib/index.js",
"types": "./lib/index.d.ts", "types": "./lib/index.d.ts",
"dependencies": { "dependencies": {
"@nocobase/resourcer": "0.7.4-alpha.7", "@nocobase/resourcer": "0.8.0-alpha.1",
"json-templates": "^4.2.0" "json-templates": "^4.2.0"
}, },
"repository": { "repository": {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,7 +4,11 @@ import { notification } from 'antd';
import React from 'react'; import React from 'react';
export class APIClient extends APIClientSDK { 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() { interceptors() {
super.interceptors(); super.interceptors();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,3 @@
import axios from 'axios';
import { resolve } from 'path';
import Application from '../application'; import Application from '../application';
export default (app: Application) => { export default (app: Application) => {
@ -8,101 +6,6 @@ export default (app: Application) => {
.argument('<method>') .argument('<method>')
.arguments('<plugins...>') .arguments('<plugins...>')
.action(async (method, plugins, options, ...args) => { .action(async (method, plugins, options, ...args) => {
const { APP_PORT, API_BASE_PATH = '/api/', API_BASE_URL } = process.env; app.pm.clientWrite({ method, plugins });
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]();
}); });
}; };

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
{ {
"name": "@nocobase/plugin-acl", "name": "@nocobase/plugin-acl",
"version": "0.7.4-alpha.7", "version": "0.8.0-alpha.1",
"description": "", "description": "",
"license": "Apache-2.0", "license": "Apache-2.0",
"licenses": [ "licenses": [
@ -12,10 +12,10 @@
"main": "./lib/index.js", "main": "./lib/index.js",
"types": "./lib/index.d.ts", "types": "./lib/index.d.ts",
"dependencies": { "dependencies": {
"@nocobase/acl": "0.7.4-alpha.7", "@nocobase/acl": "0.8.0-alpha.1",
"@nocobase/database": "0.7.4-alpha.7", "@nocobase/database": "0.8.0-alpha.1",
"@nocobase/plugin-users": "0.7.4-alpha.7", "@nocobase/plugin-users": "0.8.0-alpha.1",
"@nocobase/server": "0.7.4-alpha.7" "@nocobase/server": "0.8.0-alpha.1"
}, },
"repository": { "repository": {
"type": "git", "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( adminAgent = app.agent().auth(
userPlugin.jwtService.sign({ 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( const userAgent = app.agent().auth(
userPlugin.jwtService.sign({ userPlugin.jwtService.sign({
userId: user.get('id'), 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( userAgent = app.agent().auth(
userPlugin.jwtService.sign({ userPlugin.jwtService.sign({
userId: user.get('id'), 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({ adminAgent = app.agent().auth(userPlugin.jwtService.sign({
userId: admin.get('id'), userId: admin.get('id'),
}), { type: 'bearer' }); }), { type: 'bearer' });

View File

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

View File

@ -67,7 +67,7 @@ describe('own test', () => {
admin = await db.getRepository('users').findOne(); admin = await db.getRepository('users').findOne();
pluginUser = app.getPlugin('@nocobase/plugin-users'); pluginUser = app.getPlugin('users');
adminToken = pluginUser.jwtService.sign({ userId: admin.get('id') }); 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 { mockServer } from '@nocobase/test';
import PluginACL from '../server'; import PluginACL from '../server';
@ -9,15 +5,14 @@ export async function prepareApp() {
const app = mockServer({ const app = mockServer({
registerActions: true, registerActions: true,
acl: true, acl: true,
plugins: ['error-handler', 'users', 'ui-schema-storage', 'collection-manager'],
}); });
await app.cleanDb(); await app.cleanDb();
app.plugin(PluginUsers); app.plugin(PluginACL, {
app.plugin(PluginUiSchema); name: 'acl',
app.plugin(PluginErrorHandler); });
app.plugin(PluginCollectionManager);
app.plugin(PluginACL);
await app.loadAndInstall(); await app.loadAndInstall();
await app.db.sync(); await app.db.sync();

View File

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

View File

@ -1,6 +1,6 @@
import { Database, Model } from '@nocobase/database'; import { Database, Model } from '@nocobase/database';
import UsersPlugin from '@nocobase/plugin-users';
import { CollectionRepository } from '@nocobase/plugin-collection-manager'; import { CollectionRepository } from '@nocobase/plugin-collection-manager';
import UsersPlugin from '@nocobase/plugin-users';
import { MockServer } from '@nocobase/test'; import { MockServer } from '@nocobase/test';
import { prepareApp } from './prepare'; 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({ adminAgent = app.agent().auth(userPlugin.jwtService.sign({
userId: admin.get('id'), userId: admin.get('id'),
}), { type: 'bearer' }); }), { type: 'bearer' });

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -8,8 +8,7 @@ describe('hook', () => {
beforeEach(async () => { beforeEach(async () => {
api = mockServer(); api = mockServer();
// api.plugin(require('@nocobase/plugin-users').default); api.plugin(logPlugin, { name: 'audit-logs' });
api.plugin(logPlugin);
await api.load(); await api.load();
db = api.db; db = api.db;
db.collection({ 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", "name": "@nocobase/plugin-china-region",
"version": "0.7.4-alpha.7", "version": "0.8.0-alpha.1",
"main": "lib/index.js", "main": "lib/index.js",
"license": "Apache-2.0", "license": "Apache-2.0",
"licenses": [ "licenses": [
@ -10,12 +10,12 @@
} }
], ],
"dependencies": { "dependencies": {
"@nocobase/client": "0.7.4-alpha.7", "@nocobase/client": "0.8.0-alpha.1",
"@nocobase/server": "0.7.4-alpha.7", "@nocobase/server": "0.8.0-alpha.1",
"china-division": "^2.4.0" "china-division": "^2.4.0"
}, },
"devDependencies": { "devDependencies": {
"@nocobase/test": "0.7.4-alpha.7" "@nocobase/test": "0.8.0-alpha.1"
}, },
"gitHead": "b17d1ecae5dd0b9d4e7170dcf9663bb225598990" "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`); // console.log(`${count} rows of region data imported in ${(Date.now() - timer) / 1000}s`);
} }
getName(): string {
return this.getPackageName(__dirname);
}
} }
export default PluginChinaRegion; export default PluginChinaRegion;

View File

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

View File

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

View File

@ -1,6 +1,6 @@
{ {
"name": "@nocobase/plugin-collection-manager", "name": "@nocobase/plugin-collection-manager",
"version": "0.7.4-alpha.7", "version": "0.8.0-alpha.1",
"main": "lib/index.js", "main": "lib/index.js",
"license": "Apache-2.0", "license": "Apache-2.0",
"licenses": [ "licenses": [
@ -10,13 +10,13 @@
} }
], ],
"dependencies": { "dependencies": {
"@nocobase/database": "0.7.4-alpha.7", "@nocobase/database": "0.8.0-alpha.1",
"@nocobase/plugin-error-handler": "0.7.4-alpha.7", "@nocobase/plugin-error-handler": "0.8.0-alpha.1",
"@nocobase/server": "0.7.4-alpha.7", "@nocobase/server": "0.8.0-alpha.1",
"sequelize": "^6.9.0" "sequelize": "^6.9.0"
}, },
"devDependencies": { "devDependencies": {
"@nocobase/test": "0.7.4-alpha.7" "@nocobase/test": "0.8.0-alpha.1"
}, },
"gitHead": "b17d1ecae5dd0b9d4e7170dcf9663bb225598990" "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({ const response4 = await agent.resource(tableName).create({
values: { title: 't1' }, values: { title: 't1' },
@ -82,7 +84,7 @@ describe('field indexes', () => {
title: 't1', title: 't1',
}, },
}); });
expect(response2.status).toBe(400); expect(response2.status).toBe(500);
// update field to remove unique constraint // update field to remove unique constraint
await agent.resource('fields').update({ 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 // remove a duplicated record
await agent.resource(tableName).destroy({ await agent.resource(tableName).destroy({

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -39,7 +39,4 @@ export default class PluginFileManager extends Plugin {
this.app.acl.allow('attachments', 'upload', 'loggedIn'); 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", "name": "@nocobase/plugin-multi-app-manager",
"version": "0.7.4-alpha.7", "version": "0.8.0-alpha.1",
"main": "lib/index.js", "main": "lib/index.js",
"license": "Apache-2.0", "license": "Apache-2.0",
"licenses": [ "licenses": [
@ -10,7 +10,7 @@
} }
], ],
"dependencies": { "dependencies": {
"@nocobase/server": "0.7.4-alpha.7" "@nocobase/server": "0.8.0-alpha.1"
}, },
"gitHead": "b17d1ecae5dd0b9d4e7170dcf9663bb225598990" "gitHead": "b17d1ecae5dd0b9d4e7170dcf9663bb225598990"
} }

View File

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

View File

@ -57,7 +57,7 @@ describe('multiple apps create', () => {
values: { values: {
name, name,
options: { 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); const miniApp = app.appManager.applications.get(name);
expect(miniApp).toBeDefined(); 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).toBeDefined();
expect(plugin.options).toMatchObject({ expect(plugin.options).toMatchObject({
@ -79,7 +79,7 @@ describe('multiple apps create', () => {
values: { values: {
name, name,
options: { options: {
plugins: ['@nocobase/plugin-ui-schema-storage'], plugins: ['ui-schema-storage'],
}, },
}, },
}); });
@ -106,7 +106,7 @@ describe('multiple apps create', () => {
values: { values: {
name, name,
options: { 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) { static async handleAppStart(app: Application, options: registerAppOptions) {
await app.load();
if (!options?.skipInstall) { if (!options?.skipInstall) {
await app.install(); await app.install();
} else {
await app.load();
} }
await app.start(); await app.start();
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -15,7 +15,7 @@ export async function check(ctx: Context, next: Next) {
} }
export async function signin(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 = {}; const branches = {};
for (const [name, authenticator] of authenticators.getEntities()) { for (const [name, authenticator] of authenticators.getEntities()) {
branches[name] = authenticator; branches[name] = authenticator;

View File

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

View File

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

View File

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

View File

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

View File

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

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