@nocobase/cli 2.1.0-beta.15 → 2.1.0-beta.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/LICENSE.txt +107 -0
  2. package/README.md +307 -63
  3. package/README.zh-CN.md +332 -0
  4. package/bin/run.cmd +3 -0
  5. package/bin/run.js +114 -0
  6. package/dist/commands/api/resource/create.js +15 -0
  7. package/dist/commands/api/resource/destroy.js +15 -0
  8. package/dist/commands/api/resource/get.js +15 -0
  9. package/dist/commands/api/resource/index.js +20 -0
  10. package/dist/commands/api/resource/list.js +16 -0
  11. package/dist/commands/api/resource/query.js +15 -0
  12. package/dist/commands/api/resource/update.js +15 -0
  13. package/dist/commands/build.js +57 -0
  14. package/dist/commands/db/logs.js +85 -0
  15. package/dist/commands/db/ps.js +60 -0
  16. package/dist/commands/db/shared.js +81 -0
  17. package/dist/commands/db/start.js +70 -0
  18. package/dist/commands/db/stop.js +70 -0
  19. package/dist/commands/dev.js +149 -0
  20. package/dist/commands/down.js +193 -0
  21. package/dist/commands/download.js +743 -0
  22. package/dist/commands/env/add.js +327 -0
  23. package/dist/commands/env/auth.js +62 -0
  24. package/dist/commands/env/list.js +41 -0
  25. package/dist/commands/env/remove.js +65 -0
  26. package/dist/commands/env/update.js +73 -0
  27. package/dist/commands/env/use.js +36 -0
  28. package/dist/commands/init.js +806 -0
  29. package/dist/commands/install.js +1840 -0
  30. package/dist/commands/logs.js +90 -0
  31. package/dist/commands/pm/disable.js +63 -0
  32. package/dist/commands/pm/enable.js +63 -0
  33. package/dist/commands/pm/list.js +54 -0
  34. package/dist/commands/prompts-stages.js +150 -0
  35. package/dist/commands/prompts-test.js +181 -0
  36. package/dist/commands/ps.js +116 -0
  37. package/dist/commands/scaffold/migration.js +38 -0
  38. package/dist/commands/scaffold/plugin.js +37 -0
  39. package/dist/commands/start.js +211 -0
  40. package/dist/commands/stop.js +90 -0
  41. package/dist/commands/upgrade.js +583 -0
  42. package/dist/generated/command-registry.js +133 -0
  43. package/dist/help/runtime-help.js +20 -0
  44. package/dist/lib/api-client.js +243 -0
  45. package/dist/lib/app-runtime.js +142 -0
  46. package/dist/lib/auth-store.js +241 -0
  47. package/dist/lib/bootstrap.js +387 -0
  48. package/dist/lib/build-config.js +10 -0
  49. package/dist/lib/cli-home.js +30 -0
  50. package/dist/lib/cli-locale.js +115 -0
  51. package/dist/lib/command-discovery.js +39 -0
  52. package/dist/lib/env-auth.js +872 -0
  53. package/dist/lib/generated-command.js +142 -0
  54. package/dist/lib/naming.js +70 -0
  55. package/dist/lib/openapi.js +62 -0
  56. package/dist/lib/post-processors.js +23 -0
  57. package/dist/lib/prompt-catalog.js +574 -0
  58. package/dist/lib/prompt-validators.js +185 -0
  59. package/dist/lib/prompt-web-ui.js +2061 -0
  60. package/dist/lib/resource-command.js +335 -0
  61. package/dist/lib/resource-request.js +104 -0
  62. package/dist/lib/run-npm.js +123 -0
  63. package/dist/lib/runtime-generator.js +419 -0
  64. package/dist/lib/runtime-store.js +56 -0
  65. package/dist/lib/ui.js +175 -0
  66. package/dist/locale/en-US.json +282 -0
  67. package/dist/locale/zh-CN.json +282 -0
  68. package/dist/post-processors/data-modeling.js +66 -0
  69. package/dist/post-processors/data-source-manager.js +114 -0
  70. package/dist/post-processors/index.js +19 -0
  71. package/nocobase-ctl.config.json +287 -0
  72. package/package.json +52 -25
  73. package/LICENSE +0 -201
  74. package/bin/index.js +0 -39
  75. package/nocobase.conf.tpl +0 -95
  76. package/src/cli.js +0 -19
  77. package/src/commands/benchmark.js +0 -73
  78. package/src/commands/build.js +0 -49
  79. package/src/commands/clean.js +0 -30
  80. package/src/commands/client.js +0 -166
  81. package/src/commands/create-nginx-conf.js +0 -37
  82. package/src/commands/create-plugin.js +0 -33
  83. package/src/commands/dev.js +0 -200
  84. package/src/commands/doc.js +0 -76
  85. package/src/commands/e2e.js +0 -265
  86. package/src/commands/global.js +0 -43
  87. package/src/commands/index.js +0 -45
  88. package/src/commands/instance-id.js +0 -47
  89. package/src/commands/locale/cronstrue.js +0 -122
  90. package/src/commands/locale/react-js-cron/en-US.json +0 -75
  91. package/src/commands/locale/react-js-cron/index.js +0 -17
  92. package/src/commands/locale/react-js-cron/zh-CN.json +0 -33
  93. package/src/commands/locale/react-js-cron/zh-TW.json +0 -33
  94. package/src/commands/locale.js +0 -81
  95. package/src/commands/p-test.js +0 -88
  96. package/src/commands/perf.js +0 -63
  97. package/src/commands/pkg.js +0 -321
  98. package/src/commands/pm2.js +0 -37
  99. package/src/commands/postinstall.js +0 -88
  100. package/src/commands/start.js +0 -148
  101. package/src/commands/tar.js +0 -36
  102. package/src/commands/test-coverage.js +0 -55
  103. package/src/commands/test.js +0 -107
  104. package/src/commands/umi.js +0 -33
  105. package/src/commands/update-deps.js +0 -72
  106. package/src/commands/upgrade.js +0 -47
  107. package/src/commands/view-license-key.js +0 -44
  108. package/src/index.js +0 -14
  109. package/src/license.js +0 -76
  110. package/src/logger.js +0 -75
  111. package/src/plugin-generator.js +0 -80
  112. package/src/util.js +0 -517
  113. package/templates/bundle-status.html +0 -338
  114. package/templates/create-app-package.json +0 -39
  115. package/templates/plugin/.npmignore.tpl +0 -2
  116. package/templates/plugin/README.md.tpl +0 -1
  117. package/templates/plugin/client.d.ts +0 -2
  118. package/templates/plugin/client.js +0 -1
  119. package/templates/plugin/package.json.tpl +0 -11
  120. package/templates/plugin/server.d.ts +0 -2
  121. package/templates/plugin/server.js +0 -1
  122. package/templates/plugin/src/client/client.d.ts +0 -249
  123. package/templates/plugin/src/client/index.tsx.tpl +0 -1
  124. package/templates/plugin/src/client/locale.ts +0 -21
  125. package/templates/plugin/src/client/models/index.ts +0 -12
  126. package/templates/plugin/src/client/plugin.tsx.tpl +0 -10
  127. package/templates/plugin/src/index.ts +0 -2
  128. package/templates/plugin/src/locale/en-US.json +0 -1
  129. package/templates/plugin/src/locale/zh-CN.json +0 -1
  130. package/templates/plugin/src/server/collections/.gitkeep +0 -0
  131. package/templates/plugin/src/server/index.ts.tpl +0 -1
  132. package/templates/plugin/src/server/plugin.ts.tpl +0 -19
@@ -0,0 +1,116 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+ import { Command, Flags } from '@oclif/core';
10
+ import { buildDockerDbContainerName, dockerContainerExists, dockerContainerIsRunning, formatMissingManagedAppEnvMessage, resolveManagedAppRuntime, } from '../lib/app-runtime.js';
11
+ import { listEnvs } from '../lib/auth-store.js';
12
+ import { renderTable } from '../lib/ui.js';
13
+ function appUrl(runtime) {
14
+ const port = String(runtime.env.config.appPort ?? '').trim();
15
+ if (port) {
16
+ return `http://127.0.0.1:${port}`;
17
+ }
18
+ const baseUrl = String(runtime.env.config.baseUrl ?? '').trim();
19
+ return baseUrl.replace(/\/api\/?$/, '');
20
+ }
21
+ async function isLocalAppHealthy(runtime) {
22
+ const port = String(runtime.env.config.appPort ?? '').trim();
23
+ if (!port) {
24
+ return false;
25
+ }
26
+ const controller = new AbortController();
27
+ const timeout = setTimeout(() => controller.abort(), 1500);
28
+ try {
29
+ const response = await fetch(`http://127.0.0.1:${port}/api/__health_check`, {
30
+ signal: controller.signal,
31
+ });
32
+ const text = await response.text();
33
+ return response.ok && text.trim().toLowerCase() === 'ok';
34
+ }
35
+ catch (_error) {
36
+ return false;
37
+ }
38
+ finally {
39
+ clearTimeout(timeout);
40
+ }
41
+ }
42
+ async function dockerStatus(containerName) {
43
+ if (!(await dockerContainerExists(containerName))) {
44
+ return 'missing';
45
+ }
46
+ return await dockerContainerIsRunning(containerName) ? 'running' : 'stopped';
47
+ }
48
+ async function dbStatus(runtime) {
49
+ if (!runtime.env.config.builtinDb) {
50
+ return runtime.kind === 'remote' ? 'external' : '-';
51
+ }
52
+ if (runtime.kind === 'remote') {
53
+ return 'external';
54
+ }
55
+ const dbDialect = String(runtime.env.config.dbDialect ?? 'postgres').trim() || 'postgres';
56
+ const containerName = buildDockerDbContainerName(runtime.envName, dbDialect, runtime.workspaceName);
57
+ return await dockerStatus(containerName);
58
+ }
59
+ async function appStatus(runtime) {
60
+ if (runtime.kind === 'remote') {
61
+ return 'remote';
62
+ }
63
+ if (runtime.kind === 'docker') {
64
+ return await dockerStatus(runtime.containerName);
65
+ }
66
+ return await isLocalAppHealthy(runtime) ? 'running' : 'stopped';
67
+ }
68
+ function sourceLabel(runtime) {
69
+ if (runtime.kind === 'remote') {
70
+ return 'remote';
71
+ }
72
+ return runtime.source;
73
+ }
74
+ export default class Ps extends Command {
75
+ static description = 'Show NocoBase runtime status for configured envs without starting or stopping anything.';
76
+ static examples = [
77
+ '<%= config.bin %> <%= command.id %>',
78
+ '<%= config.bin %> <%= command.id %> --env app1',
79
+ ];
80
+ static flags = {
81
+ env: Flags.string({
82
+ char: 'e',
83
+ description: 'CLI env name to inspect. Omit to show all configured envs',
84
+ }),
85
+ };
86
+ async run() {
87
+ const { flags } = await this.parse(Ps);
88
+ const requestedEnv = flags.env?.trim() || undefined;
89
+ const envNames = requestedEnv
90
+ ? [requestedEnv]
91
+ : Object.keys((await listEnvs()).envs).sort();
92
+ if (!envNames.length) {
93
+ this.log('No NocoBase env is configured yet. Run `nb init` to create one first.');
94
+ return;
95
+ }
96
+ const rows = [];
97
+ for (const envName of envNames) {
98
+ const runtime = await resolveManagedAppRuntime(envName);
99
+ if (!runtime) {
100
+ if (requestedEnv) {
101
+ this.error(formatMissingManagedAppEnvMessage(envName));
102
+ }
103
+ rows.push([envName, '-', 'missing', '-', '']);
104
+ continue;
105
+ }
106
+ rows.push([
107
+ runtime.envName,
108
+ sourceLabel(runtime),
109
+ await appStatus(runtime),
110
+ await dbStatus(runtime),
111
+ appUrl(runtime),
112
+ ]);
113
+ }
114
+ this.log(renderTable(['Env', 'Source', 'App', 'Database', 'URL'], rows));
115
+ }
116
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+ import { Args, Command, Flags } from '@oclif/core';
10
+ import { runNocoBaseCommand } from "../../lib/run-npm.js";
11
+ export default class ScaffoldMigration extends Command {
12
+ static args = {
13
+ name: Args.string({ description: 'migration name', required: true }),
14
+ };
15
+ static description = 'Run the legacy NocoBase scaffold migration (forwards to `npm run scaffold:migration` in the repo root)';
16
+ static examples = [
17
+ '<%= config.bin %> <%= command.id %> migration-name --pkg @nocobase/plugin-acl',
18
+ '<%= config.bin %> <%= command.id %> migration-name --pkg @nocobase/plugin-acl --on afterLoad',
19
+ ];
20
+ static flags = {
21
+ pkg: Flags.string({ description: 'plugin package name', required: true }),
22
+ on: Flags.string({ description: 'on', required: false, options: ['beforeLoad', 'afterSync', 'afterLoad'] }),
23
+ };
24
+ async run() {
25
+ const { args, flags } = await this.parse(ScaffoldMigration);
26
+ const npmArgs = ['create-migration', args.name, '--pkg', flags.pkg];
27
+ if (flags.on) {
28
+ npmArgs.push('--on', flags.on);
29
+ }
30
+ try {
31
+ await runNocoBaseCommand(npmArgs, { env: { LOGGER_SILENT: 'true' } });
32
+ }
33
+ catch (error) {
34
+ const message = error instanceof Error ? error.message : String(error);
35
+ this.error(message);
36
+ }
37
+ }
38
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+ import { Args, Command, Flags } from '@oclif/core';
10
+ import { runNocoBaseCommand } from "../../lib/run-npm.js";
11
+ export default class ScaffoldPlugin extends Command {
12
+ static args = {
13
+ pkg: Args.string({ description: 'plugin package name', required: true }),
14
+ };
15
+ static description = 'Run the legacy NocoBase scaffold plugin (forwards to `npm run scaffold:plugin` in the repo root)';
16
+ static examples = [
17
+ '<%= config.bin %> <%= command.id %> @nocobase-example/plugin-hello',
18
+ '<%= config.bin %> <%= command.id %> @nocobase-example/plugin-hello --force-recreate',
19
+ ];
20
+ static flags = {
21
+ 'force-recreate': Flags.boolean({ description: 'Force recreate the plugin', char: 'f', required: false }),
22
+ };
23
+ async run() {
24
+ const { args, flags } = await this.parse(ScaffoldPlugin);
25
+ const npmArgs = ['pm', 'create', args.pkg];
26
+ if (flags['force-recreate']) {
27
+ npmArgs.push('--force-recreate');
28
+ }
29
+ try {
30
+ await runNocoBaseCommand(npmArgs, { env: { LOGGER_SILENT: 'true' } });
31
+ }
32
+ catch (error) {
33
+ const message = error instanceof Error ? error.message : String(error);
34
+ this.error(message);
35
+ }
36
+ }
37
+ }
@@ -0,0 +1,211 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+ import { Command, Flags } from '@oclif/core';
10
+ import { formatMissingManagedAppEnvMessage, resolveManagedAppRuntime, runLocalNocoBaseCommand, startDockerContainer, } from '../lib/app-runtime.js';
11
+ import { failTask, printInfo, startTask, succeedTask } from '../lib/ui.js';
12
+ function argvHasToken(argv, tokens) {
13
+ return tokens.some((token) => argv.includes(token));
14
+ }
15
+ function formatDockerStartFailure(envName, message) {
16
+ if (/does not exist/i.test(message)) {
17
+ return [
18
+ `Can't start NocoBase for "${envName}" yet.`,
19
+ 'The saved Docker app for this env could not be found on this machine.',
20
+ `Try reinstalling the env, or check whether the container was removed outside the CLI.`,
21
+ `Details: ${message}`,
22
+ ].join('\n');
23
+ }
24
+ return [
25
+ `Couldn't start NocoBase for "${envName}".`,
26
+ 'Check that the Docker runtime for this env is still available, then try again.',
27
+ `Details: ${message}`,
28
+ ].join('\n');
29
+ }
30
+ function formatLocalStartFailure(envName, options) {
31
+ const sourceLabel = options?.source === 'git'
32
+ ? 'the local Git checkout'
33
+ : options?.source === 'npm'
34
+ ? 'the local npm app'
35
+ : 'the local app';
36
+ const portHint = options?.port ? ` Expected app port: ${options.port}.` : '';
37
+ return [
38
+ `Couldn't start NocoBase for "${envName}".`,
39
+ `The CLI was not able to start ${sourceLabel} successfully.`,
40
+ `Check that the app dependencies, database connection, and local env settings are ready, then try again.${portHint}`,
41
+ ].join('\n');
42
+ }
43
+ function formatAppUrl(port) {
44
+ const value = String(port ?? '').trim();
45
+ if (!value) {
46
+ return undefined;
47
+ }
48
+ return `http://127.0.0.1:${value}`;
49
+ }
50
+ async function isAppAlreadyRunning(appUrl) {
51
+ if (!appUrl) {
52
+ return false;
53
+ }
54
+ const controller = new AbortController();
55
+ const timeout = setTimeout(() => controller.abort(), 1500);
56
+ try {
57
+ const response = await fetch(`${appUrl}/api/__health_check`, {
58
+ signal: controller.signal,
59
+ });
60
+ const text = await response.text();
61
+ return response.ok && text.trim().toLowerCase() === 'ok';
62
+ }
63
+ catch (_error) {
64
+ return false;
65
+ }
66
+ finally {
67
+ clearTimeout(timeout);
68
+ }
69
+ }
70
+ export default class Start extends Command {
71
+ static description = 'Start NocoBase for the selected env. Local npm/git installs run the app command, and Docker installs start the saved app container.';
72
+ static examples = [
73
+ '<%= config.bin %> <%= command.id %>',
74
+ '<%= config.bin %> <%= command.id %> --env local',
75
+ '<%= config.bin %> <%= command.id %> --env local --quickstart',
76
+ '<%= config.bin %> <%= command.id %> --env local --port 12000',
77
+ '<%= config.bin %> <%= command.id %> --env local --daemon',
78
+ '<%= config.bin %> <%= command.id %> --env local --no-daemon',
79
+ '<%= config.bin %> <%= command.id %> --env local --instances 2',
80
+ '<%= config.bin %> <%= command.id %> --env local --launch-mode pm2',
81
+ '<%= config.bin %> <%= command.id %> --env local --verbose',
82
+ '<%= config.bin %> <%= command.id %> --env local-docker',
83
+ ];
84
+ static flags = {
85
+ env: Flags.string({
86
+ char: 'e',
87
+ description: 'CLI env name to start. Defaults to the current env when omitted',
88
+ }),
89
+ quickstart: Flags.boolean({ description: 'Quickstart the application', required: false }),
90
+ port: Flags.string({ description: 'Port (overrides appPort from env config when set)', char: 'p', required: false }),
91
+ daemon: Flags.boolean({
92
+ description: 'Run the application as a daemon (default: true; use --no-daemon to stay in the foreground)',
93
+ char: 'd',
94
+ required: false,
95
+ default: true,
96
+ allowNo: true,
97
+ }),
98
+ instances: Flags.integer({ description: 'Number of instances to run', char: 'i', required: false }),
99
+ 'launch-mode': Flags.string({ description: 'Launch Mode', required: false, options: ['pm2', 'node'] }),
100
+ verbose: Flags.boolean({
101
+ description: 'Show raw startup output from the underlying local or Docker command',
102
+ default: false,
103
+ }),
104
+ };
105
+ async run() {
106
+ const { flags } = await this.parse(Start);
107
+ const requestedEnv = flags.env?.trim() || undefined;
108
+ const daemonFlagWasProvided = argvHasToken(this.argv, ['--daemon', '--no-daemon']);
109
+ const runtime = await resolveManagedAppRuntime(requestedEnv);
110
+ const commandStdio = flags.verbose ? 'inherit' : 'ignore';
111
+ if (!runtime) {
112
+ this.error(formatMissingManagedAppEnvMessage(requestedEnv));
113
+ }
114
+ if (runtime.kind === 'remote') {
115
+ this.error([
116
+ `Can't start "${runtime.envName}" from this machine.`,
117
+ 'This env only has an API connection, so there is no saved local app or Docker runtime to launch here.',
118
+ 'Connect it to a local checkout or reinstall it with npm, git, or Docker if you want CLI-managed start and stop.',
119
+ ].join('\n'));
120
+ }
121
+ if (runtime.kind === 'docker') {
122
+ const unsupportedFlags = [
123
+ flags.quickstart ? '--quickstart' : undefined,
124
+ flags.port ? '--port' : undefined,
125
+ daemonFlagWasProvided ? (flags.daemon ? '--daemon' : '--no-daemon') : undefined,
126
+ flags.instances !== undefined ? '--instances' : undefined,
127
+ flags['launch-mode'] ? '--launch-mode' : undefined,
128
+ ].filter(Boolean);
129
+ if (unsupportedFlags.length > 0) {
130
+ this.error([
131
+ `Can't apply ${unsupportedFlags.join(', ')} to "${runtime.envName}".`,
132
+ 'This env is managed by Docker, so those options are only available for local npm/git installs.',
133
+ `Run \`nb start --env ${runtime.envName}\` to start the saved container, or recreate the env if you need different runtime settings.`,
134
+ ].join('\n'));
135
+ }
136
+ const appUrl = formatAppUrl(runtime.env.appPort === undefined || runtime.env.appPort === null
137
+ ? undefined
138
+ : String(runtime.env.appPort));
139
+ startTask(`Starting NocoBase for "${runtime.envName}"...`);
140
+ try {
141
+ const state = await startDockerContainer(runtime.containerName, {
142
+ stdio: commandStdio,
143
+ });
144
+ succeedTask(state === 'already-running'
145
+ ? `NocoBase is already running for "${runtime.envName}"${appUrl ? ` at ${appUrl}` : ''}.`
146
+ : `NocoBase is running for "${runtime.envName}"${appUrl ? ` at ${appUrl}` : ''}.`);
147
+ }
148
+ catch (error) {
149
+ const message = error instanceof Error ? error.message : String(error);
150
+ failTask(`Failed to start NocoBase for "${runtime.envName}".`);
151
+ this.error(formatDockerStartFailure(runtime.envName, message));
152
+ }
153
+ return;
154
+ }
155
+ const npmArgs = ['start'];
156
+ if (flags.quickstart) {
157
+ npmArgs.push('--quickstart');
158
+ }
159
+ if (flags.port) {
160
+ npmArgs.push('--port', flags.port);
161
+ }
162
+ else if (runtime.env.appPort !== undefined && runtime.env.appPort !== null && String(runtime.env.appPort).trim() !== '') {
163
+ npmArgs.push('--port', String(runtime.env.appPort));
164
+ }
165
+ if (flags.daemon !== false) {
166
+ npmArgs.push('--daemon');
167
+ }
168
+ if (flags.instances !== undefined) {
169
+ npmArgs.push('--instances', flags.instances.toString());
170
+ }
171
+ if (flags['launch-mode']) {
172
+ npmArgs.push('--launch-mode', flags['launch-mode']);
173
+ }
174
+ const appUrl = formatAppUrl(flags.port
175
+ || (runtime.env.appPort !== undefined && runtime.env.appPort !== null
176
+ ? String(runtime.env.appPort).trim()
177
+ : undefined));
178
+ if (await isAppAlreadyRunning(appUrl)) {
179
+ if (flags.daemon === false) {
180
+ printInfo(`NocoBase is already running for "${runtime.envName}"${appUrl ? ` at ${appUrl}` : ''}. Use \`nb stop --env ${runtime.envName}\` before starting it again in the foreground.`);
181
+ }
182
+ else {
183
+ succeedTask(`NocoBase is already running for "${runtime.envName}"${appUrl ? ` at ${appUrl}` : ''}.`);
184
+ }
185
+ return;
186
+ }
187
+ if (flags.daemon === false) {
188
+ printInfo(`Starting NocoBase for "${runtime.envName}" in the foreground${appUrl ? ` at ${appUrl}` : ''}. Press Ctrl+C to stop.`);
189
+ }
190
+ else {
191
+ startTask(`Starting NocoBase for "${runtime.envName}" in the background...`);
192
+ }
193
+ try {
194
+ await runLocalNocoBaseCommand(runtime, npmArgs, {
195
+ stdio: commandStdio,
196
+ });
197
+ if (flags.daemon !== false) {
198
+ succeedTask(`NocoBase is starting for "${runtime.envName}"${appUrl ? ` at ${appUrl}` : ''}.`);
199
+ }
200
+ }
201
+ catch (error) {
202
+ failTask(`Failed to start NocoBase for "${runtime.envName}".`);
203
+ this.error(formatLocalStartFailure(runtime.envName, {
204
+ port: flags.port || (runtime.env.appPort !== undefined && runtime.env.appPort !== null
205
+ ? String(runtime.env.appPort).trim()
206
+ : undefined),
207
+ source: runtime.source,
208
+ }));
209
+ }
210
+ }
211
+ }
@@ -0,0 +1,90 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+ import { Command, Flags } from '@oclif/core';
10
+ import { formatMissingManagedAppEnvMessage, resolveManagedAppRuntime, runLocalNocoBaseCommand, stopDockerContainer, } from '../lib/app-runtime.js';
11
+ import { failTask, startTask, succeedTask } from '../lib/ui.js';
12
+ function formatStopFailure(envName, message) {
13
+ if (/does not exist/i.test(message)) {
14
+ return [
15
+ `Can't stop NocoBase for "${envName}" yet.`,
16
+ 'The saved Docker app for this env could not be found on this machine.',
17
+ 'If it was removed manually, reinstall or reconnect the env before trying again.',
18
+ `Details: ${message}`,
19
+ ].join('\n');
20
+ }
21
+ return [
22
+ `Couldn't stop NocoBase for "${envName}".`,
23
+ 'Check that the local app or Docker runtime is still available, then try again.',
24
+ `Details: ${message}`,
25
+ ].join('\n');
26
+ }
27
+ export default class Stop extends Command {
28
+ static description = 'Stop NocoBase for the selected env. Local npm/git installs stop the app process, and Docker installs stop the saved app container.';
29
+ static examples = [
30
+ '<%= config.bin %> <%= command.id %>',
31
+ '<%= config.bin %> <%= command.id %> --env local',
32
+ '<%= config.bin %> <%= command.id %> --env local --verbose',
33
+ '<%= config.bin %> <%= command.id %> --env local-docker',
34
+ ];
35
+ static flags = {
36
+ env: Flags.string({
37
+ char: 'e',
38
+ description: 'CLI env name to stop. Defaults to the current env when omitted',
39
+ }),
40
+ verbose: Flags.boolean({
41
+ description: 'Show raw shutdown output from the underlying local or Docker command',
42
+ default: false,
43
+ }),
44
+ };
45
+ async run() {
46
+ const { flags } = await this.parse(Stop);
47
+ const requestedEnv = flags.env?.trim() || undefined;
48
+ const runtime = await resolveManagedAppRuntime(requestedEnv);
49
+ const commandStdio = flags.verbose ? 'inherit' : 'ignore';
50
+ if (!runtime) {
51
+ this.error(formatMissingManagedAppEnvMessage(requestedEnv));
52
+ }
53
+ if (runtime.kind === 'remote') {
54
+ this.error([
55
+ `Can't stop "${runtime.envName}" from this machine.`,
56
+ 'This env only has an API connection, so there is no saved local app or Docker runtime to stop here.',
57
+ 'If the app is running on a server, stop it there or reconnect this env to a local runtime first.',
58
+ ].join('\n'));
59
+ }
60
+ if (runtime.kind === 'docker') {
61
+ startTask(`Stopping NocoBase for "${runtime.envName}"...`);
62
+ try {
63
+ const state = await stopDockerContainer(runtime.containerName, {
64
+ stdio: commandStdio,
65
+ });
66
+ succeedTask(state === 'already-stopped'
67
+ ? `NocoBase is already stopped for "${runtime.envName}".`
68
+ : `NocoBase has stopped for "${runtime.envName}".`);
69
+ }
70
+ catch (error) {
71
+ const message = error instanceof Error ? error.message : String(error);
72
+ failTask(`Failed to stop NocoBase for "${runtime.envName}".`);
73
+ this.error(formatStopFailure(runtime.envName, message));
74
+ }
75
+ return;
76
+ }
77
+ startTask(`Stopping NocoBase for "${runtime.envName}"...`);
78
+ try {
79
+ await runLocalNocoBaseCommand(runtime, ['pm2', 'kill'], {
80
+ stdio: commandStdio,
81
+ });
82
+ succeedTask(`NocoBase has stopped for "${runtime.envName}".`);
83
+ }
84
+ catch (error) {
85
+ const message = error instanceof Error ? error.message : String(error);
86
+ failTask(`Failed to stop NocoBase for "${runtime.envName}".`);
87
+ this.error(formatStopFailure(runtime.envName, message));
88
+ }
89
+ }
90
+ }