2022-02-11 23:59:03 +08:00
|
|
|
import { ACL } from '@nocobase/acl';
|
2021-09-16 00:38:48 +08:00
|
|
|
import { registerActions } from '@nocobase/actions';
|
2022-10-10 15:37:47 +08:00
|
|
|
import { Cache, createCache, ICacheConfig } from '@nocobase/cache';
|
2022-06-17 10:25:59 +08:00
|
|
|
import Database, { Collection, CollectionOptions, IDatabaseOptions } from '@nocobase/database';
|
2022-11-12 17:12:50 +08:00
|
|
|
import { AppLoggerOptions, createAppLogger, Logger } from '@nocobase/logger';
|
2022-01-30 11:11:36 +08:00
|
|
|
import Resourcer, { ResourceOptions } from '@nocobase/resourcer';
|
2022-09-29 21:05:31 +08:00
|
|
|
import { applyMixins, AsyncEmitter, Toposort, ToposortOptions } from '@nocobase/utils';
|
2022-11-12 21:32:50 +08:00
|
|
|
import chalk from 'chalk';
|
2022-08-08 14:42:48 +08:00
|
|
|
import { Command, CommandOptions, ParseOptions } from 'commander';
|
2022-01-30 11:11:36 +08:00
|
|
|
import { Server } from 'http';
|
2021-11-08 19:32:59 +08:00
|
|
|
import { i18n, InitOptions } from 'i18next';
|
2022-09-23 09:22:17 +08:00
|
|
|
import Koa, { DefaultContext as KoaDefaultContext, DefaultState as KoaDefaultState } from 'koa';
|
2022-09-29 21:05:31 +08:00
|
|
|
import compose from 'koa-compose';
|
2022-06-17 10:25:59 +08:00
|
|
|
import semver from 'semver';
|
2022-10-10 15:37:47 +08:00
|
|
|
import { promisify } from 'util';
|
2022-02-11 23:59:03 +08:00
|
|
|
import { createACL } from './acl';
|
2022-04-24 12:38:54 +08:00
|
|
|
import { AppManager } from './app-manager';
|
2022-05-19 00:40:55 +08:00
|
|
|
import { registerCli } from './commands';
|
2022-06-17 10:25:59 +08:00
|
|
|
import { createI18n, createResourcer, registerMiddlewares } from './helper';
|
2022-01-30 11:11:36 +08:00
|
|
|
import { Plugin } from './plugin';
|
2022-04-24 12:38:54 +08:00
|
|
|
import { InstallOptions, PluginManager } from './plugin-manager';
|
2022-10-10 15:37:47 +08:00
|
|
|
|
2022-05-19 00:40:55 +08:00
|
|
|
const packageJson = require('../package.json');
|
|
|
|
|
2022-04-17 10:00:42 +08:00
|
|
|
export type PluginConfiguration = string | [string, any];
|
|
|
|
|
2021-09-09 13:05:33 +08:00
|
|
|
export interface ResourcerOptions {
|
|
|
|
prefix?: string;
|
|
|
|
}
|
2020-11-12 11:48:27 +08:00
|
|
|
|
|
|
|
export interface ApplicationOptions {
|
2022-02-15 00:20:25 +08:00
|
|
|
database?: IDatabaseOptions | Database;
|
2022-10-10 15:37:47 +08:00
|
|
|
cache?: ICacheConfig | ICacheConfig[];
|
2021-09-09 13:05:33 +08:00
|
|
|
resourcer?: ResourcerOptions;
|
|
|
|
bodyParser?: any;
|
|
|
|
cors?: any;
|
|
|
|
dataWrapping?: boolean;
|
2021-10-18 12:49:37 +08:00
|
|
|
registerActions?: boolean;
|
2021-11-08 19:32:59 +08:00
|
|
|
i18n?: i18n | InitOptions;
|
2022-09-19 09:23:01 +08:00
|
|
|
plugins?: PluginConfiguration[];
|
2022-09-29 21:05:31 +08:00
|
|
|
acl?: boolean;
|
2022-11-12 17:12:50 +08:00
|
|
|
logger?: AppLoggerOptions;
|
2022-10-27 13:00:16 +08:00
|
|
|
pmSock?: string;
|
2023-02-14 15:30:58 +08:00
|
|
|
name?: string;
|
2020-11-12 11:48:27 +08:00
|
|
|
}
|
2020-11-11 18:16:18 +08:00
|
|
|
|
2022-09-23 09:22:17 +08:00
|
|
|
export interface DefaultState extends KoaDefaultState {
|
2021-09-16 15:57:21 +08:00
|
|
|
currentUser?: any;
|
2023-01-09 07:35:48 +08:00
|
|
|
|
2021-09-16 15:57:21 +08:00
|
|
|
[key: string]: any;
|
|
|
|
}
|
|
|
|
|
2022-09-23 09:22:17 +08:00
|
|
|
export interface DefaultContext extends KoaDefaultContext {
|
2021-09-16 15:57:21 +08:00
|
|
|
db: Database;
|
2022-10-10 15:37:47 +08:00
|
|
|
cache: Cache;
|
2021-09-16 15:57:21 +08:00
|
|
|
resourcer: Resourcer;
|
2022-09-23 09:22:17 +08:00
|
|
|
i18n: any;
|
2023-01-09 07:35:48 +08:00
|
|
|
|
2021-09-16 15:57:21 +08:00
|
|
|
[key: string]: any;
|
|
|
|
}
|
|
|
|
|
|
|
|
interface MiddlewareOptions {
|
|
|
|
name?: string;
|
|
|
|
resourceName?: string;
|
|
|
|
resourceNames?: string[];
|
|
|
|
insertBefore?: string;
|
|
|
|
insertAfter?: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
interface ActionsOptions {
|
|
|
|
resourceName?: string;
|
|
|
|
resourceNames?: string[];
|
|
|
|
}
|
|
|
|
|
2022-01-30 11:11:36 +08:00
|
|
|
interface ListenOptions {
|
|
|
|
port?: number | undefined;
|
|
|
|
host?: string | undefined;
|
|
|
|
backlog?: number | undefined;
|
|
|
|
path?: string | undefined;
|
|
|
|
exclusive?: boolean | undefined;
|
|
|
|
readableAll?: boolean | undefined;
|
|
|
|
writableAll?: boolean | undefined;
|
|
|
|
/**
|
|
|
|
* @default false
|
|
|
|
*/
|
|
|
|
ipv6Only?: boolean | undefined;
|
|
|
|
signal?: AbortSignal | undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
interface StartOptions {
|
2022-02-07 01:14:00 +08:00
|
|
|
cliArgs?: any[];
|
2022-11-04 15:38:08 +08:00
|
|
|
dbSync?: boolean;
|
2022-01-30 11:11:36 +08:00
|
|
|
listen?: ListenOptions;
|
|
|
|
}
|
|
|
|
|
2022-06-17 10:25:59 +08:00
|
|
|
export class ApplicationVersion {
|
|
|
|
protected app: Application;
|
|
|
|
protected collection: Collection;
|
|
|
|
|
|
|
|
constructor(app: Application) {
|
|
|
|
this.app = app;
|
|
|
|
if (!app.db.hasCollection('applicationVersion')) {
|
|
|
|
app.db.collection({
|
|
|
|
name: 'applicationVersion',
|
2023-03-10 19:16:00 +08:00
|
|
|
namespace: 'core.applicationVersion',
|
2023-02-13 09:57:03 +08:00
|
|
|
duplicator: 'required',
|
2022-06-17 10:25:59 +08:00
|
|
|
timestamps: false,
|
|
|
|
fields: [{ name: 'value', type: 'string' }],
|
|
|
|
});
|
|
|
|
}
|
|
|
|
this.collection = this.app.db.getCollection('applicationVersion');
|
|
|
|
}
|
|
|
|
|
|
|
|
async get() {
|
2022-06-18 00:18:12 +08:00
|
|
|
if (await this.app.db.collectionExistsInDb('applicationVersion')) {
|
2022-06-17 10:25:59 +08:00
|
|
|
const model = await this.collection.model.findOne();
|
2022-11-10 21:28:33 +08:00
|
|
|
if (!model) {
|
|
|
|
return null;
|
|
|
|
}
|
2022-06-17 10:25:59 +08:00
|
|
|
return model.get('value') as any;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
async update() {
|
|
|
|
await this.collection.sync();
|
|
|
|
await this.collection.model.destroy({
|
|
|
|
truncate: true,
|
|
|
|
});
|
2022-11-12 17:12:50 +08:00
|
|
|
|
2022-06-17 10:25:59 +08:00
|
|
|
await this.collection.model.create({
|
|
|
|
value: this.app.getVersion(),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
async satisfies(range: string) {
|
2022-06-18 00:18:12 +08:00
|
|
|
if (await this.app.db.collectionExistsInDb('applicationVersion')) {
|
2022-07-10 00:41:36 +08:00
|
|
|
const model: any = await this.collection.model.findOne();
|
|
|
|
const version = model?.value as any;
|
|
|
|
if (!version) {
|
|
|
|
return true;
|
|
|
|
}
|
2022-09-02 11:44:22 +08:00
|
|
|
return semver.satisfies(version, range, { includePrerelease: true });
|
2022-06-17 10:25:59 +08:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-30 11:11:36 +08:00
|
|
|
export class Application<StateT = DefaultState, ContextT = DefaultContext> extends Koa implements AsyncEmitter {
|
2022-09-18 14:10:01 +08:00
|
|
|
protected _db: Database;
|
2022-11-12 17:12:50 +08:00
|
|
|
protected _logger: Logger;
|
2021-01-13 16:23:15 +08:00
|
|
|
|
2022-09-18 14:10:01 +08:00
|
|
|
protected _resourcer: Resourcer;
|
2020-11-11 18:16:18 +08:00
|
|
|
|
2022-10-10 15:37:47 +08:00
|
|
|
protected _cache: Cache;
|
|
|
|
|
2022-09-18 14:10:01 +08:00
|
|
|
protected _cli: Command;
|
2021-09-05 23:59:38 +08:00
|
|
|
|
2022-09-18 14:10:01 +08:00
|
|
|
protected _i18n: i18n;
|
2021-11-08 19:32:59 +08:00
|
|
|
|
2022-09-18 14:10:01 +08:00
|
|
|
protected _pm: PluginManager;
|
2022-01-30 11:11:36 +08:00
|
|
|
|
2022-09-18 14:10:01 +08:00
|
|
|
protected _acl: ACL;
|
2022-02-11 23:59:03 +08:00
|
|
|
|
2022-09-18 14:10:01 +08:00
|
|
|
protected _appManager: AppManager;
|
2022-03-28 22:01:10 +08:00
|
|
|
|
2022-09-18 14:10:01 +08:00
|
|
|
protected _version: ApplicationVersion;
|
2022-06-17 10:25:59 +08:00
|
|
|
|
2021-09-14 11:09:26 +08:00
|
|
|
protected plugins = new Map<string, Plugin>();
|
2020-11-12 11:48:27 +08:00
|
|
|
|
2022-01-30 11:11:36 +08:00
|
|
|
public listenServer: Server;
|
|
|
|
|
2022-09-29 21:05:31 +08:00
|
|
|
declare middleware: any;
|
|
|
|
|
2023-04-25 13:12:14 +08:00
|
|
|
stopped = false;
|
2023-03-06 21:19:49 +08:00
|
|
|
|
2022-03-28 22:01:10 +08:00
|
|
|
constructor(public options: ApplicationOptions) {
|
2021-01-13 16:23:15 +08:00
|
|
|
super();
|
2022-09-18 14:10:01 +08:00
|
|
|
this.init();
|
|
|
|
}
|
|
|
|
|
|
|
|
get db() {
|
|
|
|
return this._db;
|
|
|
|
}
|
|
|
|
|
2022-10-10 15:37:47 +08:00
|
|
|
get cache() {
|
|
|
|
return this._cache;
|
|
|
|
}
|
|
|
|
|
2022-09-18 14:10:01 +08:00
|
|
|
get resourcer() {
|
|
|
|
return this._resourcer;
|
|
|
|
}
|
|
|
|
|
|
|
|
get cli() {
|
|
|
|
return this._cli;
|
|
|
|
}
|
|
|
|
|
|
|
|
get acl() {
|
|
|
|
return this._acl;
|
|
|
|
}
|
|
|
|
|
|
|
|
get i18n() {
|
|
|
|
return this._i18n;
|
|
|
|
}
|
|
|
|
|
|
|
|
get pm() {
|
|
|
|
return this._pm;
|
|
|
|
}
|
|
|
|
|
|
|
|
get version() {
|
|
|
|
return this._version;
|
|
|
|
}
|
2021-09-09 13:05:33 +08:00
|
|
|
|
2022-09-18 14:10:01 +08:00
|
|
|
get appManager() {
|
|
|
|
return this._appManager;
|
|
|
|
}
|
2021-09-05 23:59:38 +08:00
|
|
|
|
2022-11-12 17:12:50 +08:00
|
|
|
get logger() {
|
|
|
|
return this._logger;
|
|
|
|
}
|
|
|
|
|
|
|
|
get log() {
|
|
|
|
return this._logger;
|
|
|
|
}
|
|
|
|
|
2023-02-14 15:30:58 +08:00
|
|
|
get name() {
|
|
|
|
return this.options.name || 'main';
|
|
|
|
}
|
|
|
|
|
2022-09-18 14:10:01 +08:00
|
|
|
protected init() {
|
|
|
|
const options = this.options;
|
2023-03-10 19:16:00 +08:00
|
|
|
|
2022-11-12 17:12:50 +08:00
|
|
|
const logger = createAppLogger(options.logger);
|
|
|
|
this._logger = logger.instance;
|
2023-03-10 19:16:00 +08:00
|
|
|
|
2022-09-18 14:10:01 +08:00
|
|
|
// @ts-ignore
|
|
|
|
this._events = [];
|
|
|
|
// @ts-ignore
|
|
|
|
this._eventsCount = [];
|
2023-03-10 19:16:00 +08:00
|
|
|
|
2022-10-28 15:09:14 +08:00
|
|
|
this.removeAllListeners();
|
2023-03-10 19:16:00 +08:00
|
|
|
|
2022-09-29 21:05:31 +08:00
|
|
|
this.middleware = new Toposort<any>();
|
2022-09-18 14:10:01 +08:00
|
|
|
this.plugins = new Map<string, Plugin>();
|
|
|
|
this._acl = createACL();
|
2022-10-31 22:45:39 +08:00
|
|
|
|
2022-11-12 17:12:50 +08:00
|
|
|
this.use(logger.middleware, { tag: 'logger' });
|
|
|
|
|
2022-10-28 15:09:14 +08:00
|
|
|
if (this._db) {
|
|
|
|
// MaxListenersExceededWarning
|
|
|
|
this._db.removeAllListeners();
|
|
|
|
}
|
2022-10-31 22:45:39 +08:00
|
|
|
|
2022-09-18 14:10:01 +08:00
|
|
|
this._db = this.createDatabase(options);
|
2022-10-31 22:45:39 +08:00
|
|
|
|
2022-09-18 14:10:01 +08:00
|
|
|
this._resourcer = createResourcer(options);
|
|
|
|
this._cli = new Command('nocobase').usage('[command] [options]');
|
|
|
|
this._i18n = createI18n(options);
|
2022-10-10 15:37:47 +08:00
|
|
|
this._cache = createCache(options.cache);
|
2022-09-23 09:22:17 +08:00
|
|
|
this.context.db = this._db;
|
2022-11-12 17:12:50 +08:00
|
|
|
this.context.logger = this._logger;
|
2022-09-23 09:22:17 +08:00
|
|
|
this.context.resourcer = this._resourcer;
|
2022-10-10 15:37:47 +08:00
|
|
|
this.context.cache = this._cache;
|
2022-09-18 14:10:01 +08:00
|
|
|
|
2022-10-28 15:09:14 +08:00
|
|
|
if (this._pm) {
|
|
|
|
this._pm = this._pm.clone();
|
|
|
|
} else {
|
|
|
|
this._pm = new PluginManager({
|
|
|
|
app: this,
|
|
|
|
plugins: options.plugins,
|
|
|
|
});
|
|
|
|
}
|
2022-01-30 11:11:36 +08:00
|
|
|
|
2023-03-14 13:10:15 +08:00
|
|
|
if (this._appManager) {
|
|
|
|
this._appManager.bindMainApplication(this);
|
|
|
|
} else {
|
|
|
|
this._appManager = new AppManager(this);
|
|
|
|
}
|
2022-03-28 22:01:10 +08:00
|
|
|
|
2022-09-29 21:05:31 +08:00
|
|
|
if (this.options.acl !== false) {
|
|
|
|
this._resourcer.use(this._acl.middleware(), { tag: 'acl', after: ['parseToken'] });
|
|
|
|
}
|
|
|
|
|
2021-10-18 12:49:37 +08:00
|
|
|
registerMiddlewares(this, options);
|
2022-03-11 10:10:57 +08:00
|
|
|
|
2021-10-18 12:49:37 +08:00
|
|
|
if (options.registerActions !== false) {
|
|
|
|
registerActions(this);
|
2021-09-09 13:05:33 +08:00
|
|
|
}
|
2022-04-17 10:00:42 +08:00
|
|
|
|
2022-05-19 00:40:55 +08:00
|
|
|
registerCli(this);
|
2022-06-17 10:25:59 +08:00
|
|
|
|
2022-09-18 14:10:01 +08:00
|
|
|
this._version = new ApplicationVersion(this);
|
2022-06-17 10:25:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
private createDatabase(options: ApplicationOptions) {
|
2023-03-05 14:45:56 +08:00
|
|
|
const db = new Database({
|
2022-10-31 22:45:39 +08:00
|
|
|
...(options.database instanceof Database ? options.database.options : options.database),
|
|
|
|
migrator: {
|
|
|
|
context: { app: this },
|
|
|
|
},
|
|
|
|
});
|
2023-03-05 14:45:56 +08:00
|
|
|
|
|
|
|
db.setLogger(this._logger);
|
2023-03-10 19:16:00 +08:00
|
|
|
|
2023-03-05 14:45:56 +08:00
|
|
|
return db;
|
2022-05-19 00:40:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
getVersion() {
|
|
|
|
return packageJson.version;
|
2020-12-18 19:54:53 +08:00
|
|
|
}
|
|
|
|
|
2022-10-27 13:00:16 +08:00
|
|
|
plugin<O = any>(pluginClass: any, options?: O): Plugin {
|
|
|
|
return this.pm.addStatic(pluginClass, options);
|
2022-01-30 11:11:36 +08:00
|
|
|
}
|
|
|
|
|
2022-09-29 21:05:31 +08:00
|
|
|
// @ts-ignore
|
2021-09-16 15:57:21 +08:00
|
|
|
use<NewStateT = {}, NewContextT = {}>(
|
|
|
|
middleware: Koa.Middleware<StateT & NewStateT, ContextT & NewContextT>,
|
2022-09-29 21:05:31 +08:00
|
|
|
options?: ToposortOptions,
|
2021-09-23 00:16:04 +08:00
|
|
|
) {
|
2022-09-29 21:05:31 +08:00
|
|
|
this.middleware.add(middleware, options);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
callback() {
|
|
|
|
const fn = compose(this.middleware.nodes);
|
|
|
|
|
|
|
|
if (!this.listenerCount('error')) this.on('error', this.onerror);
|
|
|
|
|
|
|
|
const handleRequest = (req, res) => {
|
|
|
|
const ctx = this.createContext(req, res);
|
2023-03-07 22:10:51 +08:00
|
|
|
|
2022-09-29 21:05:31 +08:00
|
|
|
// @ts-ignore
|
|
|
|
return this.handleRequest(ctx, fn);
|
|
|
|
};
|
|
|
|
|
|
|
|
return handleRequest;
|
2021-09-16 15:57:21 +08:00
|
|
|
}
|
|
|
|
|
2021-12-06 21:23:34 +08:00
|
|
|
collection(options: CollectionOptions) {
|
|
|
|
return this.db.collection(options);
|
2021-09-14 11:09:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
resource(options: ResourceOptions) {
|
|
|
|
return this.resourcer.define(options);
|
|
|
|
}
|
|
|
|
|
2021-09-16 15:57:21 +08:00
|
|
|
actions(handlers: any, options?: ActionsOptions) {
|
2021-09-16 00:38:48 +08:00
|
|
|
return this.resourcer.registerActions(handlers);
|
|
|
|
}
|
|
|
|
|
2022-02-07 21:54:07 +08:00
|
|
|
command(name: string, desc?: string, opts?: CommandOptions): Command {
|
2022-05-19 00:40:55 +08:00
|
|
|
return this.cli.command(name, desc, opts).allowUnknownOption();
|
2021-09-14 11:09:26 +08:00
|
|
|
}
|
|
|
|
|
2021-10-18 12:49:37 +08:00
|
|
|
findCommand(name: string): Command {
|
2022-02-10 18:38:05 +08:00
|
|
|
return (<any>this.cli)._findCommand(name);
|
2021-10-18 12:49:37 +08:00
|
|
|
}
|
|
|
|
|
2022-10-27 13:00:16 +08:00
|
|
|
async load(options?: any) {
|
|
|
|
if (options?.reload) {
|
2023-03-10 19:16:00 +08:00
|
|
|
console.log(`Reload the ${this.name} application configuration`);
|
2022-10-31 22:45:39 +08:00
|
|
|
const oldDb = this._db;
|
2022-10-27 13:00:16 +08:00
|
|
|
this.init();
|
2022-10-31 22:45:39 +08:00
|
|
|
await oldDb.close();
|
2022-10-27 13:00:16 +08:00
|
|
|
}
|
2022-10-31 22:45:39 +08:00
|
|
|
|
2022-10-27 13:00:16 +08:00
|
|
|
await this.emitAsync('beforeLoad', this, options);
|
|
|
|
await this.pm.load(options);
|
|
|
|
await this.emitAsync('afterLoad', this, options);
|
2021-09-14 11:09:26 +08:00
|
|
|
}
|
|
|
|
|
2022-10-27 13:00:16 +08:00
|
|
|
async reload(options?: any) {
|
|
|
|
await this.load({
|
|
|
|
...options,
|
|
|
|
reload: true,
|
|
|
|
});
|
2023-03-10 19:16:00 +08:00
|
|
|
|
|
|
|
await this.emitAsync('afterReload', this, options);
|
2022-09-18 14:10:01 +08:00
|
|
|
}
|
|
|
|
|
2022-01-24 14:10:35 +08:00
|
|
|
getPlugin<P extends Plugin>(name: string) {
|
2022-01-30 11:11:36 +08:00
|
|
|
return this.pm.get(name) as P;
|
2022-01-24 14:10:35 +08:00
|
|
|
}
|
|
|
|
|
2022-01-30 11:11:36 +08:00
|
|
|
async parse(argv = process.argv) {
|
2022-08-08 14:42:48 +08:00
|
|
|
return this.runAsCLI(argv);
|
|
|
|
}
|
|
|
|
|
2022-09-18 14:10:01 +08:00
|
|
|
async runAsCLI(argv = process.argv, options?: ParseOptions) {
|
2022-12-02 22:43:51 +08:00
|
|
|
try {
|
|
|
|
await this.db.auth({ retry: 30 });
|
|
|
|
} catch (error) {
|
|
|
|
console.log(chalk.red(error.message));
|
|
|
|
process.exit(1);
|
|
|
|
}
|
2023-02-23 20:14:50 +08:00
|
|
|
|
2022-11-12 21:32:50 +08:00
|
|
|
await this.dbVersionCheck({ exit: true });
|
2023-01-29 15:46:53 +08:00
|
|
|
|
2023-02-23 20:14:50 +08:00
|
|
|
await this.db.prepare();
|
|
|
|
|
2022-12-17 10:35:10 +08:00
|
|
|
if (argv?.[2] !== 'upgrade') {
|
|
|
|
await this.load({
|
|
|
|
method: argv?.[2],
|
|
|
|
});
|
|
|
|
}
|
2023-01-29 15:46:53 +08:00
|
|
|
|
2022-08-08 14:42:48 +08:00
|
|
|
return this.cli.parseAsync(argv, options);
|
2022-01-30 11:11:36 +08:00
|
|
|
}
|
|
|
|
|
2022-05-13 11:18:36 +08:00
|
|
|
async start(options: StartOptions = {}) {
|
2022-01-30 11:11:36 +08:00
|
|
|
if (this.db.closed()) {
|
|
|
|
await this.db.reconnect();
|
2021-12-06 21:23:34 +08:00
|
|
|
}
|
2022-01-30 11:11:36 +08:00
|
|
|
|
2022-11-04 15:38:08 +08:00
|
|
|
if (options.dbSync) {
|
|
|
|
console.log('db sync...');
|
|
|
|
await this.db.sync();
|
|
|
|
}
|
|
|
|
|
2022-01-30 11:11:36 +08:00
|
|
|
await this.emitAsync('beforeStart', this, options);
|
|
|
|
|
|
|
|
if (options?.listen?.port) {
|
2022-10-27 13:00:16 +08:00
|
|
|
const pmServer = await this.pm.listen();
|
2023-03-10 19:16:00 +08:00
|
|
|
|
2022-01-30 11:11:36 +08:00
|
|
|
const listen = () =>
|
2022-05-13 11:20:30 +08:00
|
|
|
new Promise((resolve, reject) => {
|
2022-01-30 11:11:36 +08:00
|
|
|
const Server = this.listen(options?.listen, () => {
|
|
|
|
resolve(Server);
|
|
|
|
});
|
2022-05-13 11:20:30 +08:00
|
|
|
|
|
|
|
Server.on('error', (err) => {
|
|
|
|
reject(err);
|
|
|
|
});
|
2022-10-27 13:00:16 +08:00
|
|
|
|
|
|
|
Server.on('close', () => {
|
|
|
|
pmServer.close();
|
|
|
|
});
|
2021-12-06 21:23:34 +08:00
|
|
|
});
|
2022-01-30 11:11:36 +08:00
|
|
|
|
2022-05-13 11:20:30 +08:00
|
|
|
try {
|
|
|
|
//@ts-ignore
|
|
|
|
this.listenServer = await listen();
|
|
|
|
} catch (e) {
|
|
|
|
console.error(e);
|
|
|
|
process.exit(1);
|
|
|
|
}
|
2021-12-06 21:23:34 +08:00
|
|
|
}
|
|
|
|
|
2022-01-30 11:11:36 +08:00
|
|
|
await this.emitAsync('afterStart', this, options);
|
2023-03-06 21:19:49 +08:00
|
|
|
this.stopped = false;
|
2021-12-06 21:23:34 +08:00
|
|
|
}
|
|
|
|
|
2022-03-28 22:01:10 +08:00
|
|
|
listen(...args): Server {
|
|
|
|
return this.appManager.listen(...args);
|
|
|
|
}
|
|
|
|
|
2022-05-13 11:18:36 +08:00
|
|
|
async stop(options: any = {}) {
|
2023-03-06 21:19:49 +08:00
|
|
|
if (this.stopped) {
|
|
|
|
this.log.warn(`Application ${this.name} already stopped`);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-02-07 01:14:00 +08:00
|
|
|
await this.emitAsync('beforeStop', this, options);
|
2022-01-30 11:11:36 +08:00
|
|
|
|
2023-03-10 19:16:00 +08:00
|
|
|
// close http server
|
|
|
|
if (this.listenServer) {
|
|
|
|
await promisify(this.listenServer.close).call(this.listenServer);
|
|
|
|
this.listenServer = null;
|
|
|
|
}
|
|
|
|
|
2022-03-28 22:01:10 +08:00
|
|
|
try {
|
|
|
|
// close database connection
|
|
|
|
// silent if database already closed
|
2022-10-27 13:00:16 +08:00
|
|
|
if (!this.db.closed()) {
|
|
|
|
await this.db.close();
|
|
|
|
}
|
2022-03-28 22:01:10 +08:00
|
|
|
} catch (e) {
|
|
|
|
console.log(e);
|
|
|
|
}
|
2022-01-30 11:11:36 +08:00
|
|
|
|
2022-02-07 01:14:00 +08:00
|
|
|
await this.emitAsync('afterStop', this, options);
|
2023-03-06 21:19:49 +08:00
|
|
|
this.stopped = true;
|
2023-03-10 19:16:00 +08:00
|
|
|
console.log(`${this.name} is stopped`);
|
2020-12-18 19:54:53 +08:00
|
|
|
}
|
|
|
|
|
2022-05-13 11:18:36 +08:00
|
|
|
async destroy(options: any = {}) {
|
2022-02-07 01:14:00 +08:00
|
|
|
await this.emitAsync('beforeDestroy', this, options);
|
|
|
|
await this.stop(options);
|
|
|
|
await this.emitAsync('afterDestroy', this, options);
|
2022-01-30 11:11:36 +08:00
|
|
|
}
|
|
|
|
|
2022-11-12 21:32:50 +08:00
|
|
|
async dbVersionCheck(options?: { exit?: boolean }) {
|
2022-07-04 13:41:07 +08:00
|
|
|
const r = await this.db.version.satisfies({
|
2022-07-28 09:06:03 +08:00
|
|
|
mysql: '>=8.0.17',
|
2022-07-04 13:41:07 +08:00
|
|
|
sqlite: '3.x',
|
|
|
|
postgres: '>=10',
|
|
|
|
});
|
|
|
|
|
|
|
|
if (!r) {
|
2022-11-12 21:32:50 +08:00
|
|
|
console.log(chalk.red('The database only supports MySQL 8.0.17 and above, SQLite 3.x and PostgreSQL 10+'));
|
|
|
|
if (options?.exit) {
|
|
|
|
process.exit();
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.db.inDialect('mysql')) {
|
|
|
|
const result = await this.db.sequelize.query(`SHOW VARIABLES LIKE 'lower_case_table_names'`, { plain: true });
|
2023-02-25 00:54:45 +08:00
|
|
|
if (result?.Value === '1' && !this.db.options.underscored) {
|
2023-02-26 08:04:29 +08:00
|
|
|
console.log(
|
|
|
|
`Your database lower_case_table_names=1, please add ${chalk.yellow('DB_UNDERSCORED=true')} to the .env file`,
|
|
|
|
);
|
2022-11-12 21:32:50 +08:00
|
|
|
if (options?.exit) {
|
|
|
|
process.exit();
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2022-07-04 13:41:07 +08:00
|
|
|
}
|
|
|
|
|
2022-11-12 21:32:50 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-12-05 22:19:22 +08:00
|
|
|
async isInstalled() {
|
|
|
|
return (
|
|
|
|
(await this.db.collectionExistsInDb('applicationVersion')) || (await this.db.collectionExistsInDb('collections'))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-11-12 21:32:50 +08:00
|
|
|
async install(options: InstallOptions = {}) {
|
2022-11-04 00:32:25 +08:00
|
|
|
console.log('Database dialect: ' + this.db.sequelize.getDialect());
|
|
|
|
|
|
|
|
if (options?.clean || options?.sync?.force) {
|
|
|
|
console.log('Truncate database and reload app configuration');
|
|
|
|
await this.db.clean({ drop: true });
|
2022-10-27 13:00:16 +08:00
|
|
|
await this.reload({ method: 'install' });
|
2022-01-30 11:11:36 +08:00
|
|
|
}
|
2022-02-28 21:49:50 +08:00
|
|
|
|
2022-09-18 14:10:01 +08:00
|
|
|
await this.emitAsync('beforeInstall', this, options);
|
2022-11-04 00:32:25 +08:00
|
|
|
await this.db.sync();
|
2022-02-28 21:49:50 +08:00
|
|
|
await this.pm.install(options);
|
2022-06-17 10:25:59 +08:00
|
|
|
await this.version.update();
|
2022-01-30 11:11:36 +08:00
|
|
|
await this.emitAsync('afterInstall', this, options);
|
2021-09-09 13:05:33 +08:00
|
|
|
}
|
2022-01-30 11:11:36 +08:00
|
|
|
|
2022-06-17 10:25:59 +08:00
|
|
|
async upgrade(options: any = {}) {
|
2022-07-10 00:41:36 +08:00
|
|
|
await this.emitAsync('beforeUpgrade', this, options);
|
2022-06-17 10:25:59 +08:00
|
|
|
const force = false;
|
|
|
|
await this.db.migrator.up();
|
|
|
|
await this.db.sync({
|
|
|
|
force,
|
|
|
|
alter: {
|
|
|
|
drop: force,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
await this.version.update();
|
2022-07-10 00:41:36 +08:00
|
|
|
await this.emitAsync('afterUpgrade', this, options);
|
2022-06-17 10:25:59 +08:00
|
|
|
}
|
|
|
|
|
2022-05-19 00:40:55 +08:00
|
|
|
declare emitAsync: (event: string | symbol, ...args: any[]) => Promise<boolean>;
|
2023-02-14 15:30:58 +08:00
|
|
|
|
|
|
|
toJSON() {
|
|
|
|
return {
|
|
|
|
appName: this.name,
|
|
|
|
};
|
|
|
|
}
|
2020-11-11 18:16:18 +08:00
|
|
|
}
|
|
|
|
|
2022-01-30 11:11:36 +08:00
|
|
|
applyMixins(Application, [AsyncEmitter]);
|
|
|
|
|
2020-11-11 18:16:18 +08:00
|
|
|
export default Application;
|