2020-11-11 18:16:18 +08:00
|
|
|
import Koa from 'koa';
|
2021-01-13 16:23:15 +08:00
|
|
|
import Database, { DatabaseOptions } from '@nocobase/database';
|
2020-11-12 11:48:27 +08:00
|
|
|
import Resourcer from '@nocobase/resourcer';
|
2021-09-05 23:59:38 +08:00
|
|
|
import { Command } from 'commander';
|
|
|
|
import { actions, middlewares as m } from '@nocobase/actions';
|
|
|
|
import cors from '@koa/cors';
|
|
|
|
import { dbResourceRouter } from './middlewares';
|
|
|
|
import bodyParser from 'koa-bodyparser';
|
2020-11-12 11:48:27 +08:00
|
|
|
|
|
|
|
export interface ApplicationOptions {
|
2021-01-13 16:23:15 +08:00
|
|
|
database: DatabaseOptions;
|
|
|
|
resourcer?: any;
|
2020-11-12 11:48:27 +08:00
|
|
|
}
|
2020-11-11 18:16:18 +08:00
|
|
|
|
2021-01-13 16:23:15 +08:00
|
|
|
export class Application extends Koa {
|
2020-11-11 18:16:18 +08:00
|
|
|
|
2021-01-13 16:23:15 +08:00
|
|
|
public readonly database: Database;
|
|
|
|
|
|
|
|
public readonly resourcer: Resourcer;
|
2020-11-11 18:16:18 +08:00
|
|
|
|
2021-09-05 23:59:38 +08:00
|
|
|
public readonly cli: Command;
|
|
|
|
|
2020-12-18 19:54:53 +08:00
|
|
|
protected plugins = new Map<string, any>();
|
2020-11-12 11:48:27 +08:00
|
|
|
|
2021-01-13 16:23:15 +08:00
|
|
|
constructor(options: ApplicationOptions) {
|
|
|
|
super();
|
|
|
|
this.database = new Database(options.database);
|
|
|
|
this.resourcer = new Resourcer();
|
2021-09-05 23:59:38 +08:00
|
|
|
this.cli = new Command();
|
|
|
|
|
|
|
|
this.use(bodyParser());
|
|
|
|
this.use(cors({
|
|
|
|
exposeHeaders: ['content-disposition'],
|
|
|
|
}));
|
|
|
|
|
|
|
|
this.resourcer.registerActionHandlers({ ...actions.common, ...actions.associate });
|
|
|
|
|
|
|
|
this.use(async (ctx, next) => {
|
|
|
|
ctx.db = this.database;
|
|
|
|
ctx.database = this.database;
|
|
|
|
await next();
|
|
|
|
});
|
|
|
|
|
|
|
|
this.resourcer.use(m.associated);
|
|
|
|
this.use(m.dataWrapping);
|
|
|
|
|
|
|
|
this.use(dbResourceRouter({
|
|
|
|
database: this.database,
|
|
|
|
resourcer: this.resourcer,
|
|
|
|
...(options.resourcer || {}),
|
|
|
|
}));
|
|
|
|
|
|
|
|
this.cli
|
|
|
|
.command('db sync')
|
|
|
|
.option('-f, --force')
|
|
|
|
.action(async (...args) => {
|
|
|
|
const cli = args.pop();
|
|
|
|
await this.database.sync();
|
|
|
|
await this.database.close();
|
|
|
|
});
|
|
|
|
|
|
|
|
this.cli
|
|
|
|
.command('db init')
|
|
|
|
// .option('-f, --force')
|
|
|
|
.action(async (...args) => {
|
|
|
|
const cli = args.pop();
|
|
|
|
await this.emitAsync('db.init');
|
|
|
|
await this.database.close();
|
|
|
|
});
|
|
|
|
|
|
|
|
this.cli
|
|
|
|
.command('start')
|
|
|
|
.option('-p, --port [port]')
|
|
|
|
.action(async (...args) => {
|
|
|
|
const cli = args.pop();
|
|
|
|
console.log(args);
|
|
|
|
const opts = cli.opts();
|
|
|
|
await this.loadPlugins();
|
|
|
|
await this.emitAsync('server.beforeStart');
|
|
|
|
this.listen(opts.port || 3000);
|
|
|
|
console.log(`http://localhost:${opts.port || 3000}/`);
|
|
|
|
});
|
2020-12-18 19:54:53 +08:00
|
|
|
}
|
|
|
|
|
2021-09-05 14:17:45 +08:00
|
|
|
async emitAsync(event: string | symbol, ...args: any[]): Promise<boolean> {
|
|
|
|
// @ts-ignore
|
|
|
|
const events = this._events;
|
|
|
|
let callbacks = events[event];
|
|
|
|
if (!callbacks) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// helper function to reuse as much code as possible
|
|
|
|
const run = (cb) => {
|
|
|
|
switch (args.length) {
|
|
|
|
// fast cases
|
|
|
|
case 0:
|
|
|
|
cb = cb.call(this);
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
cb = cb.call(this, args[0]);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
cb = cb.call(this, args[0], args[1]);
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
cb = cb.call(this, args[0], args[1], args[2]);
|
|
|
|
break;
|
|
|
|
// slower
|
|
|
|
default:
|
|
|
|
cb = cb.apply(this, args);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (
|
|
|
|
cb && (
|
|
|
|
cb instanceof Promise ||
|
|
|
|
typeof cb.then === 'function'
|
|
|
|
)
|
|
|
|
) {
|
|
|
|
return cb;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Promise.resolve(true);
|
|
|
|
};
|
|
|
|
|
|
|
|
if (typeof callbacks === 'function') {
|
|
|
|
await run(callbacks);
|
|
|
|
} else if (typeof callbacks === 'object') {
|
|
|
|
callbacks = callbacks.slice().filter(Boolean);
|
|
|
|
await callbacks.reduce((prev, next) => {
|
|
|
|
return prev.then((res) => {
|
|
|
|
return run(next).then((result) => Promise.resolve(res.concat(result)));
|
|
|
|
});
|
|
|
|
}, Promise.resolve([]));
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-01-13 16:23:15 +08:00
|
|
|
registerPlugin(key: string | object, plugin?: any) {
|
2020-12-18 19:54:53 +08:00
|
|
|
if (typeof key === 'object') {
|
|
|
|
Object.keys(key).forEach((k) => {
|
2021-01-13 16:23:15 +08:00
|
|
|
this.registerPlugin(k, key[k]);
|
2020-12-18 19:54:53 +08:00
|
|
|
});
|
|
|
|
} else {
|
2021-01-13 16:23:15 +08:00
|
|
|
const config = {};
|
|
|
|
if (Array.isArray(plugin)) {
|
|
|
|
const [entry, options = {}] = plugin;
|
|
|
|
Object.assign(config, { entry, options });
|
2020-12-18 09:04:40 +08:00
|
|
|
} else {
|
2021-01-13 16:23:15 +08:00
|
|
|
Object.assign(config, { entry: plugin, options: {} });
|
2020-11-11 18:16:18 +08:00
|
|
|
}
|
2021-01-13 16:23:15 +08:00
|
|
|
this.plugins.set(key, config);
|
2020-11-11 18:16:18 +08:00
|
|
|
}
|
|
|
|
}
|
2020-12-18 09:04:40 +08:00
|
|
|
|
2021-01-13 16:23:15 +08:00
|
|
|
getPluginInstance(key: string) {
|
|
|
|
const plugin = this.plugins.get(key);
|
|
|
|
return plugin && plugin.instance;
|
2020-12-18 19:54:53 +08:00
|
|
|
}
|
|
|
|
|
2021-01-13 16:23:15 +08:00
|
|
|
async loadPlugins() {
|
2021-09-05 14:17:45 +08:00
|
|
|
await this.emitAsync('plugins.beforeLoad');
|
2021-01-20 08:33:47 +08:00
|
|
|
const allPlugins = this.plugins.values();
|
|
|
|
for (const plugin of allPlugins) {
|
2021-01-13 16:23:15 +08:00
|
|
|
plugin.instance = await this.loadPlugin(plugin);
|
|
|
|
}
|
2021-09-05 14:17:45 +08:00
|
|
|
await this.emitAsync('plugins.afterLoad');
|
|
|
|
}
|
|
|
|
|
2021-09-05 23:59:38 +08:00
|
|
|
async start(argv = process.argv) {
|
|
|
|
return this.cli.parseAsync(argv);
|
2020-12-18 19:54:53 +08:00
|
|
|
}
|
|
|
|
|
2021-01-13 16:23:15 +08:00
|
|
|
protected async loadPlugin({ entry, options = {} }: { entry: string | Function, options: any }) {
|
2021-04-01 09:58:30 +08:00
|
|
|
let main: any;
|
|
|
|
if (typeof entry === 'function') {
|
|
|
|
main = entry;
|
|
|
|
} else if (typeof entry === 'string') {
|
|
|
|
const pathname = `${entry}/${__filename.endsWith('.ts') ? 'src' : 'lib'}/server`;
|
|
|
|
main = require(pathname).default;
|
|
|
|
}
|
|
|
|
return main && await main.call(this, options);
|
2020-12-18 09:04:40 +08:00
|
|
|
}
|
2020-11-11 18:16:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
export default Application;
|