diff --git a/packages/core/server/src/__tests__/app-command.test.ts b/packages/core/server/src/__tests__/app-command.test.ts new file mode 100644 index 000000000..a28f7cb94 --- /dev/null +++ b/packages/core/server/src/__tests__/app-command.test.ts @@ -0,0 +1,43 @@ +import { DataTypes, mockDatabase } from '@nocobase/database'; +import Application, { ApplicationOptions } from '../application'; + +const mockServer = (options?: ApplicationOptions) => { + return new Application({ + database: mockDatabase(), + acl: false, + ...options, + }); +}; + +describe('app command', () => { + let app: Application; + + afterEach(async () => { + if (app) { + await app.destroy(); + } + }); + + beforeEach(async () => { + app = mockServer(); + await app.load(); + await app.install(); + }); + + it('should test command should handle by IPC Server or not', () => { + app.command('testaa').ipc(); + app.command('testbb'); + + expect(app.cli.parseHandleByIPCServer(['node', 'cli', 'nocobase', 'testaa'])).toBeTruthy(); + expect(app.cli.parseHandleByIPCServer(['node', 'cli', 'nocobase', 'testbb'])).toBeFalsy(); + }); + + it('should test sub command should handle by IPC Server or not', () => { + const subParent = app.command('subparent'); + subParent.command('testaa').ipc(); + subParent.command('testbb'); + + expect(app.cli.parseHandleByIPCServer(['node', 'cli', 'nocobase', 'subparent', 'testaa'])).toBeTruthy(); + expect(app.cli.parseHandleByIPCServer(['node', 'cli', 'nocobase', 'subparent', 'testbb'])).toBeFalsy(); + }); +}); diff --git a/packages/core/server/src/app-command.ts b/packages/core/server/src/app-command.ts new file mode 100644 index 000000000..490602205 --- /dev/null +++ b/packages/core/server/src/app-command.ts @@ -0,0 +1,41 @@ +import { Command } from 'commander'; + +export class AppCommand extends Command { + private _handleByIPCServer = false; + + ipc() { + this._handleByIPCServer = true; + return this; + } + + isHandleByIPCServer() { + return this._handleByIPCServer; + } + + createCommand(name?: string): AppCommand { + return new AppCommand(name); + } + + parseHandleByIPCServer(argv, parseOptions?): Boolean { + //@ts-ignore + const userArgs = this._prepareUserArgs(argv, parseOptions); + + if (userArgs[0] === 'nocobase') { + userArgs.shift(); + } + + let lastCommand = this; + + for (const arg of userArgs) { + // @ts-ignore + const subCommand = lastCommand._findCommand(arg); + if (subCommand) { + lastCommand = subCommand; + } else { + break; + } + } + + return lastCommand && lastCommand.isHandleByIPCServer(); + } +} diff --git a/packages/core/server/src/application.ts b/packages/core/server/src/application.ts index d85493583..e1a17dc8f 100644 --- a/packages/core/server/src/application.ts +++ b/packages/core/server/src/application.ts @@ -22,6 +22,7 @@ import { ApplicationVersion } from './helpers/application-version'; import { Locale } from './locale'; import { Plugin } from './plugin'; import { InstallOptions, PluginManager } from './plugin-manager'; +import { AppCommand } from './app-command'; const packageJson = require('../package.json'); @@ -163,7 +164,7 @@ export class Application exten return this._cache; } - protected _cli: Command; + protected _cli: AppCommand; get cli() { return this._cli; @@ -286,7 +287,7 @@ export class Application exten return this.resourcer.registerActions(handlers); } - command(name: string, desc?: string, opts?: CommandOptions): Command { + command(name: string, desc?: string, opts?: CommandOptions): AppCommand { return this.cli.command(name, desc, opts).allowUnknownOption(); } @@ -363,7 +364,7 @@ export class Application exten } createCli() { - const command = new Command('nocobase') + const command = new AppCommand('nocobase') .usage('[command] [options]') .hook('preAction', async (_, actionCommand) => { this._actionCommand = actionCommand; diff --git a/packages/core/server/src/commands/install.ts b/packages/core/server/src/commands/install.ts index 9b672bcfa..e98ba61fe 100644 --- a/packages/core/server/src/commands/install.ts +++ b/packages/core/server/src/commands/install.ts @@ -3,6 +3,7 @@ import Application from '../application'; export default (app: Application) => { app .command('install') + .ipc() .option('-f, --force') .option('-c, --clean') .action(async (...cliArgs) => { diff --git a/packages/core/server/src/commands/pm.ts b/packages/core/server/src/commands/pm.ts index 4965bae6c..25e942b56 100644 --- a/packages/core/server/src/commands/pm.ts +++ b/packages/core/server/src/commands/pm.ts @@ -6,18 +6,19 @@ export default (app: Application) => { const pm = app.command('pm'); pm.command('create') + .ipc() .arguments('plugin') .action(async (plugin) => { await app.pm.create(plugin); }); pm.command('add') + .ipc() .argument('') .option('--registry [registry]') .option('--auth-token [authToken]') .option('--version [version]') .action(async (name, options, cli) => { - console.log('pm.add', name, options); try { await app.pm.addViaCLI(name, _.cloneDeep(options)); } catch (error) { @@ -26,6 +27,7 @@ export default (app: Application) => { }); pm.command('update') + .ipc() .argument('') .option('--path [path]') .option('--url [url]') @@ -44,6 +46,7 @@ export default (app: Application) => { }); pm.command('enable') + .ipc() .arguments('') .action(async (plugins) => { try { @@ -54,6 +57,7 @@ export default (app: Application) => { }); pm.command('disable') + .ipc() .arguments('') .action(async (plugins) => { try { @@ -64,6 +68,7 @@ export default (app: Application) => { }); pm.command('remove') + .ipc() .arguments('') .action(async (plugins) => { await app.pm.remove(plugins); diff --git a/packages/core/server/src/commands/restart.ts b/packages/core/server/src/commands/restart.ts index f92d07735..c558038b5 100644 --- a/packages/core/server/src/commands/restart.ts +++ b/packages/core/server/src/commands/restart.ts @@ -1,9 +1,12 @@ import Application from '../application'; export default (app: Application) => { - app.command('restart').action(async (...cliArgs) => { - await app.restart({ - cliArgs, + app + .command('restart') + .ipc() + .action(async (...cliArgs) => { + await app.restart({ + cliArgs, + }); }); - }); }; diff --git a/packages/core/server/src/commands/stop.ts b/packages/core/server/src/commands/stop.ts index 58ae687af..476aef8c3 100644 --- a/packages/core/server/src/commands/stop.ts +++ b/packages/core/server/src/commands/stop.ts @@ -1,9 +1,12 @@ import Application from '../application'; export default (app: Application) => { - app.command('stop').action(async (...cliArgs) => { - await app.stop({ - cliArgs, + app + .command('stop') + .ipc() + .action(async (...cliArgs) => { + await app.stop({ + cliArgs, + }); }); - }); }; diff --git a/packages/core/server/src/commands/upgrade.ts b/packages/core/server/src/commands/upgrade.ts index 056ff3b04..0389f12f1 100644 --- a/packages/core/server/src/commands/upgrade.ts +++ b/packages/core/server/src/commands/upgrade.ts @@ -5,10 +5,13 @@ import Application from '../application'; * TODO */ export default (app: Application) => { - app.command('upgrade').action(async (...cliArgs) => { - const [opts] = cliArgs; - console.log('upgrading...'); - await app.upgrade(); - console.log(chalk.green(`✨ NocoBase has been upgraded to v${app.getVersion()}`)); - }); + app + .command('upgrade') + .ipc() + .action(async (...cliArgs) => { + const [opts] = cliArgs; + console.log('upgrading...'); + await app.upgrade(); + console.log(chalk.green(`✨ NocoBase has been upgraded to v${app.getVersion()}`)); + }); }; diff --git a/packages/core/server/src/gateway/index.ts b/packages/core/server/src/gateway/index.ts index cbc804410..9f06ab520 100644 --- a/packages/core/server/src/gateway/index.ts +++ b/packages/core/server/src/gateway/index.ts @@ -228,10 +228,12 @@ export class Gateway extends EventEmitter { ipcClient = await this.tryConnectToIPCServer(); if (ipcClient) { - await ipcClient.write({ type: 'passCliArgv', payload: { argv: process.argv } }); - // should wait for server response + const response: any = await ipcClient.write({ type: 'passCliArgv', payload: { argv: process.argv } }); ipcClient.close(); - return; + + if (response.type !== 'error' || response.payload.message !== 'Not handle by ipc server') { + return; + } } } diff --git a/packages/core/server/src/gateway/ipc-socket-server.ts b/packages/core/server/src/gateway/ipc-socket-server.ts index 98e5a22bb..07e955605 100644 --- a/packages/core/server/src/gateway/ipc-socket-server.ts +++ b/packages/core/server/src/gateway/ipc-socket-server.ts @@ -66,6 +66,14 @@ export class IPCSocketServer { const argv = payload.argv; const mainApp = await AppSupervisor.getInstance().getApp('main'); + const cli = mainApp.cli; + if ( + !cli.parseHandleByIPCServer(argv, { + from: 'node', + }) + ) { + throw new Error('Not handle by ipc server'); + } return mainApp.runAsCLI(argv, { from: 'node',