feat(server): improve application (#177)
* feat: getRepository * getRepository return type * export action * refactor(plugin-acl): plugin * refactor(plugin-action-logs): plugin class * refactor(plugin-china-region): plugin class * refactor: plugin class * fix: cli start command * feat: pass install-command options into app.install * fix: cli args Co-authored-by: chenos <chenlinxh@gmail.com>
This commit is contained in:
parent
de25f56a79
commit
8e4336cbe1
@ -82,17 +82,13 @@ export default class PluginACL extends Plugin {
|
||||
});
|
||||
}
|
||||
|
||||
async load() {
|
||||
this.app.db.registerModels({
|
||||
RoleResourceActionModel,
|
||||
RoleResourceModel,
|
||||
});
|
||||
|
||||
async beforeLoad() {
|
||||
const acl = createACL();
|
||||
this.acl = acl;
|
||||
|
||||
await this.app.db.import({
|
||||
directory: path.resolve(__dirname, 'collections'),
|
||||
this.app.db.registerModels({
|
||||
RoleResourceActionModel,
|
||||
RoleResourceModel,
|
||||
});
|
||||
|
||||
this.registerAssociationFieldsActions();
|
||||
@ -100,8 +96,6 @@ export default class PluginACL extends Plugin {
|
||||
this.app.resourcer.define(availableActionResource);
|
||||
this.app.resourcer.define(roleCollectionsResource);
|
||||
|
||||
this.app.resourcer.use(this.acl.middleware());
|
||||
|
||||
this.app.db.on('roles.afterSave', async (model, options) => {
|
||||
const { transaction } = options;
|
||||
const roleName = model.get('name');
|
||||
@ -187,4 +181,12 @@ export default class PluginACL extends Plugin {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async load() {
|
||||
await this.app.db.import({
|
||||
directory: path.resolve(__dirname, 'collections'),
|
||||
});
|
||||
|
||||
this.app.resourcer.use(this.acl.middleware());
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,17 @@
|
||||
import path from 'path';
|
||||
import { Plugin } from '@nocobase/server';
|
||||
import { afterCreate, afterUpdate, afterDestroy } from './hooks';
|
||||
import { Plugin } from '@nocobase/server';
|
||||
|
||||
export default class PluginActionLogs extends Plugin {
|
||||
async beforeLoad() {
|
||||
this.db.on('afterCreate', afterCreate);
|
||||
this.db.on('afterUpdate', afterUpdate);
|
||||
this.db.on('afterDestroy', afterDestroy);
|
||||
}
|
||||
|
||||
export default class extends Plugin {
|
||||
name: 'action-logs';
|
||||
async load() {
|
||||
const database = this.app.db;
|
||||
await database.import({
|
||||
await this.db.import({
|
||||
directory: path.resolve(__dirname, 'collections'),
|
||||
});
|
||||
database.on('afterCreate', afterCreate);
|
||||
database.on('afterUpdate', afterUpdate);
|
||||
database.on('afterDestroy', afterDestroy);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -51,18 +51,17 @@ async function importData(model) {
|
||||
console.log(`${count} rows of region data imported in ${(Date.now() - timer) / 1000}s`);
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'china-region',
|
||||
async load(this: Plugin) {
|
||||
const db = this.app.db;
|
||||
|
||||
await db.import({
|
||||
directory: path.resolve(__dirname, 'collections'),
|
||||
});
|
||||
|
||||
this.app.on('db.init', async () => {
|
||||
const ChinaRegion = db.getCollection('china_regions').model;
|
||||
export default class PluginChinaRegion extends Plugin {
|
||||
async beforeLoad() {
|
||||
this.app.on('installing', async () => {
|
||||
const ChinaRegion = this.db.getCollection('china_regions').model;
|
||||
await importData(ChinaRegion);
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async load() {
|
||||
await this.db.import({
|
||||
directory: path.resolve(__dirname, 'collections'),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -10,16 +10,12 @@ import { afterCreateForReverseField } from './hooks/afterCreateForReverseField';
|
||||
export * from './repositories/collection-repository';
|
||||
|
||||
export default class CollectionManagerPlugin extends Plugin {
|
||||
async load() {
|
||||
async beforeLoad() {
|
||||
this.app.db.registerModels({
|
||||
CollectionModel,
|
||||
FieldModel,
|
||||
});
|
||||
|
||||
await this.app.db.import({
|
||||
directory: path.resolve(__dirname, './collections'),
|
||||
});
|
||||
|
||||
// 要在 beforeInitOptions 之前处理
|
||||
this.app.db.on('fields.beforeCreate', beforeCreateForReverseField(this.app.db));
|
||||
this.app.db.on('fields.beforeCreate', beforeCreateForChildrenCollection(this.app.db));
|
||||
@ -47,4 +43,10 @@ export default class CollectionManagerPlugin extends Plugin {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async load() {
|
||||
await this.app.db.import({
|
||||
directory: path.resolve(__dirname, './collections'),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,44 +1,41 @@
|
||||
import path from 'path';
|
||||
import Database from '@nocobase/database';
|
||||
import Resourcer from '@nocobase/resourcer';
|
||||
import { PluginOptions } from '@nocobase/server';
|
||||
import { Plugin } from '@nocobase/server';
|
||||
|
||||
import { action as uploadAction, middleware as uploadMiddleware } from './actions/upload';
|
||||
import { getStorageConfig } from './storages';
|
||||
import { STORAGE_TYPE_LOCAL } from './constants';
|
||||
|
||||
export default {
|
||||
name: 'file-manager',
|
||||
async load() {
|
||||
const database: Database = this.app.db;
|
||||
const resourcer: Resourcer = this.app.resourcer;
|
||||
|
||||
await database.import({
|
||||
directory: path.resolve(__dirname, 'collections'),
|
||||
});
|
||||
|
||||
// 暂时中间件只能通过 use 加进来
|
||||
resourcer.use(uploadMiddleware);
|
||||
resourcer.registerActionHandler('upload', uploadAction);
|
||||
|
||||
const { DEFAULT_STORAGE_TYPE } = process.env;
|
||||
|
||||
if (process.env.NOCOBASE_ENV !== 'production') {
|
||||
await getStorageConfig(STORAGE_TYPE_LOCAL).middleware(this.app);
|
||||
export default class PluginFileManager extends Plugin {
|
||||
storageType() {
|
||||
return process.env.DEFAULT_STORAGE_TYPE;
|
||||
}
|
||||
|
||||
this.app.on('db.init', async () => {
|
||||
const defaultStorageConfig = getStorageConfig(DEFAULT_STORAGE_TYPE);
|
||||
async beforeLoad() {
|
||||
this.app.on('installing', async () => {
|
||||
const defaultStorageConfig = getStorageConfig(this.storageType());
|
||||
if (defaultStorageConfig) {
|
||||
const Storage = database.getCollection('storages');
|
||||
const Storage = this.db.getCollection('storages');
|
||||
await Storage.repository.create({
|
||||
values: {
|
||||
...defaultStorageConfig.defaults(),
|
||||
type: DEFAULT_STORAGE_TYPE,
|
||||
type: this.storageType(),
|
||||
default: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
} as PluginOptions;
|
||||
}
|
||||
|
||||
async load() {
|
||||
await this.db.import({
|
||||
directory: path.resolve(__dirname, 'collections'),
|
||||
});
|
||||
|
||||
// 暂时中间件只能通过 use 加进来
|
||||
this.app.resourcer.use(uploadMiddleware);
|
||||
this.app.resourcer.registerActionHandler('upload', uploadAction);
|
||||
|
||||
if (process.env.NOCOBASE_ENV !== 'production') {
|
||||
await getStorageConfig(STORAGE_TYPE_LOCAL).middleware(this.app);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,10 @@
|
||||
import path from 'path';
|
||||
import { PluginOptions } from '@nocobase/server';
|
||||
import { Plugin } from '@nocobase/server';
|
||||
|
||||
export default {
|
||||
name: 'notifications',
|
||||
export default class PluginNotifications extends Plugin {
|
||||
async load() {
|
||||
await this.app.db.import({
|
||||
directory: path.resolve(__dirname, 'collections'),
|
||||
});
|
||||
}
|
||||
} as PluginOptions
|
||||
}
|
||||
|
@ -1,19 +1,10 @@
|
||||
import path from 'path';
|
||||
import { PluginOptions } from '@nocobase/server';
|
||||
import { Plugin, PluginOptions } from '@nocobase/server';
|
||||
|
||||
export default {
|
||||
name: 'system-settings',
|
||||
async load() {
|
||||
const database = this.app.db;
|
||||
|
||||
await database.import({
|
||||
directory: path.resolve(__dirname, 'collections'),
|
||||
});
|
||||
|
||||
const SystemSetting = database.getCollection('system_settings');
|
||||
|
||||
this.app.on('db.init', async () => {
|
||||
await SystemSetting.repository.create({
|
||||
export default class PluginSystemSettings extends Plugin {
|
||||
async beforeLoad() {
|
||||
this.app.on('installing', async () => {
|
||||
await this.db.getRepository('system_settings').create({
|
||||
values: {
|
||||
title: 'NocoBase',
|
||||
logo: {
|
||||
@ -26,5 +17,11 @@ export default {
|
||||
},
|
||||
});
|
||||
});
|
||||
},
|
||||
} as PluginOptions;
|
||||
}
|
||||
|
||||
async load() {
|
||||
await this.app.db.import({
|
||||
directory: path.resolve(__dirname, 'collections'),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { mockServer, MockServer } from '@nocobase/test';
|
||||
import { Collection, Database } from '@nocobase/database';
|
||||
import PluginUiSchema, { UiSchemaRepository } from '@nocobase/plugin-ui-schema';
|
||||
import PluginUiSchema, { UiSchemaRepository } from '..';
|
||||
|
||||
describe('ui schema model', () => {
|
||||
let app: MockServer;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { ISchema } from '@formily/json-schema';
|
||||
import { mockServer, MockServer } from '@nocobase/test';
|
||||
import { Collection, Database } from '@nocobase/database';
|
||||
import PluginUiSchema, { UiSchemaRepository } from '@nocobase/plugin-ui-schema';
|
||||
import PluginUiSchema, { UiSchemaRepository } from '..';
|
||||
|
||||
describe('ui-schema', () => {
|
||||
let app: MockServer;
|
||||
|
@ -11,17 +11,13 @@ export default class PluginUiSchema extends Plugin {
|
||||
});
|
||||
}
|
||||
|
||||
async load() {
|
||||
async beforeLoad() {
|
||||
const db = this.app.db;
|
||||
|
||||
this.app.db.registerModels({ MagicAttributeModel });
|
||||
|
||||
this.registerRepository();
|
||||
|
||||
await db.import({
|
||||
directory: path.resolve(__dirname, 'collections'),
|
||||
});
|
||||
|
||||
db.on('ui_schemas.beforeCreate', (model) => {
|
||||
model.set('uid', model.get('x-uid'));
|
||||
});
|
||||
@ -44,23 +40,15 @@ export default class PluginUiSchema extends Plugin {
|
||||
});
|
||||
});
|
||||
|
||||
await db.getCollection('ui_schemas').sync({
|
||||
force: false,
|
||||
alter: {
|
||||
drop: false,
|
||||
},
|
||||
});
|
||||
|
||||
await db.getCollection('ui_schema_tree_path').sync({
|
||||
force: false,
|
||||
alter: {
|
||||
drop: false,
|
||||
},
|
||||
});
|
||||
|
||||
this.app.resourcer.define({
|
||||
name: 'ui_schemas',
|
||||
actions: uiSchemaActions,
|
||||
});
|
||||
}
|
||||
|
||||
async load() {
|
||||
await this.db.import({
|
||||
directory: path.resolve(__dirname, 'collections'),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ describe('role', () => {
|
||||
await api.cleanDb();
|
||||
api.plugin(require('../server').default);
|
||||
api.plugin(PluginACL);
|
||||
await api.loadAndSync();
|
||||
await api.loadAndInstall();
|
||||
|
||||
db = api.db;
|
||||
});
|
||||
|
@ -2,16 +2,12 @@ import path from 'path';
|
||||
import * as actions from './actions/users';
|
||||
import * as middlewares from './middlewares';
|
||||
import { Collection } from '@nocobase/database';
|
||||
import { PluginOptions } from '@nocobase/server';
|
||||
import { Plugin, PluginOptions } from '@nocobase/server';
|
||||
|
||||
export default {
|
||||
name: 'users',
|
||||
async load() {
|
||||
const database = this.app.db;
|
||||
const resourcer = this.app.resourcer;
|
||||
|
||||
this.app.on('db.init', async () => {
|
||||
const User = database.getCollection('users');
|
||||
export default class PluginUsers extends Plugin {
|
||||
async beforeLoad() {
|
||||
this.app.on('installing', async () => {
|
||||
const User = this.db.getCollection('users');
|
||||
await User.model.create({
|
||||
nickname: 'Super Admin',
|
||||
email: process.env.ADMIN_EMAIL || 'admin@nocobase.com',
|
||||
@ -19,7 +15,7 @@ export default {
|
||||
});
|
||||
});
|
||||
|
||||
database.on('users.afterCreateWithAssociations', async (model, options) => {
|
||||
this.db.on('users.afterCreateWithAssociations', async (model, options) => {
|
||||
const { transaction } = options;
|
||||
|
||||
const defaultRole = await this.app.db.getRepository('roles').findOne({
|
||||
@ -34,7 +30,7 @@ export default {
|
||||
}
|
||||
});
|
||||
|
||||
database.on('afterDefineCollection', (collection: Collection) => {
|
||||
this.db.on('afterDefineCollection', (collection: Collection) => {
|
||||
let { createdBy, updatedBy } = collection.options;
|
||||
if (createdBy === true) {
|
||||
collection.setField('createdById', {
|
||||
@ -65,14 +61,16 @@ export default {
|
||||
}
|
||||
});
|
||||
|
||||
await database.import({
|
||||
directory: path.resolve(__dirname, 'collections'),
|
||||
});
|
||||
|
||||
for (const [key, action] of Object.entries(actions)) {
|
||||
resourcer.registerActionHandler(`users:${key}`, action);
|
||||
this.app.resourcer.registerActionHandler(`users:${key}`, action);
|
||||
}
|
||||
|
||||
resourcer.use(middlewares.parseToken({}));
|
||||
},
|
||||
} as PluginOptions;
|
||||
this.app.resourcer.use(middlewares.parseToken({}));
|
||||
}
|
||||
|
||||
async load() {
|
||||
await this.db.import({
|
||||
directory: path.resolve(__dirname, 'collections'),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import { Application } from '../application';
|
||||
import { Plugin } from '../plugin';
|
||||
|
||||
class MyPlugin extends Plugin {
|
||||
load() {}
|
||||
async load() {}
|
||||
}
|
||||
|
||||
describe('application', () => {
|
||||
|
@ -65,10 +65,12 @@ interface ListenOptions {
|
||||
}
|
||||
|
||||
interface StartOptions {
|
||||
cliArgs?: any[];
|
||||
listen?: ListenOptions;
|
||||
}
|
||||
|
||||
interface InstallOptions {
|
||||
cliArgs?: any[];
|
||||
clean?: CleanOptions | boolean;
|
||||
sync?: SyncOptions;
|
||||
}
|
||||
@ -174,8 +176,8 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
|
||||
await this.emitAsync('afterStart', this, options);
|
||||
}
|
||||
|
||||
async stop() {
|
||||
await this.emitAsync('beforeStop', this);
|
||||
async stop(options?: any) {
|
||||
await this.emitAsync('beforeStop', this, options);
|
||||
|
||||
// close database connection
|
||||
await this.db.close();
|
||||
@ -188,7 +190,6 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
this.listenServer = null;
|
||||
resolve(true);
|
||||
});
|
||||
@ -197,13 +198,13 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
|
||||
await closeServer();
|
||||
}
|
||||
|
||||
await this.emitAsync('afterStop', this);
|
||||
await this.emitAsync('afterStop', this, options);
|
||||
}
|
||||
|
||||
async destroy() {
|
||||
await this.emitAsync('beforeDestroy', this);
|
||||
await this.stop();
|
||||
await this.emitAsync('afterDestroy', this);
|
||||
async destroy(options?: any) {
|
||||
await this.emitAsync('beforeDestroy', this, options);
|
||||
await this.stop(options);
|
||||
await this.emitAsync('afterDestroy', this, options);
|
||||
}
|
||||
|
||||
async install(options?: InstallOptions) {
|
||||
@ -211,7 +212,9 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
|
||||
await this.db.clean(isBoolean(options.clean) ? { drop: options.clean } : options.clean);
|
||||
}
|
||||
await this.db.sync(options?.sync);
|
||||
|
||||
await this.emitAsync('beforeInstall', this, options);
|
||||
await this.emitAsync('installing', this, options);
|
||||
await this.emitAsync('afterInstall', this, options);
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { DefaultContext, DefaultState } from 'koa';
|
||||
import bodyParser from 'koa-bodyparser';
|
||||
import cors from '@koa/cors';
|
||||
import Database from '@nocobase/database';
|
||||
import Resourcer from '@nocobase/resourcer';
|
||||
import { Command } from 'commander';
|
||||
import i18next from 'i18next';
|
||||
import { DefaultContext, DefaultState } from 'koa';
|
||||
import bodyParser from 'koa-bodyparser';
|
||||
import Application, { ApplicationOptions } from './application';
|
||||
import { dataWrapping } from './middlewares/data-wrapping';
|
||||
import { table2resource } from './middlewares/table2resource';
|
||||
import i18next from 'i18next';
|
||||
|
||||
export function createDatabase(options: ApplicationOptions) {
|
||||
if (options.database instanceof Database) {
|
||||
@ -37,12 +37,11 @@ export function createCli(app: Application, options: ApplicationOptions) {
|
||||
cli
|
||||
.command('db:sync')
|
||||
.option('-f, --force')
|
||||
.action(async (...args) => {
|
||||
.action(async (...cliArgs) => {
|
||||
const [opts] = cliArgs;
|
||||
console.log('db sync...');
|
||||
const cli = args.pop();
|
||||
const force = cli.opts()?.force;
|
||||
await app.db.sync(
|
||||
force
|
||||
opts.force
|
||||
? {
|
||||
force: true,
|
||||
alter: {
|
||||
@ -51,44 +50,44 @@ export function createCli(app: Application, options: ApplicationOptions) {
|
||||
}
|
||||
: {},
|
||||
);
|
||||
await app.destroy();
|
||||
await app.stop({
|
||||
cliArgs,
|
||||
});
|
||||
});
|
||||
|
||||
cli
|
||||
.command('init')
|
||||
.command('install')
|
||||
.option('-f, --force')
|
||||
.action(async (opts, ...args) => {
|
||||
if (!opts?.force) {
|
||||
const tables = await app.db.sequelize.getQueryInterface().showAllTables();
|
||||
if (tables.includes('collections')) {
|
||||
console.log('NocoBase is already installed. To reinstall, please execute:');
|
||||
console.log();
|
||||
let command = 'yarn nocobase init --force';
|
||||
for (const [key, value] of Object.entries(opts || {})) {
|
||||
command += value === true ? ` --${key}` : ` --${key}=${value}`;
|
||||
}
|
||||
console.log(command);
|
||||
console.log();
|
||||
return;
|
||||
}
|
||||
}
|
||||
await app.db.sync({
|
||||
force: true,
|
||||
.action(async (...cliArgs) => {
|
||||
const [opts] = cliArgs;
|
||||
await app.install({
|
||||
cliArgs,
|
||||
sync: {
|
||||
force: opts.force,
|
||||
},
|
||||
});
|
||||
await app.stop({
|
||||
cliArgs,
|
||||
});
|
||||
await app.emitAsync('db.init', opts, ...args);
|
||||
await app.destroy();
|
||||
});
|
||||
|
||||
cli
|
||||
.command('start')
|
||||
.option('-p, --port [port]')
|
||||
.action(async (...args) => {
|
||||
const cli = args.pop();
|
||||
const opts = cli.opts();
|
||||
await app.emitAsync('beforeStart');
|
||||
await app.start(opts.port || 3000);
|
||||
console.log(`http://localhost:${opts.port || 3000}/`);
|
||||
.action(async (...cliArgs) => {
|
||||
const [opts] = cliArgs;
|
||||
const port = opts.port || 3000;
|
||||
|
||||
await app.start({
|
||||
cliArgs,
|
||||
listen: {
|
||||
port,
|
||||
},
|
||||
});
|
||||
|
||||
console.log(`http://localhost:${port}/`);
|
||||
});
|
||||
|
||||
return cli;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { Database } from '@nocobase/database';
|
||||
import { Application } from './application';
|
||||
import path from 'path';
|
||||
|
||||
export interface PluginInterface {
|
||||
beforeLoad?: () => void;
|
||||
@ -41,5 +42,16 @@ export abstract class Plugin<O = any> implements PluginInterface {
|
||||
|
||||
beforeLoad() {}
|
||||
|
||||
abstract load();
|
||||
async load() {
|
||||
const collectionPath = this.collectionPath();
|
||||
if (collectionPath) {
|
||||
await this.db.import({
|
||||
directory: collectionPath,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
collectionPath() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user