@nocobase/cli 2.1.0-beta.8 → 2.1.0

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 (263) hide show
  1. package/assets/env-proxy/nginx/app.conf.tpl +23 -0
  2. package/assets/env-proxy/nginx/nocobase.conf.tpl +5 -0
  3. package/assets/env-proxy/nginx/snippets/dist-location.conf +5 -0
  4. package/assets/env-proxy/nginx/snippets/gzip.conf +17 -0
  5. package/assets/env-proxy/nginx/snippets/log-format-http.conf +13 -0
  6. package/assets/env-proxy/nginx/snippets/maps-http.conf +14 -0
  7. package/assets/env-proxy/nginx/snippets/mime-types.conf +98 -0
  8. package/assets/env-proxy/nginx/snippets/proxy-location.conf +17 -0
  9. package/assets/env-proxy/nginx/snippets/spa-location.conf +6 -0
  10. package/assets/env-proxy/nginx/snippets/uploads-location.conf +21 -0
  11. package/bin/run.cmd +3 -0
  12. package/bin/run.js +145 -0
  13. package/bin/session-env.js +39 -0
  14. package/dist/commands/api/resource/create.js +15 -0
  15. package/dist/commands/api/resource/destroy.js +15 -0
  16. package/dist/commands/api/resource/get.js +15 -0
  17. package/dist/commands/api/resource/index.js +20 -0
  18. package/dist/commands/api/resource/list.js +16 -0
  19. package/dist/commands/api/resource/query.js +15 -0
  20. package/dist/commands/api/resource/update.js +15 -0
  21. package/dist/commands/app/autostart/disable.js +55 -0
  22. package/dist/commands/app/autostart/enable.js +55 -0
  23. package/dist/commands/app/autostart/list.js +37 -0
  24. package/dist/commands/app/autostart/run.js +84 -0
  25. package/dist/commands/app/autostart/shared.js +49 -0
  26. package/dist/commands/app/destroy.js +234 -0
  27. package/dist/commands/app/down.js +71 -0
  28. package/dist/commands/app/logs.js +115 -0
  29. package/dist/commands/app/restart.js +229 -0
  30. package/dist/commands/app/shared.js +123 -0
  31. package/dist/commands/app/start.js +416 -0
  32. package/dist/commands/app/stop.js +183 -0
  33. package/dist/commands/app/upgrade.js +523 -0
  34. package/dist/commands/backup/create.js +147 -0
  35. package/dist/commands/backup/index.js +20 -0
  36. package/dist/commands/backup/restore.js +105 -0
  37. package/{src/cli.js → dist/commands/build.js} +4 -11
  38. package/dist/commands/config/delete.js +42 -0
  39. package/dist/commands/config/get.js +39 -0
  40. package/dist/commands/config/index.js +20 -0
  41. package/dist/commands/config/list.js +29 -0
  42. package/dist/commands/config/set.js +49 -0
  43. package/dist/commands/db/check.js +240 -0
  44. package/dist/commands/db/logs.js +85 -0
  45. package/dist/commands/db/ps.js +47 -0
  46. package/dist/commands/db/shared.js +96 -0
  47. package/dist/commands/db/start.js +86 -0
  48. package/dist/commands/db/stop.js +71 -0
  49. package/{templates/plugin/src/client/models/index.ts → dist/commands/dev.js} +4 -4
  50. package/{src/commands/locale/react-js-cron/index.js → dist/commands/down.js} +3 -8
  51. package/dist/commands/download.js +13 -0
  52. package/dist/commands/env/add.js +406 -0
  53. package/dist/commands/env/auth.js +189 -0
  54. package/dist/commands/env/current.js +21 -0
  55. package/dist/commands/env/info.js +202 -0
  56. package/dist/commands/env/list.js +43 -0
  57. package/dist/commands/env/remove.js +174 -0
  58. package/dist/commands/env/shared.js +204 -0
  59. package/dist/commands/env/status.js +93 -0
  60. package/dist/commands/env/update.js +448 -0
  61. package/dist/commands/env/use.js +38 -0
  62. package/dist/commands/examples/prompts-stages.js +150 -0
  63. package/dist/commands/examples/prompts-test.js +181 -0
  64. package/dist/commands/init.js +1390 -0
  65. package/dist/commands/install.js +2609 -0
  66. package/dist/commands/license/activate.js +179 -0
  67. package/dist/commands/license/env.js +94 -0
  68. package/dist/commands/license/generate-id.js +108 -0
  69. package/dist/commands/license/id.js +70 -0
  70. package/dist/commands/license/index.js +20 -0
  71. package/dist/commands/license/plugins/clean.js +115 -0
  72. package/dist/commands/license/plugins/index.js +20 -0
  73. package/dist/commands/license/plugins/list.js +64 -0
  74. package/dist/commands/license/plugins/shared.js +382 -0
  75. package/dist/commands/license/plugins/sync.js +314 -0
  76. package/dist/commands/license/shared.js +423 -0
  77. package/dist/commands/license/status.js +64 -0
  78. package/dist/commands/logs.js +12 -0
  79. package/dist/commands/plugin/disable.js +86 -0
  80. package/dist/commands/plugin/enable.js +86 -0
  81. package/dist/commands/plugin/import.js +108 -0
  82. package/dist/commands/plugin/list.js +82 -0
  83. package/dist/commands/pm/disable.js +12 -0
  84. package/dist/commands/pm/enable.js +12 -0
  85. package/dist/commands/pm/list.js +12 -0
  86. package/dist/commands/proxy/caddy/current.js +17 -0
  87. package/dist/commands/proxy/caddy/generate.js +69 -0
  88. package/dist/commands/proxy/caddy/index.js +28 -0
  89. package/dist/commands/proxy/caddy/info.js +31 -0
  90. package/dist/commands/proxy/caddy/reload.js +30 -0
  91. package/dist/commands/proxy/caddy/restart.js +28 -0
  92. package/dist/commands/proxy/caddy/start.js +30 -0
  93. package/dist/commands/proxy/caddy/status.js +19 -0
  94. package/dist/commands/proxy/caddy/stop.js +30 -0
  95. package/dist/commands/proxy/caddy/use.js +26 -0
  96. package/dist/commands/proxy/index.js +28 -0
  97. package/dist/commands/proxy/nginx/current.js +18 -0
  98. package/dist/commands/proxy/nginx/generate.js +68 -0
  99. package/dist/commands/proxy/nginx/index.js +28 -0
  100. package/dist/commands/proxy/nginx/info.js +34 -0
  101. package/dist/commands/proxy/nginx/reload.js +30 -0
  102. package/dist/commands/proxy/nginx/restart.js +28 -0
  103. package/dist/commands/proxy/nginx/start.js +30 -0
  104. package/dist/commands/proxy/nginx/status.js +19 -0
  105. package/dist/commands/proxy/nginx/stop.js +30 -0
  106. package/dist/commands/proxy/nginx/use.js +31 -0
  107. package/dist/commands/restart.js +12 -0
  108. package/dist/commands/revision/create.js +118 -0
  109. package/dist/commands/scaffold/migration.js +38 -0
  110. package/dist/commands/scaffold/plugin.js +37 -0
  111. package/dist/commands/self/check.js +71 -0
  112. package/dist/commands/self/index.js +20 -0
  113. package/dist/commands/self/update.js +152 -0
  114. package/dist/commands/session/id.js +24 -0
  115. package/dist/commands/session/remove.js +57 -0
  116. package/dist/commands/session/setup.js +62 -0
  117. package/dist/commands/skills/check.js +69 -0
  118. package/dist/commands/skills/index.js +20 -0
  119. package/dist/commands/skills/install.js +80 -0
  120. package/dist/commands/skills/remove.js +80 -0
  121. package/dist/commands/skills/update.js +87 -0
  122. package/dist/commands/source/build.js +58 -0
  123. package/dist/commands/source/dev.js +182 -0
  124. package/dist/commands/source/download.js +884 -0
  125. package/dist/commands/source/publish.js +109 -0
  126. package/dist/commands/source/registry/logs.js +70 -0
  127. package/dist/commands/source/registry/start.js +57 -0
  128. package/dist/commands/source/registry/status.js +33 -0
  129. package/dist/commands/source/registry/stop.js +48 -0
  130. package/dist/commands/source/test.js +476 -0
  131. package/dist/commands/start.js +12 -0
  132. package/dist/commands/stop.js +12 -0
  133. package/dist/commands/test.js +12 -0
  134. package/dist/commands/upgrade.js +12 -0
  135. package/dist/commands/v1.js +210 -0
  136. package/dist/generated/command-registry.js +134 -0
  137. package/dist/help/runtime-help.js +23 -0
  138. package/dist/lib/api-client.js +335 -0
  139. package/dist/lib/api-command-compat.js +641 -0
  140. package/dist/lib/app-health.js +139 -0
  141. package/dist/lib/app-managed-resources.js +337 -0
  142. package/dist/lib/app-public-path.js +80 -0
  143. package/dist/lib/app-runtime.js +189 -0
  144. package/dist/lib/auth-store.js +528 -0
  145. package/dist/lib/backup.js +171 -0
  146. package/dist/lib/bootstrap.js +409 -0
  147. package/dist/lib/build-config.js +18 -0
  148. package/dist/lib/builtin-db.js +86 -0
  149. package/dist/lib/cli-config.js +569 -0
  150. package/dist/lib/cli-entry-error.js +52 -0
  151. package/dist/lib/cli-home.js +47 -0
  152. package/dist/lib/cli-locale.js +141 -0
  153. package/dist/lib/command-discovery.js +39 -0
  154. package/dist/lib/command-log.js +284 -0
  155. package/dist/lib/db-connection-check.js +219 -0
  156. package/dist/lib/docker-env-file.js +60 -0
  157. package/dist/lib/docker-image.js +37 -0
  158. package/dist/lib/docker-log-stream.js +45 -0
  159. package/dist/lib/env-auth.js +963 -0
  160. package/dist/lib/env-command-config.js +45 -0
  161. package/dist/lib/env-config.js +108 -0
  162. package/dist/lib/env-guard.js +61 -0
  163. package/dist/lib/env-paths.js +101 -0
  164. package/dist/lib/env-proxy.js +1325 -0
  165. package/dist/lib/generated-command.js +203 -0
  166. package/dist/lib/http-request.js +49 -0
  167. package/dist/lib/inquirer-theme.js +17 -0
  168. package/dist/lib/inquirer.js +243 -0
  169. package/dist/lib/managed-env-file.js +101 -0
  170. package/dist/lib/managed-init-env.js +32 -0
  171. package/dist/lib/naming.js +70 -0
  172. package/dist/lib/object-utils.js +76 -0
  173. package/dist/lib/openapi.js +62 -0
  174. package/dist/lib/plugin-import.js +279 -0
  175. package/dist/lib/plugin-storage.js +64 -0
  176. package/dist/lib/post-processors.js +23 -0
  177. package/dist/lib/prompt-catalog-core.js +186 -0
  178. package/dist/lib/prompt-catalog-terminal.js +374 -0
  179. package/{src/index.js → dist/lib/prompt-catalog.js} +2 -6
  180. package/dist/lib/prompt-validators.js +278 -0
  181. package/dist/lib/prompt-web-ui.js +2234 -0
  182. package/dist/lib/proxy-caddy.js +274 -0
  183. package/dist/lib/proxy-nginx.js +330 -0
  184. package/dist/lib/resource-command.js +357 -0
  185. package/dist/lib/resource-request.js +104 -0
  186. package/dist/lib/run-npm.js +429 -0
  187. package/dist/lib/runtime-env-vars.js +32 -0
  188. package/dist/lib/runtime-generator.js +498 -0
  189. package/dist/lib/runtime-store.js +56 -0
  190. package/dist/lib/self-manager.js +301 -0
  191. package/dist/lib/session-id.js +17 -0
  192. package/dist/lib/session-integration.js +703 -0
  193. package/dist/lib/session-store.js +118 -0
  194. package/dist/lib/skills-manager.js +438 -0
  195. package/dist/lib/source-publish.js +326 -0
  196. package/dist/lib/source-registry.js +188 -0
  197. package/dist/lib/startup-update.js +309 -0
  198. package/dist/lib/ui.js +159 -0
  199. package/dist/locale/en-US.json +526 -0
  200. package/dist/locale/zh-CN.json +526 -0
  201. package/dist/post-processors/data-modeling.js +84 -0
  202. package/dist/post-processors/data-source-manager.js +138 -0
  203. package/dist/post-processors/index.js +19 -0
  204. package/nocobase-ctl.config.json +388 -0
  205. package/package.json +128 -24
  206. package/scripts/build.mjs +34 -0
  207. package/scripts/clean.mjs +9 -0
  208. package/tsconfig.json +19 -0
  209. package/bin/index.js +0 -39
  210. package/nocobase.conf.tpl +0 -95
  211. package/src/commands/benchmark.js +0 -73
  212. package/src/commands/build.js +0 -49
  213. package/src/commands/clean.js +0 -30
  214. package/src/commands/client.js +0 -166
  215. package/src/commands/create-nginx-conf.js +0 -37
  216. package/src/commands/create-plugin.js +0 -33
  217. package/src/commands/dev.js +0 -200
  218. package/src/commands/doc.js +0 -76
  219. package/src/commands/e2e.js +0 -265
  220. package/src/commands/global.js +0 -43
  221. package/src/commands/index.js +0 -45
  222. package/src/commands/instance-id.js +0 -47
  223. package/src/commands/locale/cronstrue.js +0 -122
  224. package/src/commands/locale/react-js-cron/en-US.json +0 -75
  225. package/src/commands/locale/react-js-cron/zh-CN.json +0 -33
  226. package/src/commands/locale/react-js-cron/zh-TW.json +0 -33
  227. package/src/commands/locale.js +0 -81
  228. package/src/commands/p-test.js +0 -88
  229. package/src/commands/perf.js +0 -63
  230. package/src/commands/pkg.js +0 -321
  231. package/src/commands/pm2.js +0 -37
  232. package/src/commands/postinstall.js +0 -88
  233. package/src/commands/start.js +0 -148
  234. package/src/commands/tar.js +0 -36
  235. package/src/commands/test-coverage.js +0 -55
  236. package/src/commands/test.js +0 -107
  237. package/src/commands/umi.js +0 -33
  238. package/src/commands/update-deps.js +0 -72
  239. package/src/commands/upgrade.js +0 -47
  240. package/src/commands/view-license-key.js +0 -44
  241. package/src/license.js +0 -76
  242. package/src/logger.js +0 -75
  243. package/src/plugin-generator.js +0 -80
  244. package/src/util.js +0 -517
  245. package/templates/bundle-status.html +0 -338
  246. package/templates/create-app-package.json +0 -39
  247. package/templates/plugin/.npmignore.tpl +0 -2
  248. package/templates/plugin/README.md.tpl +0 -1
  249. package/templates/plugin/client.d.ts +0 -2
  250. package/templates/plugin/client.js +0 -1
  251. package/templates/plugin/package.json.tpl +0 -11
  252. package/templates/plugin/server.d.ts +0 -2
  253. package/templates/plugin/server.js +0 -1
  254. package/templates/plugin/src/client/client.d.ts +0 -249
  255. package/templates/plugin/src/client/index.tsx.tpl +0 -1
  256. package/templates/plugin/src/client/locale.ts +0 -21
  257. package/templates/plugin/src/client/plugin.tsx.tpl +0 -10
  258. package/templates/plugin/src/index.ts +0 -2
  259. package/templates/plugin/src/locale/en-US.json +0 -1
  260. package/templates/plugin/src/locale/zh-CN.json +0 -1
  261. package/templates/plugin/src/server/collections/.gitkeep +0 -0
  262. package/templates/plugin/src/server/index.ts.tpl +0 -1
  263. package/templates/plugin/src/server/plugin.ts.tpl +0 -19
@@ -0,0 +1,423 @@
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 { Flags } from '@oclif/core';
10
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
11
+ import path from 'node:path';
12
+ import { getEnvAsync, getInstanceIdAsync, keyDecrypt } from '@nocobase/license-kit';
13
+ import { checkExternalDbConnection, readExternalDbConnectionConfig, } from "../../lib/db-connection-check.js";
14
+ import { DEFAULT_DOCKER_REGISTRY, DEFAULT_DOCKER_VERSION, resolveDockerImageRef, } from "../../lib/docker-image.js";
15
+ import { formatMissingManagedAppEnvMessage, resolveManagedAppRuntime } from '../../lib/app-runtime.js';
16
+ import { buildRuntimeEnvVars } from '../../lib/runtime-env-vars.js';
17
+ import { resolveLicensePkgUrlFromConfig } from '../../lib/cli-config.js';
18
+ import { deepEqual, omitKeys } from "../../lib/object-utils.js";
19
+ import { commandOutput } from '../../lib/run-npm.js';
20
+ import { appUrl } from '../env/shared.js';
21
+ export function createLicenseEnvFlag(description) {
22
+ return Flags.string({
23
+ char: 'e',
24
+ description,
25
+ });
26
+ }
27
+ export const licenseYesFlag = Flags.boolean({
28
+ char: 'y',
29
+ description: 'Confirm using --env when it targets a different env than the current env',
30
+ default: false,
31
+ });
32
+ export const licenseJsonFlag = Flags.boolean({
33
+ description: 'Output the result as JSON',
34
+ default: false,
35
+ });
36
+ const DEFAULT_LICENSE_PKG_URL = 'https://pkg.nocobase.com/';
37
+ export const licensePkgUrlFlag = Flags.string({
38
+ description: 'Commercial package service base URL',
39
+ hidden: true,
40
+ });
41
+ export async function requireLicenseRuntime(envName) {
42
+ const runtime = await resolveManagedAppRuntime(envName);
43
+ if (!runtime) {
44
+ throw new Error(formatMissingManagedAppEnvMessage(envName));
45
+ }
46
+ return runtime;
47
+ }
48
+ export function resolveLicenseDir(runtime) {
49
+ return path.resolve(runtime.env.storagePath, '.license');
50
+ }
51
+ export function resolveInstanceIdFile(runtime) {
52
+ return path.resolve(resolveLicenseDir(runtime), 'instance-id');
53
+ }
54
+ export function resolveLicenseKeyFile(runtime) {
55
+ return path.resolve(resolveLicenseDir(runtime), 'license-key');
56
+ }
57
+ export async function readSavedInstanceId(runtime) {
58
+ try {
59
+ const value = await readFile(resolveInstanceIdFile(runtime), 'utf8');
60
+ const normalized = value.trim();
61
+ return normalized || undefined;
62
+ }
63
+ catch {
64
+ return undefined;
65
+ }
66
+ }
67
+ function trimValue(value) {
68
+ const text = String(value ?? '').trim();
69
+ return text || undefined;
70
+ }
71
+ function normalizeDockerPlatform(value) {
72
+ const text = trimValue(value);
73
+ if (!text || text === 'auto') {
74
+ return undefined;
75
+ }
76
+ if (text === 'linux/amd64' || text === 'linux/arm64') {
77
+ return text;
78
+ }
79
+ return undefined;
80
+ }
81
+ function resolveDockerLicenseImageRef(runtime) {
82
+ const config = runtime.env.config ?? {};
83
+ return resolveDockerImageRef(config.dockerRegistry, config.downloadVersion, {
84
+ defaultRegistry: DEFAULT_DOCKER_REGISTRY,
85
+ defaultVersion: DEFAULT_DOCKER_VERSION,
86
+ });
87
+ }
88
+ function buildDockerLicenseDbFlagArgs(envVars) {
89
+ return [
90
+ '--db-dialect',
91
+ String(envVars.DB_DIALECT ?? ''),
92
+ '--db-host',
93
+ String(envVars.DB_HOST ?? ''),
94
+ '--db-port',
95
+ String(envVars.DB_PORT ?? ''),
96
+ '--db-database',
97
+ String(envVars.DB_DATABASE ?? ''),
98
+ '--db-user',
99
+ String(envVars.DB_USER ?? ''),
100
+ '--db-password',
101
+ String(envVars.DB_PASSWORD ?? ''),
102
+ ];
103
+ }
104
+ async function runDockerLicenseJsonCommand(runtime, commandArgs) {
105
+ const args = [
106
+ 'run',
107
+ '--rm',
108
+ '--network',
109
+ runtime.dockerNetworkName || runtime.workspaceName,
110
+ ];
111
+ const dockerPlatform = normalizeDockerPlatform(runtime.env.config?.dockerPlatform);
112
+ if (dockerPlatform) {
113
+ args.push('--platform', dockerPlatform);
114
+ }
115
+ args.push('--entrypoint', 'nb', resolveDockerLicenseImageRef(runtime), ...commandArgs, '--json');
116
+ const output = await commandOutput('docker', args, {
117
+ errorName: 'docker run',
118
+ });
119
+ try {
120
+ return JSON.parse(output);
121
+ }
122
+ catch {
123
+ throw new Error(`Failed to parse Docker license command response: ${output}`);
124
+ }
125
+ }
126
+ function buildLicenseDbConfigFromEnvVars(envVars) {
127
+ return {
128
+ builtinDb: false,
129
+ dbDialect: trimValue(envVars.DB_DIALECT),
130
+ dbHost: trimValue(envVars.DB_HOST),
131
+ dbPort: trimValue(envVars.DB_PORT),
132
+ dbDatabase: trimValue(envVars.DB_DATABASE),
133
+ dbUser: trimValue(envVars.DB_USER),
134
+ dbPassword: envVars.DB_PASSWORD,
135
+ };
136
+ }
137
+ export async function validateLicenseDbConnectionFromEnvVars(envVars) {
138
+ const connectionConfig = readExternalDbConnectionConfig(buildLicenseDbConfigFromEnvVars(envVars));
139
+ if (!connectionConfig) {
140
+ throw new Error('Unsupported or incomplete database settings for instance ID generation.');
141
+ }
142
+ const validationError = await checkExternalDbConnection(connectionConfig);
143
+ if (validationError) {
144
+ throw new Error(validationError);
145
+ }
146
+ }
147
+ export async function withLicenseEnvVars(nextEnv, task) {
148
+ const previous = {};
149
+ for (const [key, value] of Object.entries(nextEnv)) {
150
+ previous[key] = process.env[key];
151
+ process.env[key] = value;
152
+ }
153
+ try {
154
+ return await task();
155
+ }
156
+ finally {
157
+ for (const key of Object.keys(nextEnv)) {
158
+ const value = previous[key];
159
+ if (value === undefined) {
160
+ delete process.env[key];
161
+ }
162
+ else {
163
+ process.env[key] = value;
164
+ }
165
+ }
166
+ }
167
+ }
168
+ async function withLicenseEnv(runtime, task) {
169
+ return await withLicenseEnvVars(await buildRuntimeEnvVars(runtime), task);
170
+ }
171
+ export async function getCurrentLicenseEnv(runtime) {
172
+ if (runtime.kind === 'docker') {
173
+ const envVars = await buildRuntimeEnvVars(runtime);
174
+ const payload = await runDockerLicenseJsonCommand(runtime, [
175
+ 'license',
176
+ 'env',
177
+ ...buildDockerLicenseDbFlagArgs(envVars),
178
+ ]);
179
+ return payload?.env;
180
+ }
181
+ return await withLicenseEnv(runtime, async () => await getEnvAsync());
182
+ }
183
+ export async function generateInstanceIdFromEnvVars(envVars) {
184
+ const instanceId = String(await withLicenseEnvVars(envVars, async () => await getInstanceIdAsync())).trim();
185
+ if (!instanceId) {
186
+ throw new Error('Generated instance ID is empty.');
187
+ }
188
+ return instanceId;
189
+ }
190
+ export async function generateValidatedInstanceIdFromEnvVars(envVars) {
191
+ await validateLicenseDbConnectionFromEnvVars(envVars);
192
+ return await generateInstanceIdFromEnvVars(envVars);
193
+ }
194
+ async function generateInstanceIdForDockerRuntime(runtime) {
195
+ const envVars = await buildRuntimeEnvVars(runtime);
196
+ const payload = await runDockerLicenseJsonCommand(runtime, [
197
+ 'license',
198
+ 'generate-id',
199
+ ...buildDockerLicenseDbFlagArgs(envVars),
200
+ ]);
201
+ const instanceId = trimValue(payload.instanceId);
202
+ if (!instanceId) {
203
+ throw new Error('Docker instance ID generation did not return an instance ID.');
204
+ }
205
+ return instanceId;
206
+ }
207
+ export async function generateInstanceIdForRuntime(runtime) {
208
+ if (runtime.kind === 'docker') {
209
+ return await generateInstanceIdForDockerRuntime(runtime);
210
+ }
211
+ if (runtime.kind === 'local') {
212
+ return await generateValidatedInstanceIdFromEnvVars(await buildRuntimeEnvVars(runtime));
213
+ }
214
+ throw new Error(`Env "${runtime.envName}" does not support automatic instance ID generation.`);
215
+ }
216
+ export async function saveInstanceId(runtime, instanceId) {
217
+ const normalized = String(instanceId ?? '').trim();
218
+ if (!normalized) {
219
+ throw new Error('Generated instance ID is empty.');
220
+ }
221
+ await mkdir(resolveLicenseDir(runtime), { recursive: true });
222
+ await writeFile(resolveInstanceIdFile(runtime), `${normalized}\n`);
223
+ return normalized;
224
+ }
225
+ export async function generateAndSaveInstanceId(runtime) {
226
+ const instanceId = await generateInstanceIdForRuntime(runtime);
227
+ return await saveInstanceId(runtime, instanceId);
228
+ }
229
+ export async function ensureInstanceId(runtime, options = {}) {
230
+ if (!options.force) {
231
+ const saved = await readSavedInstanceId(runtime);
232
+ if (saved) {
233
+ return saved;
234
+ }
235
+ }
236
+ return await generateAndSaveInstanceId(runtime);
237
+ }
238
+ export function parseLicenseKey(key) {
239
+ try {
240
+ return JSON.parse(keyDecrypt(key));
241
+ }
242
+ catch {
243
+ throw new Error('invalid');
244
+ }
245
+ }
246
+ export async function saveLicenseKey(runtime, key) {
247
+ await mkdir(resolveLicenseDir(runtime), { recursive: true });
248
+ const filePath = resolveLicenseKeyFile(runtime);
249
+ await writeFile(filePath, key.trim());
250
+ return filePath;
251
+ }
252
+ export async function readSavedLicenseKey(runtime) {
253
+ try {
254
+ const value = await readFile(resolveLicenseKeyFile(runtime), 'utf8');
255
+ const normalized = value.trim();
256
+ return normalized || undefined;
257
+ }
258
+ catch {
259
+ return undefined;
260
+ }
261
+ }
262
+ function matchSingleDomain(licenseDomain, currentDomain) {
263
+ let hostname = '';
264
+ let port = '';
265
+ try {
266
+ const url = new URL(currentDomain);
267
+ hostname = url.hostname;
268
+ port = url.port ? `:${url.port}` : '';
269
+ }
270
+ catch {
271
+ return false;
272
+ }
273
+ const fullDomain = hostname + port;
274
+ if (!licenseDomain.includes('*')) {
275
+ return fullDomain === licenseDomain;
276
+ }
277
+ const base = licenseDomain.replace('*', '');
278
+ return fullDomain.endsWith(base);
279
+ }
280
+ export function isDomainMatch(currentDomain, keyData) {
281
+ if (!keyData?.licenseKey?.domain || !currentDomain) {
282
+ return false;
283
+ }
284
+ const licenseDomains = String(keyData.licenseKey.domain)
285
+ .split(',')
286
+ .map((value) => value.trim())
287
+ .filter(Boolean);
288
+ return licenseDomains.some((licenseDomain) => matchSingleDomain(licenseDomain, currentDomain));
289
+ }
290
+ export function isDbMatch(env, keyData) {
291
+ const currentDb = env?.db;
292
+ const licenseDb = keyData?.instanceData?.db;
293
+ if (!currentDb || !licenseDb) {
294
+ return false;
295
+ }
296
+ if (currentDb?.id && licenseDb?.id) {
297
+ return currentDb.id === licenseDb.id;
298
+ }
299
+ return deepEqual(omitKeys(currentDb, ['id']), omitKeys(licenseDb, ['id']));
300
+ }
301
+ export function isSysMatch(env, keyData) {
302
+ const instance = keyData?.instanceData;
303
+ if (!env || !instance) {
304
+ return false;
305
+ }
306
+ const normalize = (item) => ({
307
+ sys: item?.sys ?? null,
308
+ osVer: item?.osVer ?? null,
309
+ });
310
+ return deepEqual(normalize(env), normalize(instance));
311
+ }
312
+ export async function getLicenseStatus(keyData) {
313
+ if (!keyData) {
314
+ return 'invalid';
315
+ }
316
+ if (keyData.licenseKey?.licenseStatus === 'invalid') {
317
+ return 'invalid';
318
+ }
319
+ const domain = String(keyData.service?.domain ?? '').trim();
320
+ const accessKeyId = String(keyData.accessKeyId ?? '').trim();
321
+ const accessKeySecret = String(keyData.accessKeySecret ?? '').trim();
322
+ if (!domain || !accessKeyId || !accessKeySecret) {
323
+ return 'active';
324
+ }
325
+ const controller = new AbortController();
326
+ const timeout = setTimeout(() => controller.abort(), 5000);
327
+ try {
328
+ const response = await fetch(`${domain.replace(/\/$/, '')}/api/license_keys:getKeyStatus`, {
329
+ method: 'POST',
330
+ headers: {
331
+ 'Content-Type': 'application/json',
332
+ ...(keyData.service?.headers ?? {}),
333
+ },
334
+ body: JSON.stringify({
335
+ access_key_id: accessKeyId,
336
+ access_key_secret: accessKeySecret,
337
+ }),
338
+ signal: controller.signal,
339
+ });
340
+ const payload = await response.json();
341
+ return payload?.data?.status === 'active' ? 'active' : 'invalid';
342
+ }
343
+ catch {
344
+ return 'active';
345
+ }
346
+ finally {
347
+ clearTimeout(timeout);
348
+ }
349
+ }
350
+ export async function validateLicenseKey(runtime, key) {
351
+ let keyData;
352
+ let keyStatus;
353
+ try {
354
+ keyData = parseLicenseKey(key);
355
+ }
356
+ catch {
357
+ keyStatus = 'invalid';
358
+ }
359
+ const currentEnv = await getCurrentLicenseEnv(runtime);
360
+ const currentDomain = appUrl(runtime);
361
+ const dbMatch = isDbMatch(currentEnv, keyData);
362
+ const sysMatch = isSysMatch(currentEnv, keyData);
363
+ const envMatch = dbMatch && sysMatch;
364
+ const domainMatch = isDomainMatch(currentDomain, keyData);
365
+ const licenseStatus = await getLicenseStatus(keyData);
366
+ return {
367
+ current: {
368
+ env: currentEnv,
369
+ domain: currentDomain ? new URL(currentDomain).host : '',
370
+ },
371
+ keyData,
372
+ keyStatus,
373
+ dbMatch,
374
+ sysMatch,
375
+ envMatch,
376
+ domainMatch,
377
+ licenseStatus,
378
+ };
379
+ }
380
+ export function redactLicenseKey(value) {
381
+ const text = String(value ?? '').trim();
382
+ if (!text) {
383
+ return '';
384
+ }
385
+ if (text.length <= 8) {
386
+ return '*'.repeat(text.length);
387
+ }
388
+ return `${text.slice(0, 4)}...${text.slice(-4)}`;
389
+ }
390
+ export async function resolveLicenseServiceUrl(value) {
391
+ return (await resolveLicensePkgUrl(value)).replace(/\/+$/, '');
392
+ }
393
+ export async function resolveLicensePkgUrl(value) {
394
+ const normalized = String(value ?? '').trim() || await resolveLicensePkgUrlFromConfig();
395
+ return normalized.replace(/\/+$/, '') + '/';
396
+ }
397
+ function shouldRedactOutputKey(key) {
398
+ return /accesskeyid|accesskeysecret|secret|token|password|authorization/i.test(key);
399
+ }
400
+ function redactOutputValue(value) {
401
+ const text = String(value ?? '').trim();
402
+ if (!text) {
403
+ return '';
404
+ }
405
+ if (text.length <= 8) {
406
+ return '*'.repeat(text.length);
407
+ }
408
+ return `${text.slice(0, 2)}***${text.slice(-2)}`;
409
+ }
410
+ export function sanitizeLicenseOutput(value) {
411
+ if (Array.isArray(value)) {
412
+ return value.map((item) => sanitizeLicenseOutput(item));
413
+ }
414
+ if (value && typeof value === 'object') {
415
+ return Object.fromEntries(Object.entries(value).map(([key, nestedValue]) => [
416
+ key,
417
+ shouldRedactOutputKey(key)
418
+ ? redactOutputValue(String(nestedValue ?? ''))
419
+ : sanitizeLicenseOutput(nestedValue),
420
+ ]));
421
+ }
422
+ return value;
423
+ }
@@ -0,0 +1,64 @@
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 { createLicenseEnvFlag, ensureInstanceId, licenseJsonFlag, licenseYesFlag, requireLicenseRuntime } from './shared.js';
12
+ export default class LicenseStatus extends Command {
13
+ static summary = 'Show commercial license status for the selected env';
14
+ static description = 'Inspect the selected env and show the current commercial licensing status. Use `--doctor` for extra diagnostic checks once the license backend wiring is implemented.';
15
+ static examples = [
16
+ '<%= config.bin %> <%= command.id %>',
17
+ '<%= config.bin %> <%= command.id %> --env app1',
18
+ '<%= config.bin %> <%= command.id %> --env app1 --doctor',
19
+ '<%= config.bin %> <%= command.id %> --env app1 --json',
20
+ ];
21
+ static flags = {
22
+ env: createLicenseEnvFlag('CLI env name to inspect. Defaults to the current env when omitted'),
23
+ json: licenseJsonFlag,
24
+ yes: licenseYesFlag,
25
+ doctor: Flags.boolean({
26
+ description: 'Run extra diagnostic checks and suggestions',
27
+ default: false,
28
+ }),
29
+ };
30
+ async run() {
31
+ const { flags } = await this.parse(LicenseStatus);
32
+ const requestedEnv = flags.env?.trim() || undefined;
33
+ const explicitEnvSelection = Boolean(requestedEnv && hasExplicitEnvSelection(this.argv ?? []));
34
+ if (explicitEnvSelection) {
35
+ const confirmed = await ensureCrossEnvConfirmed({
36
+ command: this,
37
+ requestedEnv,
38
+ yes: flags.yes,
39
+ });
40
+ if (!confirmed) {
41
+ return;
42
+ }
43
+ }
44
+ const runtime = await requireLicenseRuntime(flags.env);
45
+ const payload = {
46
+ ok: true,
47
+ env: runtime.envName,
48
+ kind: runtime.kind,
49
+ instanceId: await ensureInstanceId(runtime),
50
+ licensed: false,
51
+ doctor: Boolean(flags.doctor),
52
+ implemented: false,
53
+ message: 'Commercial license status is not implemented yet in the new CLI.',
54
+ };
55
+ if (flags.json) {
56
+ this.log(JSON.stringify(payload, null, 2));
57
+ return;
58
+ }
59
+ this.log(`License status for env "${runtime.envName}": not implemented yet`);
60
+ if (flags.doctor) {
61
+ this.log('Diagnostic checks for commercial licensing are not implemented yet in the new CLI.');
62
+ }
63
+ }
64
+ }
@@ -0,0 +1,12 @@
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 AppLogs from './app/logs.js';
10
+ export default class Logs extends AppLogs {
11
+ static hidden = true;
12
+ }
@@ -0,0 +1,86 @@
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 { formatMissingManagedAppEnvMessage, resolveManagedAppRuntime, runDockerNocoBaseCommand, runLocalNocoBaseCommand, } from '../../lib/app-runtime.js';
11
+ import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
12
+ import { announceTargetEnv } from '../../lib/ui.js';
13
+ export default class PluginDisable extends Command {
14
+ static hidden = false;
15
+ static args = {
16
+ packages: Args.string({
17
+ required: true,
18
+ multiple: true,
19
+ description: 'Plugin package name(s) to disable (e.g. `@nocobase/plugin-sample`). Pass one or more names as separate arguments.',
20
+ }),
21
+ };
22
+ static description = 'Disable one or more plugins in the selected env (npm/git runs locally, Docker runs inside the saved app container)';
23
+ static examples = [
24
+ '<%= config.bin %> <%= command.id %> @nocobase/plugin-sample',
25
+ '<%= config.bin %> <%= command.id %> @nocobase/plugin-a @nocobase/plugin-b',
26
+ '<%= config.bin %> <%= command.id %> -e local @nocobase/plugin-sample',
27
+ ];
28
+ static flags = {
29
+ env: Flags.string({
30
+ char: 'e',
31
+ description: 'CLI env name to disable plugins for. Defaults to the current env when omitted',
32
+ }),
33
+ yes: Flags.boolean({
34
+ char: 'y',
35
+ description: 'Confirm using --env when it targets a different env than the current env',
36
+ default: false,
37
+ }),
38
+ };
39
+ async run() {
40
+ const { args, flags } = await this.parse(PluginDisable);
41
+ const requestedEnv = flags.env?.trim() || undefined;
42
+ const explicitEnvSelection = Boolean(requestedEnv && hasExplicitEnvSelection(this.argv));
43
+ if (explicitEnvSelection) {
44
+ const confirmed = await ensureCrossEnvConfirmed({
45
+ command: this,
46
+ requestedEnv,
47
+ yes: flags.yes,
48
+ });
49
+ if (!confirmed) {
50
+ return;
51
+ }
52
+ }
53
+ const packages = args.packages;
54
+ if (!Array.isArray(packages) || packages.length === 0) {
55
+ this.error('Pass at least one plugin package name.');
56
+ }
57
+ const runtime = await resolveManagedAppRuntime(requestedEnv);
58
+ if (!runtime) {
59
+ this.error(formatMissingManagedAppEnvMessage(requestedEnv));
60
+ }
61
+ announceTargetEnv(runtime.envName);
62
+ if (runtime.kind === 'local') {
63
+ try {
64
+ await runLocalNocoBaseCommand(runtime, ['pm', 'disable', ...packages]);
65
+ }
66
+ catch (error) {
67
+ const message = error instanceof Error ? error.message : String(error);
68
+ this.error(message);
69
+ }
70
+ return;
71
+ }
72
+ if (runtime.kind === 'docker') {
73
+ try {
74
+ await runDockerNocoBaseCommand(runtime.containerName, ['pm', 'disable', ...packages]);
75
+ }
76
+ catch (error) {
77
+ const message = error instanceof Error ? error.message : String(error);
78
+ this.error(message);
79
+ }
80
+ return;
81
+ }
82
+ await this.config.runCommand('api:pm:disable', explicitEnvSelection
83
+ ? ['--await-response', '--filter-by-tk', packages.join(','), '--env', runtime.envName, '--yes']
84
+ : ['--await-response', '--filter-by-tk', packages.join(',')]);
85
+ }
86
+ }
@@ -0,0 +1,86 @@
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 { formatMissingManagedAppEnvMessage, resolveManagedAppRuntime, runDockerNocoBaseCommand, runLocalNocoBaseCommand, } from '../../lib/app-runtime.js';
11
+ import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
12
+ import { announceTargetEnv } from '../../lib/ui.js';
13
+ export default class PluginEnable extends Command {
14
+ static hidden = false;
15
+ static args = {
16
+ packages: Args.string({
17
+ required: true,
18
+ multiple: true,
19
+ description: 'Plugin package name(s) to enable (e.g. `@nocobase/plugin-sample`). Pass one or more names as separate arguments.',
20
+ }),
21
+ };
22
+ static description = 'Enable one or more plugins in the selected env (npm/git runs locally, Docker runs inside the saved app container)';
23
+ static examples = [
24
+ '<%= config.bin %> <%= command.id %> @nocobase/plugin-sample',
25
+ '<%= config.bin %> <%= command.id %> @nocobase/plugin-a @nocobase/plugin-b',
26
+ '<%= config.bin %> <%= command.id %> -e local @nocobase/plugin-sample',
27
+ ];
28
+ static flags = {
29
+ env: Flags.string({
30
+ char: 'e',
31
+ description: 'CLI env name to enable plugins for. Defaults to the current env when omitted',
32
+ }),
33
+ yes: Flags.boolean({
34
+ char: 'y',
35
+ description: 'Confirm using --env when it targets a different env than the current env',
36
+ default: false,
37
+ }),
38
+ };
39
+ async run() {
40
+ const { args, flags } = await this.parse(PluginEnable);
41
+ const requestedEnv = flags.env?.trim() || undefined;
42
+ const explicitEnvSelection = Boolean(requestedEnv && hasExplicitEnvSelection(this.argv));
43
+ if (explicitEnvSelection) {
44
+ const confirmed = await ensureCrossEnvConfirmed({
45
+ command: this,
46
+ requestedEnv,
47
+ yes: flags.yes,
48
+ });
49
+ if (!confirmed) {
50
+ return;
51
+ }
52
+ }
53
+ const packages = args.packages;
54
+ if (!Array.isArray(packages) || packages.length === 0) {
55
+ this.error('Pass at least one plugin package name.');
56
+ }
57
+ const runtime = await resolveManagedAppRuntime(requestedEnv);
58
+ if (!runtime) {
59
+ this.error(formatMissingManagedAppEnvMessage(requestedEnv));
60
+ }
61
+ announceTargetEnv(runtime.envName);
62
+ if (runtime.kind === 'local') {
63
+ try {
64
+ await runLocalNocoBaseCommand(runtime, ['pm', 'enable', ...packages]);
65
+ }
66
+ catch (error) {
67
+ const message = error instanceof Error ? error.message : String(error);
68
+ this.error(message);
69
+ }
70
+ return;
71
+ }
72
+ if (runtime.kind === 'docker') {
73
+ try {
74
+ await runDockerNocoBaseCommand(runtime.containerName, ['pm', 'enable', ...packages]);
75
+ }
76
+ catch (error) {
77
+ const message = error instanceof Error ? error.message : String(error);
78
+ this.error(message);
79
+ }
80
+ return;
81
+ }
82
+ await this.config.runCommand('api:pm:enable', explicitEnvSelection
83
+ ? ['--await-response', '--filter-by-tk', packages.join(','), '--env', runtime.envName, '--yes']
84
+ : ['--await-response', '--filter-by-tk', packages.join(',')]);
85
+ }
86
+ }