@nocobase/cli 2.1.0-alpha.4 → 2.1.0-alpha.45

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 (214) hide show
  1. package/LICENSE.txt +107 -0
  2. package/README.md +405 -19
  3. package/README.zh-CN.md +355 -0
  4. package/bin/run.cmd +3 -0
  5. package/bin/run.js +136 -0
  6. package/bin/session-env.js +39 -0
  7. package/dist/commands/api/resource/create.js +15 -0
  8. package/dist/commands/api/resource/destroy.js +15 -0
  9. package/dist/commands/api/resource/get.js +15 -0
  10. package/dist/commands/api/resource/index.js +20 -0
  11. package/dist/commands/api/resource/list.js +16 -0
  12. package/dist/commands/api/resource/query.js +15 -0
  13. package/dist/commands/api/resource/update.js +15 -0
  14. package/dist/commands/app/destroy.js +225 -0
  15. package/dist/commands/app/down.js +71 -0
  16. package/dist/commands/app/logs.js +114 -0
  17. package/dist/commands/app/restart.js +158 -0
  18. package/dist/commands/app/shared.js +122 -0
  19. package/dist/commands/app/start.js +286 -0
  20. package/dist/commands/app/stop.js +161 -0
  21. package/dist/commands/app/upgrade.js +519 -0
  22. package/dist/commands/backup/create.js +147 -0
  23. package/dist/commands/backup/index.js +20 -0
  24. package/dist/commands/backup/restore.js +105 -0
  25. package/{src/cli.js → dist/commands/build.js} +4 -11
  26. package/dist/commands/config/delete.js +34 -0
  27. package/dist/commands/config/get.js +33 -0
  28. package/dist/commands/config/index.js +20 -0
  29. package/dist/commands/config/list.js +29 -0
  30. package/dist/commands/config/set.js +39 -0
  31. package/dist/commands/db/check.js +240 -0
  32. package/dist/commands/db/logs.js +85 -0
  33. package/dist/commands/db/ps.js +60 -0
  34. package/dist/commands/db/shared.js +96 -0
  35. package/dist/commands/db/start.js +86 -0
  36. package/dist/commands/db/stop.js +71 -0
  37. package/{templates/plugin/src/client/models/index.ts → dist/commands/dev.js} +4 -4
  38. package/{src/commands/locale/react-js-cron/index.js → dist/commands/down.js} +3 -8
  39. package/dist/commands/download.js +13 -0
  40. package/dist/commands/env/add.js +426 -0
  41. package/dist/commands/env/auth.js +189 -0
  42. package/dist/commands/env/current.js +21 -0
  43. package/dist/commands/env/info.js +201 -0
  44. package/dist/commands/env/list.js +44 -0
  45. package/dist/commands/env/remove.js +84 -0
  46. package/dist/commands/env/shared.js +196 -0
  47. package/dist/commands/env/status.js +90 -0
  48. package/dist/commands/env/update.js +74 -0
  49. package/dist/commands/env/use.js +38 -0
  50. package/dist/commands/examples/prompts-stages.js +150 -0
  51. package/dist/commands/examples/prompts-test.js +181 -0
  52. package/dist/commands/init.js +1152 -0
  53. package/dist/commands/install.js +2424 -0
  54. package/dist/commands/license/activate.js +360 -0
  55. package/dist/commands/license/env.js +94 -0
  56. package/dist/commands/license/generate-id.js +108 -0
  57. package/dist/commands/license/id.js +70 -0
  58. package/dist/commands/license/index.js +20 -0
  59. package/dist/commands/license/plugins/clean.js +115 -0
  60. package/dist/commands/license/plugins/index.js +20 -0
  61. package/dist/commands/license/plugins/list.js +64 -0
  62. package/dist/commands/license/plugins/shared.js +331 -0
  63. package/dist/commands/license/plugins/sync.js +314 -0
  64. package/dist/commands/license/shared.js +423 -0
  65. package/dist/commands/license/status.js +64 -0
  66. package/dist/commands/logs.js +12 -0
  67. package/dist/commands/plugin/disable.js +86 -0
  68. package/dist/commands/plugin/enable.js +86 -0
  69. package/dist/commands/plugin/list.js +82 -0
  70. package/dist/commands/pm/disable.js +12 -0
  71. package/dist/commands/pm/enable.js +12 -0
  72. package/dist/commands/pm/list.js +12 -0
  73. package/dist/commands/restart.js +12 -0
  74. package/dist/commands/scaffold/migration.js +38 -0
  75. package/dist/commands/scaffold/plugin.js +37 -0
  76. package/dist/commands/self/check.js +71 -0
  77. package/dist/commands/self/index.js +20 -0
  78. package/dist/commands/self/update.js +95 -0
  79. package/dist/commands/session/id.js +24 -0
  80. package/dist/commands/session/remove.js +57 -0
  81. package/dist/commands/session/setup.js +62 -0
  82. package/dist/commands/skills/check.js +69 -0
  83. package/dist/commands/skills/index.js +20 -0
  84. package/dist/commands/skills/install.js +80 -0
  85. package/dist/commands/skills/remove.js +80 -0
  86. package/dist/commands/skills/update.js +87 -0
  87. package/dist/commands/source/build.js +58 -0
  88. package/dist/commands/source/dev.js +182 -0
  89. package/dist/commands/source/download.js +884 -0
  90. package/dist/commands/source/publish.js +109 -0
  91. package/dist/commands/source/registry/logs.js +70 -0
  92. package/dist/commands/source/registry/start.js +57 -0
  93. package/dist/commands/source/registry/status.js +33 -0
  94. package/dist/commands/source/registry/stop.js +48 -0
  95. package/dist/commands/source/test.js +477 -0
  96. package/dist/commands/start.js +12 -0
  97. package/dist/commands/stop.js +12 -0
  98. package/dist/commands/test.js +12 -0
  99. package/dist/commands/upgrade.js +12 -0
  100. package/dist/commands/v1.js +210 -0
  101. package/dist/generated/command-registry.js +134 -0
  102. package/dist/help/runtime-help.js +23 -0
  103. package/dist/lib/api-client.js +335 -0
  104. package/dist/lib/api-command-compat.js +641 -0
  105. package/dist/lib/app-health.js +132 -0
  106. package/dist/lib/app-managed-resources.js +316 -0
  107. package/dist/lib/app-runtime.js +180 -0
  108. package/dist/lib/auth-store.js +396 -0
  109. package/dist/lib/backup.js +171 -0
  110. package/dist/lib/bootstrap.js +403 -0
  111. package/dist/lib/build-config.js +18 -0
  112. package/dist/lib/builtin-db.js +86 -0
  113. package/dist/lib/cli-config.js +271 -0
  114. package/dist/lib/cli-home.js +47 -0
  115. package/dist/lib/cli-locale.js +141 -0
  116. package/dist/lib/command-discovery.js +39 -0
  117. package/dist/lib/db-connection-check.js +219 -0
  118. package/dist/lib/docker-env-file.js +52 -0
  119. package/dist/lib/docker-image.js +37 -0
  120. package/dist/lib/env-auth.js +952 -0
  121. package/dist/lib/env-config.js +95 -0
  122. package/dist/lib/env-guard.js +62 -0
  123. package/dist/lib/generated-command.js +203 -0
  124. package/dist/lib/http-request.js +49 -0
  125. package/dist/lib/inquirer-theme.js +17 -0
  126. package/dist/lib/inquirer.js +244 -0
  127. package/dist/lib/naming.js +70 -0
  128. package/dist/lib/object-utils.js +76 -0
  129. package/dist/lib/openapi.js +62 -0
  130. package/dist/lib/plugin-storage.js +64 -0
  131. package/dist/lib/post-processors.js +23 -0
  132. package/dist/lib/prompt-catalog-core.js +185 -0
  133. package/dist/lib/prompt-catalog-terminal.js +375 -0
  134. package/{src/index.js → dist/lib/prompt-catalog.js} +2 -6
  135. package/dist/lib/prompt-validators.js +258 -0
  136. package/dist/lib/prompt-web-ui.js +2227 -0
  137. package/dist/lib/resource-command.js +357 -0
  138. package/dist/lib/resource-request.js +104 -0
  139. package/dist/lib/run-npm.js +385 -0
  140. package/dist/lib/runtime-env-vars.js +32 -0
  141. package/dist/lib/runtime-generator.js +498 -0
  142. package/dist/lib/runtime-store.js +56 -0
  143. package/dist/lib/self-manager.js +301 -0
  144. package/dist/lib/session-id.js +17 -0
  145. package/dist/lib/session-integration.js +703 -0
  146. package/dist/lib/session-store.js +118 -0
  147. package/dist/lib/skills-manager.js +436 -0
  148. package/dist/lib/source-publish.js +306 -0
  149. package/dist/lib/source-registry.js +188 -0
  150. package/dist/lib/startup-update.js +285 -0
  151. package/dist/lib/ui.js +158 -0
  152. package/dist/locale/en-US.json +425 -0
  153. package/dist/locale/zh-CN.json +425 -0
  154. package/dist/post-processors/data-modeling.js +84 -0
  155. package/dist/post-processors/data-source-manager.js +138 -0
  156. package/dist/post-processors/index.js +19 -0
  157. package/nocobase-ctl.config.json +388 -0
  158. package/package.json +132 -26
  159. package/LICENSE +0 -661
  160. package/bin/index.js +0 -39
  161. package/nocobase.conf.tpl +0 -95
  162. package/src/commands/benchmark.js +0 -73
  163. package/src/commands/build.js +0 -49
  164. package/src/commands/clean.js +0 -30
  165. package/src/commands/client.js +0 -166
  166. package/src/commands/create-nginx-conf.js +0 -37
  167. package/src/commands/create-plugin.js +0 -33
  168. package/src/commands/dev.js +0 -200
  169. package/src/commands/doc.js +0 -76
  170. package/src/commands/e2e.js +0 -265
  171. package/src/commands/global.js +0 -43
  172. package/src/commands/index.js +0 -45
  173. package/src/commands/instance-id.js +0 -47
  174. package/src/commands/locale/cronstrue.js +0 -122
  175. package/src/commands/locale/react-js-cron/en-US.json +0 -75
  176. package/src/commands/locale/react-js-cron/zh-CN.json +0 -33
  177. package/src/commands/locale/react-js-cron/zh-TW.json +0 -33
  178. package/src/commands/locale.js +0 -81
  179. package/src/commands/p-test.js +0 -88
  180. package/src/commands/perf.js +0 -63
  181. package/src/commands/pkg.js +0 -321
  182. package/src/commands/pm2.js +0 -37
  183. package/src/commands/postinstall.js +0 -88
  184. package/src/commands/start.js +0 -148
  185. package/src/commands/tar.js +0 -36
  186. package/src/commands/test-coverage.js +0 -55
  187. package/src/commands/test.js +0 -107
  188. package/src/commands/umi.js +0 -33
  189. package/src/commands/update-deps.js +0 -72
  190. package/src/commands/upgrade.js +0 -47
  191. package/src/commands/view-license-key.js +0 -44
  192. package/src/license.js +0 -76
  193. package/src/logger.js +0 -75
  194. package/src/plugin-generator.js +0 -80
  195. package/src/util.js +0 -517
  196. package/templates/bundle-status.html +0 -338
  197. package/templates/create-app-package.json +0 -39
  198. package/templates/plugin/.npmignore.tpl +0 -2
  199. package/templates/plugin/README.md.tpl +0 -1
  200. package/templates/plugin/client.d.ts +0 -2
  201. package/templates/plugin/client.js +0 -1
  202. package/templates/plugin/package.json.tpl +0 -11
  203. package/templates/plugin/server.d.ts +0 -2
  204. package/templates/plugin/server.js +0 -1
  205. package/templates/plugin/src/client/client.d.ts +0 -249
  206. package/templates/plugin/src/client/index.tsx.tpl +0 -1
  207. package/templates/plugin/src/client/locale.ts +0 -21
  208. package/templates/plugin/src/client/plugin.tsx.tpl +0 -10
  209. package/templates/plugin/src/index.ts +0 -2
  210. package/templates/plugin/src/locale/en-US.json +0 -1
  211. package/templates/plugin/src/locale/zh-CN.json +0 -1
  212. package/templates/plugin/src/server/collections/.gitkeep +0 -0
  213. package/templates/plugin/src/server/index.ts.tpl +0 -1
  214. package/templates/plugin/src/server/plugin.ts.tpl +0 -19
@@ -0,0 +1,225 @@
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 { removeEnv } from '../../lib/auth-store.js';
11
+ import { formatMissingManagedAppEnvMessage, resolveManagedAppRuntime, runLocalNocoBaseCommand, } from '../../lib/app-runtime.js';
12
+ import { hasExplicitEnvSelection } from '../../lib/env-guard.js';
13
+ import { input } from "../../lib/inquirer.js";
14
+ import { announceTargetEnv, failTask, isInteractiveTerminal, printInfo, startTask, succeedTask } from '../../lib/ui.js';
15
+ import { builtinDbContainerName, managedDockerNetworkName, removeDockerContainerIfExists, removeDockerNetworkIfUnused, removePathIfExists, resolveConfiguredPath, resolveManagedLocalAppPath, shouldRemoveManagedLocalAppFiles, } from './shared.js';
16
+ function formatDestroyFailure(envName, message) {
17
+ return [
18
+ `Couldn't destroy env "${envName}".`,
19
+ 'Some managed local resources may still exist. Check Docker, local app files, and storage data, then try again.',
20
+ `Details: ${message}`,
21
+ ].join('\n');
22
+ }
23
+ function formatDestroyForceRequiredMessage(envName, explicitEnv) {
24
+ if (!explicitEnv) {
25
+ return [
26
+ `Refusing to destroy current env "${envName}" without explicit selection in non-interactive mode.`,
27
+ `Re-run with \`--env ${envName} --force\` to destroy this env.`,
28
+ ].join('\n');
29
+ }
30
+ return [
31
+ `Refusing to destroy env "${envName}" without confirmation in non-interactive mode.`,
32
+ `Re-run with \`--env ${envName} --force\` to destroy this env.`,
33
+ ].join('\n');
34
+ }
35
+ function buildDestroyPrompt(runtime, options) {
36
+ const subject = options.explicitEnv ? `env "${runtime.envName}"` : `current env "${runtime.envName}"`;
37
+ const lines = [`Destroy ${subject}?`];
38
+ if (runtime.kind === 'http' || runtime.kind === 'ssh') {
39
+ lines.push('This env has no CLI-managed local app or database runtime on this machine.');
40
+ lines.push('Only the saved CLI env config will be removed. External services are not touched.');
41
+ }
42
+ else {
43
+ lines.push('This removes managed app runtime resources.');
44
+ if (runtime.env.config.builtinDb) {
45
+ lines.push('CLI-managed built-in database runtime will also be removed.');
46
+ }
47
+ else {
48
+ lines.push('External database resources are not managed by the CLI and will be left untouched.');
49
+ }
50
+ if (options.removesManagedLocalAppFiles) {
51
+ lines.push('CLI-managed local app files will also be removed.');
52
+ }
53
+ else if (runtime.kind === 'local') {
54
+ lines.push('Custom local app source files will be kept.');
55
+ }
56
+ if (options.removesStorageData) {
57
+ lines.push('Storage data will be removed.');
58
+ }
59
+ else {
60
+ lines.push('No saved storage path was found for this env.');
61
+ }
62
+ lines.push('The saved CLI env config will be removed.');
63
+ }
64
+ lines.push(`Type "${runtime.envName}" to confirm:`);
65
+ return lines.join('\n');
66
+ }
67
+ async function confirmDestroy(runtime, options) {
68
+ if (!isInteractiveTerminal()) {
69
+ if (!options.explicitEnv) {
70
+ throw new Error(formatDestroyForceRequiredMessage(runtime.envName, false));
71
+ }
72
+ if (options.force) {
73
+ return true;
74
+ }
75
+ throw new Error(formatDestroyForceRequiredMessage(runtime.envName, options.explicitEnv));
76
+ }
77
+ if (options.force) {
78
+ return true;
79
+ }
80
+ try {
81
+ await input({
82
+ message: buildDestroyPrompt(runtime, options),
83
+ required: true,
84
+ validate: (value) => (value.trim() === runtime.envName ? true : `Type "${runtime.envName}" to confirm.`),
85
+ placeholder: runtime.envName,
86
+ });
87
+ return true;
88
+ }
89
+ catch {
90
+ return false;
91
+ }
92
+ }
93
+ export default class AppDestroy extends Command {
94
+ static hidden = false;
95
+ static description = 'Destroy the selected env by removing managed runtime resources, storage data, and the saved CLI env config.';
96
+ static examples = [
97
+ '<%= config.bin %> <%= command.id %> --env app1',
98
+ '<%= config.bin %> <%= command.id %> --env app1 --force',
99
+ ];
100
+ static flags = {
101
+ env: Flags.string({
102
+ char: 'e',
103
+ description: 'CLI env name to destroy. Defaults to the current env when omitted in interactive mode',
104
+ }),
105
+ force: Flags.boolean({
106
+ char: 'f',
107
+ description: 'Skip confirmation and destroy the selected env immediately',
108
+ default: false,
109
+ }),
110
+ verbose: Flags.boolean({
111
+ description: 'Show raw output from destruction commands',
112
+ default: false,
113
+ }),
114
+ };
115
+ async run() {
116
+ const { flags } = await this.parse(AppDestroy);
117
+ const requestedEnv = flags.env?.trim() || undefined;
118
+ const explicitEnv = Boolean(requestedEnv && hasExplicitEnvSelection(this.argv));
119
+ const runtime = await resolveManagedAppRuntime(requestedEnv);
120
+ if (!runtime) {
121
+ this.error(formatMissingManagedAppEnvMessage(requestedEnv));
122
+ }
123
+ const removesManagedLocalAppFiles = runtime.kind === 'local' &&
124
+ Boolean(resolveManagedLocalAppPath(runtime)) &&
125
+ shouldRemoveManagedLocalAppFiles(runtime);
126
+ const removesStorageData = (runtime.kind === 'local' || runtime.kind === 'docker') &&
127
+ Boolean(resolveConfiguredPath(runtime.env.config.storagePath));
128
+ let confirmed = false;
129
+ try {
130
+ confirmed = await confirmDestroy(runtime, {
131
+ explicitEnv,
132
+ force: flags.force,
133
+ removesManagedLocalAppFiles,
134
+ removesStorageData,
135
+ });
136
+ }
137
+ catch (error) {
138
+ this.error(error instanceof Error ? error.message : String(error));
139
+ }
140
+ if (!confirmed) {
141
+ return;
142
+ }
143
+ announceTargetEnv(runtime.envName);
144
+ try {
145
+ if (runtime.kind === 'docker') {
146
+ startTask(`Removing Docker app container for "${runtime.envName}"...`);
147
+ const state = await removeDockerContainerIfExists(runtime.containerName, {
148
+ stdio: flags.verbose ? 'inherit' : 'ignore',
149
+ });
150
+ succeedTask(state === 'removed'
151
+ ? `Docker app container removed for "${runtime.envName}".`
152
+ : `No Docker app container found for "${runtime.envName}".`);
153
+ }
154
+ else if (runtime.kind === 'local') {
155
+ startTask(`Stopping local NocoBase app for "${runtime.envName}"...`);
156
+ await runLocalNocoBaseCommand(runtime, ['pm2', 'kill'], {
157
+ stdio: flags.verbose ? 'inherit' : 'ignore',
158
+ });
159
+ succeedTask(`Local NocoBase app stopped for "${runtime.envName}".`);
160
+ }
161
+ if (runtime.kind === 'local' || runtime.kind === 'docker') {
162
+ const dbContainer = builtinDbContainerName(runtime);
163
+ if (dbContainer) {
164
+ startTask(`Removing built-in database container for "${runtime.envName}"...`);
165
+ const state = await removeDockerContainerIfExists(dbContainer, {
166
+ stdio: flags.verbose ? 'inherit' : 'ignore',
167
+ });
168
+ succeedTask(state === 'removed'
169
+ ? `Built-in database container removed for "${runtime.envName}".`
170
+ : `No built-in database container found for "${runtime.envName}".`);
171
+ }
172
+ else {
173
+ printInfo(`External database resources for "${runtime.envName}" were left untouched.`);
174
+ }
175
+ const networkName = managedDockerNetworkName(runtime);
176
+ if (networkName) {
177
+ startTask(`Removing Docker network for "${runtime.envName}" if unused...`);
178
+ const state = await removeDockerNetworkIfUnused(networkName);
179
+ if (state === 'removed') {
180
+ succeedTask(`Docker network removed for "${runtime.envName}".`);
181
+ }
182
+ else if (state === 'missing') {
183
+ succeedTask(`No Docker network found for "${runtime.envName}".`);
184
+ }
185
+ else {
186
+ succeedTask(`Docker network is still in use for "${runtime.envName}". Keeping it.`);
187
+ }
188
+ }
189
+ if (runtime.kind === 'local') {
190
+ const localAppPath = resolveManagedLocalAppPath(runtime);
191
+ if (localAppPath && removesManagedLocalAppFiles) {
192
+ startTask(`Removing managed local app files for "${runtime.envName}"...`);
193
+ await removePathIfExists(localAppPath, `managed app files for "${runtime.envName}"`);
194
+ succeedTask(`Managed local app files removed for "${runtime.envName}".`);
195
+ }
196
+ else if (localAppPath) {
197
+ printInfo(`Keeping custom local app files for "${runtime.envName}" at "${localAppPath}".`);
198
+ }
199
+ else {
200
+ printInfo(`No saved local app path found for "${runtime.envName}".`);
201
+ }
202
+ }
203
+ const configuredStoragePath = resolveConfiguredPath(runtime.env.config.storagePath);
204
+ if (configuredStoragePath) {
205
+ startTask(`Removing storage data for "${runtime.envName}"...`);
206
+ await removePathIfExists(configuredStoragePath, `storage data for "${runtime.envName}"`);
207
+ succeedTask(`Storage data removed for "${runtime.envName}".`);
208
+ }
209
+ else {
210
+ printInfo(`No saved storage path found for "${runtime.envName}".`);
211
+ }
212
+ }
213
+ else {
214
+ printInfo(`No CLI-managed local app or database runtime exists for "${runtime.envName}" on this machine.`);
215
+ }
216
+ startTask(`Removing saved CLI env config for "${runtime.envName}"...`);
217
+ const result = await removeEnv(runtime.envName);
218
+ succeedTask(`Saved CLI env config removed for "${runtime.envName}"${result.lastEnv ? ` (last env: ${result.lastEnv})` : ''}.`);
219
+ }
220
+ catch (error) {
221
+ failTask(`Failed to destroy env "${runtime.envName}".`);
222
+ this.error(formatDestroyFailure(runtime.envName, error instanceof Error ? error.message : String(error)));
223
+ }
224
+ }
225
+ }
@@ -0,0 +1,71 @@
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 { printWarning } from '../../lib/ui.js';
11
+ export default class AppDown extends Command {
12
+ static hidden = true;
13
+ static description = 'Deprecated compatibility alias for `nb app stop --with-db` and `nb app destroy`.';
14
+ static examples = [
15
+ '<%= config.bin %> <%= command.id %> --env app1',
16
+ '<%= config.bin %> <%= command.id %> --env app1 --all --force',
17
+ ];
18
+ static flags = {
19
+ env: Flags.string({
20
+ char: 'e',
21
+ description: 'CLI env name to bring down. Defaults to the current env when omitted',
22
+ }),
23
+ all: Flags.boolean({
24
+ description: 'Delete everything for this env, including storage data and the saved env config',
25
+ default: false,
26
+ }),
27
+ yes: Flags.boolean({
28
+ char: 'y',
29
+ description: 'Compatibility alias for confirmation flags on the replacement command',
30
+ default: false,
31
+ }),
32
+ force: Flags.boolean({
33
+ char: 'f',
34
+ description: 'Compatibility alias for confirmation flags on the replacement command',
35
+ default: false,
36
+ }),
37
+ verbose: Flags.boolean({
38
+ description: 'Show raw output from shutdown commands',
39
+ default: false,
40
+ }),
41
+ };
42
+ async run() {
43
+ const { flags } = await this.parse(AppDown);
44
+ const runCommand = this.config.runCommand.bind(this.config);
45
+ const envName = flags.env?.trim();
46
+ const argv = [];
47
+ if (envName) {
48
+ argv.push('--env', envName);
49
+ }
50
+ if (flags.verbose) {
51
+ argv.push('--verbose');
52
+ }
53
+ if (flags.all) {
54
+ printWarning('`nb app down --all` is deprecated. Use `nb app destroy` instead.');
55
+ if (flags.force || flags.yes) {
56
+ argv.push('--force');
57
+ }
58
+ await runCommand('app:destroy', argv);
59
+ return;
60
+ }
61
+ printWarning('`nb app down` is deprecated. Use `nb app stop --with-db` instead.');
62
+ argv.push('--with-db');
63
+ if (flags.yes) {
64
+ argv.push('--yes');
65
+ }
66
+ if (flags.force) {
67
+ argv.push('--force');
68
+ }
69
+ await runCommand('app:stop', argv);
70
+ }
71
+ }
@@ -0,0 +1,114 @@
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 { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
11
+ import { formatMissingManagedAppEnvMessage, resolveManagedAppRuntime, runLocalNocoBaseCommand, } from '../../lib/app-runtime.js';
12
+ import { run } from '../../lib/run-npm.js';
13
+ import { printInfo } from '../../lib/ui.js';
14
+ function formatLogsFailure(envName, message) {
15
+ return [
16
+ `Couldn't show logs for "${envName}".`,
17
+ 'Check that the saved local app or Docker container still exists on this machine, then try again.',
18
+ `Details: ${message}`,
19
+ ].join('\n');
20
+ }
21
+ export default class AppLogs extends Command {
22
+ static hidden = false;
23
+ static description = 'Show NocoBase logs for the selected env. Local npm/git installs use pm2 logs, and Docker installs use docker logs.';
24
+ static examples = [
25
+ '<%= config.bin %> <%= command.id %>',
26
+ '<%= config.bin %> <%= command.id %> --env app1',
27
+ '<%= config.bin %> <%= command.id %> --env app1 --tail 200',
28
+ '<%= config.bin %> <%= command.id %> --env app1 --no-follow',
29
+ ];
30
+ static flags = {
31
+ env: Flags.string({
32
+ char: 'e',
33
+ description: 'CLI env name to inspect logs for. Defaults to the current env when omitted',
34
+ }),
35
+ yes: Flags.boolean({
36
+ char: 'y',
37
+ description: 'Confirm using --env when it targets a different env than the current env',
38
+ default: false,
39
+ }),
40
+ tail: Flags.integer({
41
+ description: 'Number of recent log lines to show before following',
42
+ default: 100,
43
+ min: 0,
44
+ }),
45
+ follow: Flags.boolean({
46
+ char: 'f',
47
+ description: 'Keep streaming new log lines',
48
+ default: false,
49
+ allowNo: true,
50
+ }),
51
+ };
52
+ async run() {
53
+ const { flags } = await this.parse(AppLogs);
54
+ const requestedEnv = flags.env?.trim() || undefined;
55
+ if (requestedEnv && hasExplicitEnvSelection(this.argv)) {
56
+ const confirmed = await ensureCrossEnvConfirmed({
57
+ command: this,
58
+ requestedEnv,
59
+ yes: flags.yes,
60
+ });
61
+ if (!confirmed) {
62
+ return;
63
+ }
64
+ }
65
+ const runtime = await resolveManagedAppRuntime(requestedEnv);
66
+ if (!runtime) {
67
+ this.error(formatMissingManagedAppEnvMessage(requestedEnv));
68
+ }
69
+ if (runtime.kind === 'http') {
70
+ this.error([
71
+ `Can't show runtime logs for "${runtime.envName}" from this machine.`,
72
+ 'This env only has an API connection, so there is no saved local app or Docker container to read logs from.',
73
+ 'Connect it to a local checkout or reinstall it with npm, git, or Docker if you want CLI-managed logs.',
74
+ ].join('\n'));
75
+ }
76
+ if (runtime.kind === 'ssh') {
77
+ this.error([
78
+ `Can't show runtime logs for "${runtime.envName}" yet.`,
79
+ 'SSH env support is reserved but not implemented yet.',
80
+ 'Use a local or Docker env for CLI-managed logs for now.',
81
+ ].join('\n'));
82
+ }
83
+ const tail = String(flags.tail ?? 100);
84
+ const follow = flags.follow === true;
85
+ printInfo(follow
86
+ ? `Showing logs for "${runtime.envName}" (press Ctrl+C to stop).`
87
+ : `Showing recent logs for "${runtime.envName}".`);
88
+ try {
89
+ if (runtime.kind === 'docker') {
90
+ const dockerArgs = ['logs', '--tail', tail];
91
+ if (follow) {
92
+ dockerArgs.push('--follow');
93
+ }
94
+ dockerArgs.push(runtime.containerName);
95
+ await run('docker', dockerArgs, {
96
+ errorName: 'docker logs',
97
+ stdio: 'inherit',
98
+ });
99
+ return;
100
+ }
101
+ const localArgs = ['pm2', 'logs', '--lines', tail];
102
+ if (!follow) {
103
+ localArgs.push('--nostream');
104
+ }
105
+ await runLocalNocoBaseCommand(runtime, localArgs, {
106
+ stdio: 'inherit',
107
+ });
108
+ }
109
+ catch (error) {
110
+ const message = error instanceof Error ? error.message : String(error);
111
+ this.error(formatLogsFailure(runtime.envName, message));
112
+ }
113
+ }
114
+ }
@@ -0,0 +1,158 @@
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 { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
11
+ import { formatMissingManagedAppEnvMessage, resolveManagedAppRuntime, stopDockerContainer, } from '../../lib/app-runtime.js';
12
+ import { formatAppUrl, resolveManagedAppApiBaseUrl, waitForAppReady } from '../../lib/app-health.js';
13
+ import { recreateSavedDockerApp } from '../../lib/app-managed-resources.js';
14
+ import { run } from '../../lib/run-npm.js';
15
+ import { announceTargetEnv, failTask, startTask, succeedTask } from '../../lib/ui.js';
16
+ function argvHasToken(argv, tokens) {
17
+ return tokens.some((token) => argv.includes(token));
18
+ }
19
+ function pushFlag(argv, flag, value) {
20
+ if (value !== undefined) {
21
+ argv.push(flag, String(value));
22
+ }
23
+ }
24
+ function formatDockerRestartFailure(envName, message) {
25
+ return [
26
+ `Couldn't restart NocoBase for "${envName}".`,
27
+ 'The CLI was not able to recreate the saved Docker app container successfully.',
28
+ 'Check the saved Docker image, envFile, container settings, and database connection, then try again.',
29
+ `Details: ${message}`,
30
+ ].join('\n');
31
+ }
32
+ export default class AppRestart extends Command {
33
+ static hidden = false;
34
+ static description = 'Restart NocoBase for the selected env. Local npm/git installs stop and start the app again, and Docker installs recreate the saved app container so saved env changes can take effect.';
35
+ static examples = [
36
+ '<%= config.bin %> <%= command.id %>',
37
+ '<%= config.bin %> <%= command.id %> --env local',
38
+ '<%= config.bin %> <%= command.id %> --env local --quickstart',
39
+ '<%= config.bin %> <%= command.id %> --env local --port 12000',
40
+ '<%= config.bin %> <%= command.id %> --env local --daemon',
41
+ '<%= config.bin %> <%= command.id %> --env local --no-daemon',
42
+ '<%= config.bin %> <%= command.id %> --env local --instances 2',
43
+ '<%= config.bin %> <%= command.id %> --env local --launch-mode pm2',
44
+ '<%= config.bin %> <%= command.id %> --env local --verbose',
45
+ '<%= config.bin %> <%= command.id %> --env local-docker',
46
+ ];
47
+ static flags = {
48
+ env: Flags.string({
49
+ char: 'e',
50
+ description: 'CLI env name to restart. Defaults to the current env when omitted',
51
+ }),
52
+ yes: Flags.boolean({
53
+ char: 'y',
54
+ description: 'Confirm using --env when it targets a different env than the current env',
55
+ default: false,
56
+ }),
57
+ quickstart: Flags.boolean({ description: 'Quickstart the application after stopping it', required: false }),
58
+ port: Flags.string({ description: 'Port (overrides appPort from env config when set)', char: 'p', required: false }),
59
+ daemon: Flags.boolean({
60
+ description: 'Run the application as a daemon after stopping it (default: true; use --no-daemon to stay in the foreground)',
61
+ char: 'd',
62
+ required: false,
63
+ default: true,
64
+ allowNo: true,
65
+ }),
66
+ instances: Flags.integer({ description: 'Number of instances to run after stopping it', char: 'i', required: false }),
67
+ 'launch-mode': Flags.string({ description: 'Launch Mode', required: false, options: ['pm2', 'node'] }),
68
+ verbose: Flags.boolean({
69
+ description: 'Show raw shutdown/startup output from the underlying local or Docker command',
70
+ default: false,
71
+ }),
72
+ };
73
+ async run() {
74
+ const { flags } = await this.parse(AppRestart);
75
+ const requestedEnv = flags.env?.trim() || undefined;
76
+ const explicitEnvSelection = Boolean(requestedEnv && hasExplicitEnvSelection(this.argv));
77
+ if (explicitEnvSelection) {
78
+ const confirmed = await ensureCrossEnvConfirmed({
79
+ command: this,
80
+ requestedEnv,
81
+ yes: flags.yes,
82
+ });
83
+ if (!confirmed) {
84
+ return;
85
+ }
86
+ }
87
+ const runtime = await resolveManagedAppRuntime(requestedEnv);
88
+ const commandStdio = flags.verbose ? 'inherit' : 'ignore';
89
+ if (!runtime) {
90
+ this.error(formatMissingManagedAppEnvMessage(requestedEnv));
91
+ }
92
+ if (runtime.kind === 'docker') {
93
+ announceTargetEnv(runtime.envName);
94
+ startTask(`Stopping NocoBase for "${runtime.envName}" before restart...`);
95
+ try {
96
+ const state = await stopDockerContainer(runtime.containerName, {
97
+ stdio: commandStdio,
98
+ });
99
+ succeedTask(state === 'already-stopped'
100
+ ? `NocoBase was already stopped for "${runtime.envName}".`
101
+ : `Stopped NocoBase for "${runtime.envName}".`);
102
+ }
103
+ catch (error) {
104
+ const message = error instanceof Error ? error.message : String(error);
105
+ failTask(`Failed to stop NocoBase for "${runtime.envName}".`);
106
+ this.error(formatDockerRestartFailure(runtime.envName, message));
107
+ }
108
+ startTask(`Recreating the Docker app container for "${runtime.envName}"...`);
109
+ try {
110
+ await run('docker', ['rm', '-f', runtime.containerName], {
111
+ errorName: 'docker rm',
112
+ stdio: commandStdio,
113
+ }).catch(() => undefined);
114
+ await recreateSavedDockerApp(runtime, {
115
+ verbose: flags.verbose,
116
+ });
117
+ succeedTask(`Docker app container is ready for "${runtime.envName}".`);
118
+ }
119
+ catch (error) {
120
+ const message = error instanceof Error ? error.message : String(error);
121
+ failTask(`Failed to recreate NocoBase for "${runtime.envName}".`);
122
+ this.error(formatDockerRestartFailure(runtime.envName, message));
123
+ }
124
+ const appUrl = formatAppUrl(runtime.env.appPort === undefined || runtime.env.appPort === null
125
+ ? undefined
126
+ : String(runtime.env.appPort));
127
+ await waitForAppReady({
128
+ envName: runtime.envName,
129
+ apiBaseUrl: resolveManagedAppApiBaseUrl(runtime),
130
+ containerName: runtime.containerName,
131
+ logHint: `You can inspect startup logs with \`nb app logs --env ${runtime.envName}\`.`,
132
+ });
133
+ succeedTask(`NocoBase is running for "${runtime.envName}"${appUrl ? ` at ${appUrl}` : ''}.`);
134
+ return;
135
+ }
136
+ const stopArgv = [];
137
+ const daemonFlagWasProvided = argvHasToken(this.argv, ['--daemon', '--no-daemon']);
138
+ pushFlag(stopArgv, '--env', requestedEnv);
139
+ if (flags.yes || explicitEnvSelection) {
140
+ stopArgv.push('--yes');
141
+ }
142
+ if (flags.verbose) {
143
+ stopArgv.push('--verbose');
144
+ }
145
+ await this.config.runCommand('app:stop', stopArgv);
146
+ const startArgv = [...stopArgv];
147
+ if (flags.quickstart) {
148
+ startArgv.push('--quickstart');
149
+ }
150
+ pushFlag(startArgv, '--port', flags.port);
151
+ if (daemonFlagWasProvided) {
152
+ startArgv.push(flags.daemon === false ? '--no-daemon' : '--daemon');
153
+ }
154
+ pushFlag(startArgv, '--instances', flags.instances);
155
+ pushFlag(startArgv, '--launch-mode', flags['launch-mode']);
156
+ await this.config.runCommand('app:start', startArgv);
157
+ }
158
+ }