@nocobase/cli 2.1.0-alpha.3 → 2.1.0-alpha.31

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 (182) hide show
  1. package/LICENSE.txt +107 -0
  2. package/README.md +379 -19
  3. package/README.zh-CN.md +329 -0
  4. package/bin/run.cmd +3 -0
  5. package/bin/run.js +131 -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/app/down.js +266 -0
  14. package/dist/commands/app/logs.js +98 -0
  15. package/dist/commands/app/restart.js +75 -0
  16. package/dist/commands/app/start.js +253 -0
  17. package/dist/commands/app/stop.js +99 -0
  18. package/dist/commands/app/upgrade.js +582 -0
  19. package/{src/cli.js → dist/commands/build.js} +4 -11
  20. package/dist/commands/config/delete.js +30 -0
  21. package/dist/commands/config/get.js +29 -0
  22. package/dist/commands/config/index.js +20 -0
  23. package/dist/commands/config/list.js +29 -0
  24. package/dist/commands/config/set.js +35 -0
  25. package/dist/commands/db/check.js +238 -0
  26. package/dist/commands/db/logs.js +85 -0
  27. package/dist/commands/db/ps.js +60 -0
  28. package/dist/commands/db/shared.js +96 -0
  29. package/dist/commands/db/start.js +71 -0
  30. package/dist/commands/db/stop.js +71 -0
  31. package/{templates/plugin/src/client/models/index.ts → dist/commands/dev.js} +4 -4
  32. package/{src/index.js → dist/commands/down.js} +4 -6
  33. package/{src/commands/locale/react-js-cron/index.js → dist/commands/download.js} +4 -8
  34. package/dist/commands/env/add.js +312 -0
  35. package/dist/commands/env/auth.js +55 -0
  36. package/dist/commands/env/info.js +156 -0
  37. package/dist/commands/env/list.js +50 -0
  38. package/dist/commands/env/remove.js +59 -0
  39. package/dist/commands/env/shared.js +158 -0
  40. package/dist/commands/env/update.js +67 -0
  41. package/dist/commands/env/use.js +28 -0
  42. package/dist/commands/examples/prompts-stages.js +150 -0
  43. package/dist/commands/examples/prompts-test.js +181 -0
  44. package/dist/commands/init.js +1027 -0
  45. package/dist/commands/install.js +2206 -0
  46. package/dist/commands/license/activate.js +360 -0
  47. package/dist/commands/license/env.js +94 -0
  48. package/dist/commands/license/generate-id.js +108 -0
  49. package/dist/commands/license/id.js +56 -0
  50. package/dist/commands/license/index.js +20 -0
  51. package/dist/commands/license/plugins/clean.js +101 -0
  52. package/dist/commands/license/plugins/index.js +20 -0
  53. package/dist/commands/license/plugins/list.js +50 -0
  54. package/dist/commands/license/plugins/shared.js +325 -0
  55. package/dist/commands/license/plugins/sync.js +269 -0
  56. package/dist/commands/license/shared.js +414 -0
  57. package/dist/commands/license/status.js +50 -0
  58. package/dist/commands/logs.js +12 -0
  59. package/dist/commands/plugin/disable.js +66 -0
  60. package/dist/commands/plugin/enable.js +66 -0
  61. package/dist/commands/plugin/list.js +62 -0
  62. package/dist/commands/pm/disable.js +12 -0
  63. package/dist/commands/pm/enable.js +12 -0
  64. package/dist/commands/pm/list.js +12 -0
  65. package/dist/commands/restart.js +12 -0
  66. package/dist/commands/scaffold/migration.js +38 -0
  67. package/dist/commands/scaffold/plugin.js +37 -0
  68. package/dist/commands/self/check.js +71 -0
  69. package/dist/commands/self/index.js +20 -0
  70. package/dist/commands/self/update.js +86 -0
  71. package/dist/commands/skills/check.js +69 -0
  72. package/dist/commands/skills/index.js +20 -0
  73. package/dist/commands/skills/install.js +71 -0
  74. package/dist/commands/skills/remove.js +71 -0
  75. package/dist/commands/skills/update.js +78 -0
  76. package/dist/commands/source/build.js +58 -0
  77. package/dist/commands/source/dev.js +158 -0
  78. package/dist/commands/source/download.js +866 -0
  79. package/dist/commands/source/test.js +477 -0
  80. package/dist/commands/start.js +12 -0
  81. package/dist/commands/stop.js +12 -0
  82. package/dist/commands/test.js +12 -0
  83. package/dist/commands/upgrade.js +12 -0
  84. package/dist/generated/command-registry.js +133 -0
  85. package/dist/help/runtime-help.js +23 -0
  86. package/dist/lib/api-client.js +329 -0
  87. package/dist/lib/app-health.js +126 -0
  88. package/dist/lib/app-managed-resources.js +268 -0
  89. package/dist/lib/app-runtime.js +171 -0
  90. package/dist/lib/auth-store.js +328 -0
  91. package/dist/lib/bootstrap.js +384 -0
  92. package/dist/lib/build-config.js +18 -0
  93. package/dist/lib/builtin-db.js +86 -0
  94. package/dist/lib/cli-config.js +176 -0
  95. package/dist/lib/cli-home.js +47 -0
  96. package/dist/lib/cli-locale.js +129 -0
  97. package/dist/lib/command-discovery.js +39 -0
  98. package/dist/lib/db-connection-check.js +178 -0
  99. package/dist/lib/env-auth.js +872 -0
  100. package/dist/lib/env-config.js +87 -0
  101. package/dist/lib/generated-command.js +171 -0
  102. package/dist/lib/http-request.js +49 -0
  103. package/dist/lib/naming.js +70 -0
  104. package/dist/lib/openapi.js +62 -0
  105. package/dist/lib/plugin-storage.js +127 -0
  106. package/dist/lib/post-processors.js +23 -0
  107. package/dist/lib/prompt-catalog.js +581 -0
  108. package/dist/lib/prompt-validators.js +185 -0
  109. package/dist/lib/prompt-web-ui.js +2103 -0
  110. package/dist/lib/resource-command.js +343 -0
  111. package/dist/lib/resource-request.js +104 -0
  112. package/dist/lib/run-npm.js +250 -0
  113. package/dist/lib/runtime-env-vars.js +32 -0
  114. package/dist/lib/runtime-generator.js +498 -0
  115. package/dist/lib/runtime-store.js +56 -0
  116. package/dist/lib/self-manager.js +301 -0
  117. package/dist/lib/skills-manager.js +296 -0
  118. package/dist/lib/startup-update.js +281 -0
  119. package/dist/lib/ui.js +178 -0
  120. package/dist/locale/en-US.json +339 -0
  121. package/dist/locale/zh-CN.json +339 -0
  122. package/dist/post-processors/data-modeling.js +66 -0
  123. package/dist/post-processors/data-source-manager.js +114 -0
  124. package/dist/post-processors/index.js +19 -0
  125. package/nocobase-ctl.config.json +369 -0
  126. package/package.json +95 -26
  127. package/LICENSE +0 -661
  128. package/bin/index.js +0 -39
  129. package/nocobase.conf.tpl +0 -95
  130. package/src/commands/benchmark.js +0 -73
  131. package/src/commands/build.js +0 -49
  132. package/src/commands/clean.js +0 -30
  133. package/src/commands/client.js +0 -166
  134. package/src/commands/create-nginx-conf.js +0 -37
  135. package/src/commands/create-plugin.js +0 -33
  136. package/src/commands/dev.js +0 -200
  137. package/src/commands/doc.js +0 -76
  138. package/src/commands/e2e.js +0 -265
  139. package/src/commands/global.js +0 -43
  140. package/src/commands/index.js +0 -45
  141. package/src/commands/instance-id.js +0 -47
  142. package/src/commands/locale/cronstrue.js +0 -122
  143. package/src/commands/locale/react-js-cron/en-US.json +0 -75
  144. package/src/commands/locale/react-js-cron/zh-CN.json +0 -33
  145. package/src/commands/locale/react-js-cron/zh-TW.json +0 -33
  146. package/src/commands/locale.js +0 -81
  147. package/src/commands/p-test.js +0 -88
  148. package/src/commands/perf.js +0 -63
  149. package/src/commands/pkg.js +0 -321
  150. package/src/commands/pm2.js +0 -37
  151. package/src/commands/postinstall.js +0 -88
  152. package/src/commands/start.js +0 -148
  153. package/src/commands/tar.js +0 -36
  154. package/src/commands/test-coverage.js +0 -55
  155. package/src/commands/test.js +0 -107
  156. package/src/commands/umi.js +0 -33
  157. package/src/commands/update-deps.js +0 -72
  158. package/src/commands/upgrade.js +0 -47
  159. package/src/commands/view-license-key.js +0 -44
  160. package/src/license.js +0 -76
  161. package/src/logger.js +0 -75
  162. package/src/plugin-generator.js +0 -80
  163. package/src/util.js +0 -517
  164. package/templates/bundle-status.html +0 -338
  165. package/templates/create-app-package.json +0 -39
  166. package/templates/plugin/.npmignore.tpl +0 -2
  167. package/templates/plugin/README.md.tpl +0 -1
  168. package/templates/plugin/client.d.ts +0 -2
  169. package/templates/plugin/client.js +0 -1
  170. package/templates/plugin/package.json.tpl +0 -11
  171. package/templates/plugin/server.d.ts +0 -2
  172. package/templates/plugin/server.js +0 -1
  173. package/templates/plugin/src/client/client.d.ts +0 -249
  174. package/templates/plugin/src/client/index.tsx.tpl +0 -1
  175. package/templates/plugin/src/client/locale.ts +0 -21
  176. package/templates/plugin/src/client/plugin.tsx.tpl +0 -10
  177. package/templates/plugin/src/index.ts +0 -2
  178. package/templates/plugin/src/locale/en-US.json +0 -1
  179. package/templates/plugin/src/locale/zh-CN.json +0 -1
  180. package/templates/plugin/src/server/collections/.gitkeep +0 -0
  181. package/templates/plugin/src/server/index.ts.tpl +0 -1
  182. package/templates/plugin/src/server/plugin.ts.tpl +0 -19
@@ -0,0 +1,266 @@
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 * as p from '@clack/prompts';
10
+ import { Command, Flags } from '@oclif/core';
11
+ import fsp from 'node:fs/promises';
12
+ import os from 'node:os';
13
+ import path from 'node:path';
14
+ import { buildDockerDbContainerName, formatMissingManagedAppEnvMessage, resolveManagedAppRuntime, runLocalNocoBaseCommand, } from '../../lib/app-runtime.js';
15
+ import { removeEnv } from '../../lib/auth-store.js';
16
+ import { resolveConfiguredEnvPath } from '../../lib/cli-home.js';
17
+ import { commandOutput, commandSucceeds, run } from '../../lib/run-npm.js';
18
+ import { failTask, isInteractiveTerminal, printInfo, startTask, succeedTask, } from '../../lib/ui.js';
19
+ function resolveConfiguredPath(value) {
20
+ return resolveConfiguredEnvPath(value);
21
+ }
22
+ function assertSafeRemovalPath(target, label) {
23
+ const resolved = path.resolve(target);
24
+ const cwd = path.resolve(process.cwd());
25
+ const home = path.resolve(os.homedir());
26
+ const root = path.parse(resolved).root;
27
+ if (resolved === root || resolved === cwd || resolved === home) {
28
+ throw new Error(`Refusing to remove ${label} at "${resolved}" because it is too broad.`);
29
+ }
30
+ }
31
+ async function removePathIfExists(target, label) {
32
+ const resolved = path.resolve(target);
33
+ assertSafeRemovalPath(resolved, label);
34
+ await fsp.rm(resolved, { recursive: true, force: true });
35
+ }
36
+ async function dockerContainerExists(containerName) {
37
+ return await commandSucceeds('docker', ['container', 'inspect', containerName]);
38
+ }
39
+ async function removeDockerContainerIfExists(containerName) {
40
+ if (!(await dockerContainerExists(containerName))) {
41
+ return 'missing';
42
+ }
43
+ await run('docker', ['rm', '-f', containerName], {
44
+ errorName: 'docker rm',
45
+ stdio: 'ignore',
46
+ });
47
+ return 'removed';
48
+ }
49
+ async function dockerNetworkExists(networkName) {
50
+ return await commandSucceeds('docker', ['network', 'inspect', networkName]);
51
+ }
52
+ async function dockerNetworkHasActiveEndpoints(networkName) {
53
+ try {
54
+ const output = await commandOutput('docker', [
55
+ 'network',
56
+ 'inspect',
57
+ networkName,
58
+ '--format',
59
+ '{{json .Containers}}',
60
+ ], {
61
+ errorName: 'docker network inspect',
62
+ });
63
+ const containers = JSON.parse(output || '{}');
64
+ return Boolean(containers && typeof containers === 'object' && Object.keys(containers).length > 0);
65
+ }
66
+ catch {
67
+ return false;
68
+ }
69
+ }
70
+ async function removeDockerNetworkIfUnused(networkName) {
71
+ if (!(await dockerNetworkExists(networkName))) {
72
+ return 'missing';
73
+ }
74
+ try {
75
+ await run('docker', ['network', 'rm', networkName], {
76
+ errorName: 'docker network rm',
77
+ stdio: 'ignore',
78
+ });
79
+ return 'removed';
80
+ }
81
+ catch (error) {
82
+ const message = error instanceof Error ? error.message : String(error);
83
+ if (/has active endpoints|is in use|active endpoints/i.test(message)
84
+ || (await dockerNetworkExists(networkName) && await dockerNetworkHasActiveEndpoints(networkName))) {
85
+ return 'in-use';
86
+ }
87
+ throw error;
88
+ }
89
+ }
90
+ function builtinDbContainerName(runtime) {
91
+ if (!runtime.env.config.builtinDb) {
92
+ return undefined;
93
+ }
94
+ const dbDialect = String(runtime.env.config.dbDialect ?? 'postgres').trim() || 'postgres';
95
+ return buildDockerDbContainerName(runtime.envName, dbDialect, runtime.dockerContainerPrefix || runtime.workspaceName);
96
+ }
97
+ function managedDockerNetworkName(runtime) {
98
+ return runtime.dockerNetworkName?.trim() || runtime.workspaceName?.trim() || undefined;
99
+ }
100
+ async function confirmDownAll(envName, yes, options) {
101
+ if (yes) {
102
+ return true;
103
+ }
104
+ const usedCurrentEnv = options?.explicitEnv === false;
105
+ if (!isInteractiveTerminal()) {
106
+ if (usedCurrentEnv) {
107
+ throw new Error(`\`nb app down --all\` is using the current env "${envName}". Re-run with --env ${envName} --yes to delete everything for that env in non-interactive mode.`);
108
+ }
109
+ throw new Error(`\`nb app down --all\` needs confirmation. Re-run with --yes to delete everything for "${envName}" in non-interactive mode.`);
110
+ }
111
+ const answer = await p.confirm({
112
+ message: usedCurrentEnv
113
+ ? `Delete everything for current env "${envName}"? This removes the app, managed containers, storage data, and the saved CLI env config.`
114
+ : `Delete everything for "${envName}"? This removes the app, managed containers, storage data, and the saved CLI env config.`,
115
+ active: 'yes',
116
+ inactive: 'no',
117
+ initialValue: false,
118
+ });
119
+ if (p.isCancel(answer)) {
120
+ p.cancel('Down cancelled.');
121
+ return false;
122
+ }
123
+ return answer;
124
+ }
125
+ function formatDownFailure(envName, message) {
126
+ return [
127
+ `Couldn't bring down NocoBase for "${envName}".`,
128
+ 'Some local runtime resources may still exist. Check Docker or the local app process, then try again.',
129
+ `Details: ${message}`,
130
+ ].join('\n');
131
+ }
132
+ export default class AppDown extends Command {
133
+ static hidden = false;
134
+ static description = 'Bring down the selected env by removing runtime containers and the saved local app files. Storage data and env config are kept unless explicitly requested.';
135
+ static examples = [
136
+ '<%= config.bin %> <%= command.id %> --env app1',
137
+ '<%= config.bin %> <%= command.id %> --env app1 --all --yes',
138
+ ];
139
+ static flags = {
140
+ env: Flags.string({
141
+ char: 'e',
142
+ description: 'CLI env name to bring down. Defaults to the current env when omitted',
143
+ }),
144
+ all: Flags.boolean({
145
+ description: 'Delete everything for this env, including storage data and the saved env config',
146
+ default: false,
147
+ }),
148
+ yes: Flags.boolean({
149
+ char: 'y',
150
+ description: 'Confirm destructive actions without prompting',
151
+ default: false,
152
+ }),
153
+ verbose: Flags.boolean({
154
+ description: 'Show raw output from shutdown commands',
155
+ default: false,
156
+ }),
157
+ };
158
+ async run() {
159
+ const { flags } = await this.parse(AppDown);
160
+ const requestedEnv = flags.env?.trim() || undefined;
161
+ const explicitEnv = Boolean(requestedEnv);
162
+ const removeData = Boolean(flags.all);
163
+ const removeEnvConfig = Boolean(flags.all);
164
+ const runtime = await resolveManagedAppRuntime(requestedEnv);
165
+ if (!runtime) {
166
+ this.error(formatMissingManagedAppEnvMessage(requestedEnv));
167
+ }
168
+ if (runtime.kind === 'http') {
169
+ this.error([
170
+ `Can't bring down "${runtime.envName}" from this machine.`,
171
+ 'This env only has an API connection, so there is no saved local app, Docker app, or managed database to remove here.',
172
+ 'Use `nb env remove` if you only want to remove the CLI connection config.',
173
+ ].join('\n'));
174
+ }
175
+ if (runtime.kind === 'ssh') {
176
+ this.error([
177
+ `Can't bring down "${runtime.envName}" yet.`,
178
+ 'SSH env support is reserved but not implemented yet.',
179
+ 'Use `nb env remove` if you only want to remove the saved CLI config for now.',
180
+ ].join('\n'));
181
+ }
182
+ if (flags.all) {
183
+ let confirmed = false;
184
+ try {
185
+ confirmed = await confirmDownAll(runtime.envName, flags.yes, { explicitEnv });
186
+ }
187
+ catch (error) {
188
+ this.error(error instanceof Error ? error.message : String(error));
189
+ }
190
+ if (!confirmed) {
191
+ return;
192
+ }
193
+ }
194
+ try {
195
+ if (runtime.kind === 'docker') {
196
+ startTask(`Removing Docker app container for "${runtime.envName}"...`);
197
+ const state = await removeDockerContainerIfExists(runtime.containerName);
198
+ succeedTask(state === 'removed'
199
+ ? `Docker app container removed for "${runtime.envName}".`
200
+ : `No Docker app container found for "${runtime.envName}".`);
201
+ }
202
+ else {
203
+ startTask(`Stopping local NocoBase app for "${runtime.envName}"...`);
204
+ await runLocalNocoBaseCommand(runtime, ['pm2', 'kill'], {
205
+ stdio: flags.verbose ? 'inherit' : 'ignore',
206
+ });
207
+ succeedTask(`Local NocoBase app stopped for "${runtime.envName}".`);
208
+ }
209
+ if (runtime.kind === 'local' || runtime.kind === 'docker') {
210
+ const dbContainerName = builtinDbContainerName(runtime);
211
+ if (dbContainerName) {
212
+ startTask(`Removing built-in database container for "${runtime.envName}"...`);
213
+ const state = await removeDockerContainerIfExists(dbContainerName);
214
+ succeedTask(state === 'removed'
215
+ ? `Built-in database container removed for "${runtime.envName}".`
216
+ : `No built-in database container found for "${runtime.envName}".`);
217
+ }
218
+ const networkName = managedDockerNetworkName(runtime);
219
+ if (networkName) {
220
+ startTask(`Removing Docker network for "${runtime.envName}" if unused...`);
221
+ const state = await removeDockerNetworkIfUnused(networkName);
222
+ if (state === 'removed') {
223
+ succeedTask(`Docker network removed for "${runtime.envName}".`);
224
+ }
225
+ else if (state === 'missing') {
226
+ succeedTask(`No Docker network found for "${runtime.envName}".`);
227
+ }
228
+ else {
229
+ succeedTask(`Docker network is still in use for "${runtime.envName}". Keeping it.`);
230
+ }
231
+ }
232
+ }
233
+ if (runtime.kind === 'local') {
234
+ const localAppPath = resolveConfiguredPath(runtime.env.config.appRootPath) || runtime.projectRoot;
235
+ if (localAppPath) {
236
+ startTask(`Removing local app files for "${runtime.envName}"...`);
237
+ await removePathIfExists(localAppPath, `app files for "${runtime.envName}"`);
238
+ succeedTask(`Local app files removed for "${runtime.envName}".`);
239
+ }
240
+ else {
241
+ printInfo(`No saved local app path found for "${runtime.envName}".`);
242
+ }
243
+ }
244
+ if (removeData) {
245
+ const configuredStoragePath = resolveConfiguredPath(runtime.env.config.storagePath);
246
+ if (configuredStoragePath) {
247
+ startTask(`Removing storage data for "${runtime.envName}"...`);
248
+ await removePathIfExists(configuredStoragePath, `storage data for "${runtime.envName}"`);
249
+ succeedTask(`Storage data removed for "${runtime.envName}".`);
250
+ }
251
+ else {
252
+ printInfo(`No saved storage path found for "${runtime.envName}".`);
253
+ }
254
+ }
255
+ if (removeEnvConfig) {
256
+ startTask(`Removing saved CLI env config for "${runtime.envName}"...`);
257
+ const result = await removeEnv(runtime.envName);
258
+ succeedTask(`Saved CLI env config removed for "${runtime.envName}"${result.currentEnv ? ` (current env: ${result.currentEnv})` : ''}.`);
259
+ }
260
+ }
261
+ catch (error) {
262
+ failTask(`Failed to bring down NocoBase for "${runtime.envName}".`);
263
+ this.error(formatDownFailure(runtime.envName, error instanceof Error ? error.message : String(error)));
264
+ }
265
+ }
266
+ }
@@ -0,0 +1,98 @@
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, } from '../../lib/app-runtime.js';
11
+ import { run } from '../../lib/run-npm.js';
12
+ import { printInfo } from '../../lib/ui.js';
13
+ function formatLogsFailure(envName, message) {
14
+ return [
15
+ `Couldn't show logs for "${envName}".`,
16
+ 'Check that the saved local app or Docker container still exists on this machine, then try again.',
17
+ `Details: ${message}`,
18
+ ].join('\n');
19
+ }
20
+ export default class AppLogs extends Command {
21
+ static hidden = false;
22
+ static description = 'Show NocoBase logs for the selected env. Local npm/git installs use pm2 logs, and Docker installs use docker logs.';
23
+ static examples = [
24
+ '<%= config.bin %> <%= command.id %>',
25
+ '<%= config.bin %> <%= command.id %> --env app1',
26
+ '<%= config.bin %> <%= command.id %> --env app1 --tail 200',
27
+ '<%= config.bin %> <%= command.id %> --env app1 --no-follow',
28
+ ];
29
+ static flags = {
30
+ env: Flags.string({
31
+ char: 'e',
32
+ description: 'CLI env name to inspect logs for. Defaults to the current env when omitted',
33
+ }),
34
+ tail: Flags.integer({
35
+ description: 'Number of recent log lines to show before following',
36
+ default: 100,
37
+ min: 0,
38
+ }),
39
+ follow: Flags.boolean({
40
+ char: 'f',
41
+ description: 'Keep streaming new log lines',
42
+ default: false,
43
+ allowNo: true,
44
+ }),
45
+ };
46
+ async run() {
47
+ const { flags } = await this.parse(AppLogs);
48
+ const requestedEnv = flags.env?.trim() || undefined;
49
+ const runtime = await resolveManagedAppRuntime(requestedEnv);
50
+ if (!runtime) {
51
+ this.error(formatMissingManagedAppEnvMessage(requestedEnv));
52
+ }
53
+ if (runtime.kind === 'http') {
54
+ this.error([
55
+ `Can't show runtime logs for "${runtime.envName}" from this machine.`,
56
+ 'This env only has an API connection, so there is no saved local app or Docker container to read logs from.',
57
+ 'Connect it to a local checkout or reinstall it with npm, git, or Docker if you want CLI-managed logs.',
58
+ ].join('\n'));
59
+ }
60
+ if (runtime.kind === 'ssh') {
61
+ this.error([
62
+ `Can't show runtime logs for "${runtime.envName}" yet.`,
63
+ 'SSH env support is reserved but not implemented yet.',
64
+ 'Use a local or Docker env for CLI-managed logs for now.',
65
+ ].join('\n'));
66
+ }
67
+ const tail = String(flags.tail ?? 100);
68
+ const follow = flags.follow === true;
69
+ printInfo(follow
70
+ ? `Showing logs for "${runtime.envName}" (press Ctrl+C to stop).`
71
+ : `Showing recent logs for "${runtime.envName}".`);
72
+ try {
73
+ if (runtime.kind === 'docker') {
74
+ const dockerArgs = ['logs', '--tail', tail];
75
+ if (follow) {
76
+ dockerArgs.push('--follow');
77
+ }
78
+ dockerArgs.push(runtime.containerName);
79
+ await run('docker', dockerArgs, {
80
+ errorName: 'docker logs',
81
+ stdio: 'inherit',
82
+ });
83
+ return;
84
+ }
85
+ const localArgs = ['pm2', 'logs', '--lines', tail];
86
+ if (!follow) {
87
+ localArgs.push('--nostream');
88
+ }
89
+ await runLocalNocoBaseCommand(runtime, localArgs, {
90
+ stdio: 'inherit',
91
+ });
92
+ }
93
+ catch (error) {
94
+ const message = error instanceof Error ? error.message : String(error);
95
+ this.error(formatLogsFailure(runtime.envName, message));
96
+ }
97
+ }
98
+ }
@@ -0,0 +1,75 @@
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
+ function argvHasToken(argv, tokens) {
11
+ return tokens.some((token) => argv.includes(token));
12
+ }
13
+ function pushFlag(argv, flag, value) {
14
+ if (value !== undefined) {
15
+ argv.push(flag, String(value));
16
+ }
17
+ }
18
+ export default class AppRestart extends Command {
19
+ static hidden = false;
20
+ static description = 'Restart NocoBase for the selected env by stopping it first, then starting it again.';
21
+ static examples = [
22
+ '<%= config.bin %> <%= command.id %>',
23
+ '<%= config.bin %> <%= command.id %> --env local',
24
+ '<%= config.bin %> <%= command.id %> --env local --quickstart',
25
+ '<%= config.bin %> <%= command.id %> --env local --port 12000',
26
+ '<%= config.bin %> <%= command.id %> --env local --daemon',
27
+ '<%= config.bin %> <%= command.id %> --env local --no-daemon',
28
+ '<%= config.bin %> <%= command.id %> --env local --instances 2',
29
+ '<%= config.bin %> <%= command.id %> --env local --launch-mode pm2',
30
+ '<%= config.bin %> <%= command.id %> --env local --verbose',
31
+ '<%= config.bin %> <%= command.id %> --env local-docker',
32
+ ];
33
+ static flags = {
34
+ env: Flags.string({
35
+ char: 'e',
36
+ description: 'CLI env name to restart. Defaults to the current env when omitted',
37
+ }),
38
+ quickstart: Flags.boolean({ description: 'Quickstart the application after stopping it', required: false }),
39
+ port: Flags.string({ description: 'Port (overrides appPort from env config when set)', char: 'p', required: false }),
40
+ daemon: Flags.boolean({
41
+ description: 'Run the application as a daemon after stopping it (default: true; use --no-daemon to stay in the foreground)',
42
+ char: 'd',
43
+ required: false,
44
+ default: true,
45
+ allowNo: true,
46
+ }),
47
+ instances: Flags.integer({ description: 'Number of instances to run after stopping it', char: 'i', required: false }),
48
+ 'launch-mode': Flags.string({ description: 'Launch Mode', required: false, options: ['pm2', 'node'] }),
49
+ verbose: Flags.boolean({
50
+ description: 'Show raw shutdown/startup output from the underlying local or Docker command',
51
+ default: false,
52
+ }),
53
+ };
54
+ async run() {
55
+ const { flags } = await this.parse(AppRestart);
56
+ const stopArgv = [];
57
+ const daemonFlagWasProvided = argvHasToken(this.argv, ['--daemon', '--no-daemon']);
58
+ pushFlag(stopArgv, '--env', flags.env?.trim() || undefined);
59
+ if (flags.verbose) {
60
+ stopArgv.push('--verbose');
61
+ }
62
+ await this.config.runCommand('app:stop', stopArgv);
63
+ const startArgv = [...stopArgv];
64
+ if (flags.quickstart) {
65
+ startArgv.push('--quickstart');
66
+ }
67
+ pushFlag(startArgv, '--port', flags.port);
68
+ if (daemonFlagWasProvided) {
69
+ startArgv.push(flags.daemon === false ? '--no-daemon' : '--daemon');
70
+ }
71
+ pushFlag(startArgv, '--instances', flags.instances);
72
+ pushFlag(startArgv, '--launch-mode', flags['launch-mode']);
73
+ await this.config.runCommand('app:start', startArgv);
74
+ }
75
+ }