@nocobase/cli 2.1.0-beta.9 → 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.
- package/assets/env-proxy/nginx/app.conf.tpl +23 -0
- package/assets/env-proxy/nginx/nocobase.conf.tpl +5 -0
- package/assets/env-proxy/nginx/snippets/dist-location.conf +5 -0
- package/assets/env-proxy/nginx/snippets/gzip.conf +17 -0
- package/assets/env-proxy/nginx/snippets/log-format-http.conf +13 -0
- package/assets/env-proxy/nginx/snippets/maps-http.conf +14 -0
- package/assets/env-proxy/nginx/snippets/mime-types.conf +98 -0
- package/assets/env-proxy/nginx/snippets/proxy-location.conf +17 -0
- package/assets/env-proxy/nginx/snippets/spa-location.conf +6 -0
- package/assets/env-proxy/nginx/snippets/uploads-location.conf +21 -0
- package/bin/run.cmd +3 -0
- package/bin/run.js +145 -0
- package/bin/session-env.js +39 -0
- package/dist/commands/api/resource/create.js +15 -0
- package/dist/commands/api/resource/destroy.js +15 -0
- package/dist/commands/api/resource/get.js +15 -0
- package/dist/commands/api/resource/index.js +20 -0
- package/dist/commands/api/resource/list.js +16 -0
- package/dist/commands/api/resource/query.js +15 -0
- package/dist/commands/api/resource/update.js +15 -0
- package/dist/commands/app/autostart/disable.js +55 -0
- package/dist/commands/app/autostart/enable.js +55 -0
- package/dist/commands/app/autostart/list.js +37 -0
- package/dist/commands/app/autostart/run.js +84 -0
- package/dist/commands/app/autostart/shared.js +49 -0
- package/dist/commands/app/destroy.js +234 -0
- package/dist/commands/app/down.js +71 -0
- package/dist/commands/app/logs.js +115 -0
- package/dist/commands/app/restart.js +229 -0
- package/dist/commands/app/shared.js +123 -0
- package/dist/commands/app/start.js +416 -0
- package/dist/commands/app/stop.js +183 -0
- package/dist/commands/app/upgrade.js +523 -0
- package/dist/commands/backup/create.js +147 -0
- package/dist/commands/backup/index.js +20 -0
- package/dist/commands/backup/restore.js +105 -0
- package/{src/cli.js → dist/commands/build.js} +4 -11
- package/dist/commands/config/delete.js +42 -0
- package/dist/commands/config/get.js +39 -0
- package/dist/commands/config/index.js +20 -0
- package/dist/commands/config/list.js +29 -0
- package/dist/commands/config/set.js +49 -0
- package/dist/commands/db/check.js +240 -0
- package/dist/commands/db/logs.js +85 -0
- package/dist/commands/db/ps.js +47 -0
- package/dist/commands/db/shared.js +96 -0
- package/dist/commands/db/start.js +86 -0
- package/dist/commands/db/stop.js +71 -0
- package/{templates/plugin/src/client/models/index.ts → dist/commands/dev.js} +4 -4
- package/{src/commands/locale/react-js-cron/index.js → dist/commands/down.js} +3 -8
- package/dist/commands/download.js +13 -0
- package/dist/commands/env/add.js +406 -0
- package/dist/commands/env/auth.js +189 -0
- package/dist/commands/env/current.js +21 -0
- package/dist/commands/env/info.js +202 -0
- package/dist/commands/env/list.js +43 -0
- package/dist/commands/env/remove.js +174 -0
- package/dist/commands/env/shared.js +204 -0
- package/dist/commands/env/status.js +93 -0
- package/dist/commands/env/update.js +448 -0
- package/dist/commands/env/use.js +38 -0
- package/dist/commands/examples/prompts-stages.js +150 -0
- package/dist/commands/examples/prompts-test.js +181 -0
- package/dist/commands/init.js +1390 -0
- package/dist/commands/install.js +2609 -0
- package/dist/commands/license/activate.js +179 -0
- package/dist/commands/license/env.js +94 -0
- package/dist/commands/license/generate-id.js +108 -0
- package/dist/commands/license/id.js +70 -0
- package/dist/commands/license/index.js +20 -0
- package/dist/commands/license/plugins/clean.js +115 -0
- package/dist/commands/license/plugins/index.js +20 -0
- package/dist/commands/license/plugins/list.js +64 -0
- package/dist/commands/license/plugins/shared.js +382 -0
- package/dist/commands/license/plugins/sync.js +314 -0
- package/dist/commands/license/shared.js +423 -0
- package/dist/commands/license/status.js +64 -0
- package/dist/commands/logs.js +12 -0
- package/dist/commands/plugin/disable.js +86 -0
- package/dist/commands/plugin/enable.js +86 -0
- package/dist/commands/plugin/import.js +108 -0
- package/dist/commands/plugin/list.js +82 -0
- package/dist/commands/pm/disable.js +12 -0
- package/dist/commands/pm/enable.js +12 -0
- package/dist/commands/pm/list.js +12 -0
- package/dist/commands/proxy/caddy/current.js +17 -0
- package/dist/commands/proxy/caddy/generate.js +69 -0
- package/dist/commands/proxy/caddy/index.js +28 -0
- package/dist/commands/proxy/caddy/info.js +31 -0
- package/dist/commands/proxy/caddy/reload.js +30 -0
- package/dist/commands/proxy/caddy/restart.js +28 -0
- package/dist/commands/proxy/caddy/start.js +30 -0
- package/dist/commands/proxy/caddy/status.js +19 -0
- package/dist/commands/proxy/caddy/stop.js +30 -0
- package/dist/commands/proxy/caddy/use.js +26 -0
- package/dist/commands/proxy/index.js +28 -0
- package/dist/commands/proxy/nginx/current.js +18 -0
- package/dist/commands/proxy/nginx/generate.js +68 -0
- package/dist/commands/proxy/nginx/index.js +28 -0
- package/dist/commands/proxy/nginx/info.js +34 -0
- package/dist/commands/proxy/nginx/reload.js +30 -0
- package/dist/commands/proxy/nginx/restart.js +28 -0
- package/dist/commands/proxy/nginx/start.js +30 -0
- package/dist/commands/proxy/nginx/status.js +19 -0
- package/dist/commands/proxy/nginx/stop.js +30 -0
- package/dist/commands/proxy/nginx/use.js +31 -0
- package/dist/commands/restart.js +12 -0
- package/dist/commands/revision/create.js +118 -0
- package/dist/commands/scaffold/migration.js +38 -0
- package/dist/commands/scaffold/plugin.js +37 -0
- package/dist/commands/self/check.js +71 -0
- package/dist/commands/self/index.js +20 -0
- package/dist/commands/self/update.js +152 -0
- package/dist/commands/session/id.js +24 -0
- package/dist/commands/session/remove.js +57 -0
- package/dist/commands/session/setup.js +62 -0
- package/dist/commands/skills/check.js +69 -0
- package/dist/commands/skills/index.js +20 -0
- package/dist/commands/skills/install.js +80 -0
- package/dist/commands/skills/remove.js +80 -0
- package/dist/commands/skills/update.js +87 -0
- package/dist/commands/source/build.js +58 -0
- package/dist/commands/source/dev.js +182 -0
- package/dist/commands/source/download.js +884 -0
- package/dist/commands/source/publish.js +109 -0
- package/dist/commands/source/registry/logs.js +70 -0
- package/dist/commands/source/registry/start.js +57 -0
- package/dist/commands/source/registry/status.js +33 -0
- package/dist/commands/source/registry/stop.js +48 -0
- package/dist/commands/source/test.js +476 -0
- package/dist/commands/start.js +12 -0
- package/dist/commands/stop.js +12 -0
- package/dist/commands/test.js +12 -0
- package/dist/commands/upgrade.js +12 -0
- package/dist/commands/v1.js +210 -0
- package/dist/generated/command-registry.js +134 -0
- package/dist/help/runtime-help.js +23 -0
- package/dist/lib/api-client.js +335 -0
- package/dist/lib/api-command-compat.js +641 -0
- package/dist/lib/app-health.js +139 -0
- package/dist/lib/app-managed-resources.js +337 -0
- package/dist/lib/app-public-path.js +80 -0
- package/dist/lib/app-runtime.js +189 -0
- package/dist/lib/auth-store.js +528 -0
- package/dist/lib/backup.js +171 -0
- package/dist/lib/bootstrap.js +409 -0
- package/dist/lib/build-config.js +18 -0
- package/dist/lib/builtin-db.js +86 -0
- package/dist/lib/cli-config.js +569 -0
- package/dist/lib/cli-entry-error.js +52 -0
- package/dist/lib/cli-home.js +47 -0
- package/dist/lib/cli-locale.js +141 -0
- package/dist/lib/command-discovery.js +39 -0
- package/dist/lib/command-log.js +284 -0
- package/dist/lib/db-connection-check.js +219 -0
- package/dist/lib/docker-env-file.js +60 -0
- package/dist/lib/docker-image.js +37 -0
- package/dist/lib/docker-log-stream.js +45 -0
- package/dist/lib/env-auth.js +963 -0
- package/dist/lib/env-command-config.js +45 -0
- package/dist/lib/env-config.js +108 -0
- package/dist/lib/env-guard.js +61 -0
- package/dist/lib/env-paths.js +101 -0
- package/dist/lib/env-proxy.js +1325 -0
- package/dist/lib/generated-command.js +203 -0
- package/dist/lib/http-request.js +49 -0
- package/dist/lib/inquirer-theme.js +17 -0
- package/dist/lib/inquirer.js +243 -0
- package/dist/lib/managed-env-file.js +101 -0
- package/dist/lib/managed-init-env.js +32 -0
- package/dist/lib/naming.js +70 -0
- package/dist/lib/object-utils.js +76 -0
- package/dist/lib/openapi.js +62 -0
- package/dist/lib/plugin-import.js +279 -0
- package/dist/lib/plugin-storage.js +64 -0
- package/dist/lib/post-processors.js +23 -0
- package/dist/lib/prompt-catalog-core.js +186 -0
- package/dist/lib/prompt-catalog-terminal.js +374 -0
- package/{src/index.js → dist/lib/prompt-catalog.js} +2 -6
- package/dist/lib/prompt-validators.js +278 -0
- package/dist/lib/prompt-web-ui.js +2234 -0
- package/dist/lib/proxy-caddy.js +274 -0
- package/dist/lib/proxy-nginx.js +330 -0
- package/dist/lib/resource-command.js +357 -0
- package/dist/lib/resource-request.js +104 -0
- package/dist/lib/run-npm.js +429 -0
- package/dist/lib/runtime-env-vars.js +32 -0
- package/dist/lib/runtime-generator.js +498 -0
- package/dist/lib/runtime-store.js +56 -0
- package/dist/lib/self-manager.js +301 -0
- package/dist/lib/session-id.js +17 -0
- package/dist/lib/session-integration.js +703 -0
- package/dist/lib/session-store.js +118 -0
- package/dist/lib/skills-manager.js +438 -0
- package/dist/lib/source-publish.js +326 -0
- package/dist/lib/source-registry.js +188 -0
- package/dist/lib/startup-update.js +309 -0
- package/dist/lib/ui.js +159 -0
- package/dist/locale/en-US.json +526 -0
- package/dist/locale/zh-CN.json +526 -0
- package/dist/post-processors/data-modeling.js +84 -0
- package/dist/post-processors/data-source-manager.js +138 -0
- package/dist/post-processors/index.js +19 -0
- package/nocobase-ctl.config.json +388 -0
- package/package.json +128 -24
- package/scripts/build.mjs +34 -0
- package/scripts/clean.mjs +9 -0
- package/tsconfig.json +19 -0
- package/bin/index.js +0 -39
- package/nocobase.conf.tpl +0 -95
- package/src/commands/benchmark.js +0 -73
- package/src/commands/build.js +0 -49
- package/src/commands/clean.js +0 -30
- package/src/commands/client.js +0 -166
- package/src/commands/create-nginx-conf.js +0 -37
- package/src/commands/create-plugin.js +0 -33
- package/src/commands/dev.js +0 -200
- package/src/commands/doc.js +0 -76
- package/src/commands/e2e.js +0 -265
- package/src/commands/global.js +0 -43
- package/src/commands/index.js +0 -45
- package/src/commands/instance-id.js +0 -47
- package/src/commands/locale/cronstrue.js +0 -122
- package/src/commands/locale/react-js-cron/en-US.json +0 -75
- package/src/commands/locale/react-js-cron/zh-CN.json +0 -33
- package/src/commands/locale/react-js-cron/zh-TW.json +0 -33
- package/src/commands/locale.js +0 -81
- package/src/commands/p-test.js +0 -88
- package/src/commands/perf.js +0 -63
- package/src/commands/pkg.js +0 -321
- package/src/commands/pm2.js +0 -37
- package/src/commands/postinstall.js +0 -88
- package/src/commands/start.js +0 -148
- package/src/commands/tar.js +0 -36
- package/src/commands/test-coverage.js +0 -55
- package/src/commands/test.js +0 -107
- package/src/commands/umi.js +0 -33
- package/src/commands/update-deps.js +0 -72
- package/src/commands/upgrade.js +0 -47
- package/src/commands/view-license-key.js +0 -44
- package/src/license.js +0 -76
- package/src/logger.js +0 -75
- package/src/plugin-generator.js +0 -80
- package/src/util.js +0 -517
- package/templates/bundle-status.html +0 -338
- package/templates/create-app-package.json +0 -39
- package/templates/plugin/.npmignore.tpl +0 -2
- package/templates/plugin/README.md.tpl +0 -1
- package/templates/plugin/client.d.ts +0 -2
- package/templates/plugin/client.js +0 -1
- package/templates/plugin/package.json.tpl +0 -11
- package/templates/plugin/server.d.ts +0 -2
- package/templates/plugin/server.js +0 -1
- package/templates/plugin/src/client/client.d.ts +0 -249
- package/templates/plugin/src/client/index.tsx.tpl +0 -1
- package/templates/plugin/src/client/locale.ts +0 -21
- package/templates/plugin/src/client/plugin.tsx.tpl +0 -10
- package/templates/plugin/src/index.ts +0 -2
- package/templates/plugin/src/locale/en-US.json +0 -1
- package/templates/plugin/src/locale/zh-CN.json +0 -1
- package/templates/plugin/src/server/collections/.gitkeep +0 -0
- package/templates/plugin/src/server/index.ts.tpl +0 -1
- package/templates/plugin/src/server/plugin.ts.tpl +0 -19
|
@@ -0,0 +1,409 @@
|
|
|
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 { confirm } from "./inquirer.js";
|
|
10
|
+
import { getCurrentEnvName, getEnv, setEnvRuntime, updateEnvConnection } from './auth-store.js';
|
|
11
|
+
import { resolveAccessToken } from './env-auth.js';
|
|
12
|
+
import { fetchWithPreservedAuthRedirect } from './http-request.js';
|
|
13
|
+
import { generateRuntime } from './runtime-generator.js';
|
|
14
|
+
import { hasRuntimeSync, saveRuntime } from './runtime-store.js';
|
|
15
|
+
import { printInfo, printVerbose, printWarningBlock, setVerboseMode, stopTask, updateTask } from './ui.js';
|
|
16
|
+
const APP_RETRY_INTERVAL = 2000;
|
|
17
|
+
const APP_RETRY_TIMEOUT = 120000;
|
|
18
|
+
function readFlag(argv, name) {
|
|
19
|
+
const exact = `--${name}`;
|
|
20
|
+
const prefix = `--${name}=`;
|
|
21
|
+
const alias = name === 'env' ? '-e' : name === 'scope' ? '-s' : undefined;
|
|
22
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
23
|
+
const value = argv[index];
|
|
24
|
+
if (value === exact) {
|
|
25
|
+
return argv[index + 1];
|
|
26
|
+
}
|
|
27
|
+
if (alias && value === alias) {
|
|
28
|
+
return argv[index + 1];
|
|
29
|
+
}
|
|
30
|
+
if (value.startsWith(prefix)) {
|
|
31
|
+
return value.slice(prefix.length);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
function hasBooleanFlag(argv, name) {
|
|
37
|
+
const exact = `--${name}`;
|
|
38
|
+
const negated = `--no-${name}`;
|
|
39
|
+
const prefix = `--${name}=`;
|
|
40
|
+
for (const value of argv) {
|
|
41
|
+
if (value === exact) {
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
if (value === negated) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
if (value.startsWith(prefix)) {
|
|
48
|
+
return value.slice(prefix.length) !== 'false';
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
function getCommandToken(argv) {
|
|
54
|
+
const tokens = [];
|
|
55
|
+
for (const token of argv) {
|
|
56
|
+
if (!token || token.startsWith('-')) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
tokens.push(token);
|
|
60
|
+
}
|
|
61
|
+
if (tokens[0] === 'api') {
|
|
62
|
+
return tokens[1] ?? tokens[0];
|
|
63
|
+
}
|
|
64
|
+
return tokens[0];
|
|
65
|
+
}
|
|
66
|
+
function hasHelpFlag(argv) {
|
|
67
|
+
return argv.includes('--help') || argv.includes('-h');
|
|
68
|
+
}
|
|
69
|
+
function hasVersionFlag(argv) {
|
|
70
|
+
return argv.includes('--version') || argv.includes('-v');
|
|
71
|
+
}
|
|
72
|
+
function isBuiltinCommand(argv) {
|
|
73
|
+
const commandTokens = argv.filter((token) => token && !token.startsWith('-'));
|
|
74
|
+
const [topic, subtopic] = commandTokens;
|
|
75
|
+
return topic === 'env' || topic === 'resource' || (topic === 'api' && subtopic === 'resource');
|
|
76
|
+
}
|
|
77
|
+
export function shouldSkipRuntimeBootstrap(argv) {
|
|
78
|
+
return hasVersionFlag(argv) || isBuiltinCommand(argv);
|
|
79
|
+
}
|
|
80
|
+
function shouldIgnoreBootstrapFailure(argv, commandToken) {
|
|
81
|
+
return !commandToken || hasHelpFlag(argv) || (commandToken === 'api' && argv.length === 1);
|
|
82
|
+
}
|
|
83
|
+
async function requestJson(url, options) {
|
|
84
|
+
const headers = new Headers();
|
|
85
|
+
if (options.token) {
|
|
86
|
+
headers.set('authorization', `Bearer ${options.token}`);
|
|
87
|
+
}
|
|
88
|
+
if (options.role) {
|
|
89
|
+
headers.set('x-role', options.role);
|
|
90
|
+
}
|
|
91
|
+
let response;
|
|
92
|
+
try {
|
|
93
|
+
response = await fetchWithPreservedAuthRedirect(url, {
|
|
94
|
+
method: options.method ?? 'GET',
|
|
95
|
+
headers,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
return {
|
|
100
|
+
status: 0,
|
|
101
|
+
ok: false,
|
|
102
|
+
data: {
|
|
103
|
+
error: {
|
|
104
|
+
message: error?.message ?? 'fetch failed',
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
const text = await response.text();
|
|
110
|
+
let data = undefined;
|
|
111
|
+
if (text) {
|
|
112
|
+
try {
|
|
113
|
+
data = JSON.parse(text);
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
data = text;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
status: response.status,
|
|
121
|
+
ok: response.ok,
|
|
122
|
+
data,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function sleep(ms) {
|
|
126
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
127
|
+
}
|
|
128
|
+
function isAppRestarting(response) {
|
|
129
|
+
return response.status === 503 && response.data?.error?.code === 'APP_COMMANDING';
|
|
130
|
+
}
|
|
131
|
+
function shouldRetryAppAvailability(response) {
|
|
132
|
+
return isAppRestarting(response) || response.status === 0;
|
|
133
|
+
}
|
|
134
|
+
function getSwaggerUrl(baseUrl) {
|
|
135
|
+
return `${baseUrl.replace(/\/+$/, '')}/swagger:get`;
|
|
136
|
+
}
|
|
137
|
+
function getHealthCheckUrl(baseUrl) {
|
|
138
|
+
return `${baseUrl.replace(/\/+$/, '')}/__health_check`;
|
|
139
|
+
}
|
|
140
|
+
async function waitForServiceReady(baseUrl, token, role, options) {
|
|
141
|
+
const healthCheckUrl = getHealthCheckUrl(baseUrl);
|
|
142
|
+
const startedAt = Date.now();
|
|
143
|
+
let notified = false;
|
|
144
|
+
while (Date.now() - startedAt < APP_RETRY_TIMEOUT) {
|
|
145
|
+
const response = await fetchWithPreservedAuthRedirect(healthCheckUrl, {
|
|
146
|
+
method: 'GET',
|
|
147
|
+
headers: token || role
|
|
148
|
+
? {
|
|
149
|
+
...(token ? { authorization: `Bearer ${token}` } : undefined),
|
|
150
|
+
...(role ? { 'x-role': role } : undefined),
|
|
151
|
+
}
|
|
152
|
+
: undefined,
|
|
153
|
+
}).catch((error) => {
|
|
154
|
+
return {
|
|
155
|
+
ok: false,
|
|
156
|
+
status: 0,
|
|
157
|
+
text: async () => error?.message ?? 'fetch failed',
|
|
158
|
+
};
|
|
159
|
+
});
|
|
160
|
+
const text = await response.text();
|
|
161
|
+
if (response.ok && text.trim().toLowerCase() === 'ok') {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
if (!notified) {
|
|
165
|
+
if (!options?.quiet) {
|
|
166
|
+
printVerbose(`Waiting for health check: ${healthCheckUrl}`);
|
|
167
|
+
updateTask(`Waiting for application readiness (${healthCheckUrl})`);
|
|
168
|
+
}
|
|
169
|
+
notified = true;
|
|
170
|
+
}
|
|
171
|
+
await sleep(APP_RETRY_INTERVAL);
|
|
172
|
+
}
|
|
173
|
+
throw new Error(`The application did not become ready in time. Expected \`${healthCheckUrl}\` to respond with \`ok\`.`);
|
|
174
|
+
}
|
|
175
|
+
async function waitForSwaggerSchema(baseUrl, token, role, options) {
|
|
176
|
+
const swaggerUrl = getSwaggerUrl(baseUrl);
|
|
177
|
+
const startedAt = Date.now();
|
|
178
|
+
if (!options?.quiet) {
|
|
179
|
+
printVerbose(`Checking swagger schema: ${swaggerUrl}`);
|
|
180
|
+
}
|
|
181
|
+
while (Date.now() - startedAt < APP_RETRY_TIMEOUT) {
|
|
182
|
+
const response = await requestJson(swaggerUrl, { token, role });
|
|
183
|
+
if (response.ok) {
|
|
184
|
+
return response;
|
|
185
|
+
}
|
|
186
|
+
if (!shouldRetryAppAvailability(response)) {
|
|
187
|
+
return response;
|
|
188
|
+
}
|
|
189
|
+
await waitForServiceReady(baseUrl, token, role, options);
|
|
190
|
+
}
|
|
191
|
+
return await requestJson(swaggerUrl, { token, role });
|
|
192
|
+
}
|
|
193
|
+
async function confirmEnableApiDoc() {
|
|
194
|
+
try {
|
|
195
|
+
return await confirm({
|
|
196
|
+
message: 'Enable the API documentation plugin now?',
|
|
197
|
+
default: false,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
async function fetchSwaggerSchema(baseUrl, token, role, context = {}, options = {}) {
|
|
205
|
+
let response = options.retryAppAvailability === false
|
|
206
|
+
? await requestJson(getSwaggerUrl(baseUrl), { token, role })
|
|
207
|
+
: await waitForSwaggerSchema(baseUrl, token, role, { quiet: options.quiet });
|
|
208
|
+
if (response.status === 404) {
|
|
209
|
+
if (options.allowEnableApiDoc === false || options.quiet) {
|
|
210
|
+
throw new Error('`swagger:get` returned 404. Check the base URL and enable the `API documentation plugin` if needed.');
|
|
211
|
+
}
|
|
212
|
+
printInfo('The API documentation plugin is not enabled.');
|
|
213
|
+
const shouldEnable = await confirmEnableApiDoc();
|
|
214
|
+
if (!shouldEnable) {
|
|
215
|
+
throw new Error('`swagger:get` returned 404. Enable the `API documentation plugin` first.');
|
|
216
|
+
}
|
|
217
|
+
const enableUrl = `${baseUrl.replace(/\/+$/, '')}/pm:enable?filterByTk=api-doc`;
|
|
218
|
+
printVerbose(`Enabling API documentation plugin via ${enableUrl}`);
|
|
219
|
+
const enableResponse = await requestJson(enableUrl, { method: 'POST', token, role });
|
|
220
|
+
if (!enableResponse.ok) {
|
|
221
|
+
throw new Error(`Failed to enable the \`API documentation plugin\` via \`pm:enable\`.\n${JSON.stringify(enableResponse.data, null, 2)}`);
|
|
222
|
+
}
|
|
223
|
+
updateTask('Enabled the API documentation plugin. Waiting for application readiness...');
|
|
224
|
+
await waitForServiceReady(baseUrl, token, role);
|
|
225
|
+
response = await waitForSwaggerSchema(baseUrl, token, role);
|
|
226
|
+
}
|
|
227
|
+
if (!response.ok) {
|
|
228
|
+
throw new Error(formatSwaggerSchemaError(response, { baseUrl, token, ...context }));
|
|
229
|
+
}
|
|
230
|
+
return (response.data?.data ?? response.data);
|
|
231
|
+
}
|
|
232
|
+
function collectErrorEntries(data) {
|
|
233
|
+
if (Array.isArray(data?.errors)) {
|
|
234
|
+
return data.errors.filter(Boolean);
|
|
235
|
+
}
|
|
236
|
+
if (data?.error) {
|
|
237
|
+
return [data.error];
|
|
238
|
+
}
|
|
239
|
+
return [];
|
|
240
|
+
}
|
|
241
|
+
function hasAuthenticationError(data) {
|
|
242
|
+
return collectErrorEntries(data).some((entry) => entry?.code === 'INVALID_TOKEN' || entry?.code === 'EMPTY_TOKEN');
|
|
243
|
+
}
|
|
244
|
+
function isNetworkFetchFailure(response) {
|
|
245
|
+
return response.status === 0;
|
|
246
|
+
}
|
|
247
|
+
export function formatSwaggerSchemaError(response, context) {
|
|
248
|
+
if (hasAuthenticationError(response.data)) {
|
|
249
|
+
const entries = collectErrorEntries(response.data);
|
|
250
|
+
const details = entries
|
|
251
|
+
.map((entry) => {
|
|
252
|
+
const code = entry?.code ? `[${entry.code}] ` : '';
|
|
253
|
+
return `${code}${entry?.message ?? 'Authentication failed.'}`;
|
|
254
|
+
})
|
|
255
|
+
.join('\n');
|
|
256
|
+
const envLabel = context.envName ? ` for env "${context.envName}"` : '';
|
|
257
|
+
const tokenHint = context.envName
|
|
258
|
+
? `Update the API key with \`nb env update ${context.envName} --token <api-key>\`, log in with \`nb env auth ${context.envName}\`, or rerun the command with \`--access-token <api-key>\`.`
|
|
259
|
+
: 'Update the API key with `nb env update <name> --token <api-key>`, log in with `nb env auth <name>`, or rerun the command with `--access-token <api-key>`.';
|
|
260
|
+
const commandHint = context.commandToken
|
|
261
|
+
? `If \`${context.commandToken}\` is a runtime command, retry it after updating the token. If it is a typo, run \`nb --help\` to inspect available commands.`
|
|
262
|
+
: 'Run `nb --help` to inspect built-in commands, then retry the original command after updating the token.';
|
|
263
|
+
return [
|
|
264
|
+
`Authentication failed while loading the command runtime from \`swagger:get\`${envLabel}.`,
|
|
265
|
+
`Base URL: ${context.baseUrl}`,
|
|
266
|
+
details,
|
|
267
|
+
tokenHint,
|
|
268
|
+
commandHint,
|
|
269
|
+
].join('\n');
|
|
270
|
+
}
|
|
271
|
+
if (isNetworkFetchFailure(response)) {
|
|
272
|
+
const rawMessage = response.data?.error?.message || 'fetch failed';
|
|
273
|
+
const updateConnectionHint = context.envName
|
|
274
|
+
? `If you recently changed the server address, update env "${context.envName}" with \`nb env update ${context.envName} --api-base-url <url>\` and retry the original command.`
|
|
275
|
+
: 'If you recently changed the server address, update the saved env connection with `nb env update <name> --api-base-url <url>` and retry the original command.';
|
|
276
|
+
return [
|
|
277
|
+
'Failed to reach the NocoBase server while loading the command runtime from `swagger:get`.',
|
|
278
|
+
`Base URL: ${context.baseUrl}`,
|
|
279
|
+
`Network error: ${rawMessage}`,
|
|
280
|
+
'Check that the NocoBase app is running, the base URL is correct, and the server is reachable from this machine.',
|
|
281
|
+
updateConnectionHint,
|
|
282
|
+
'Use `nb env list` to inspect the current env configuration.',
|
|
283
|
+
].join('\n');
|
|
284
|
+
}
|
|
285
|
+
return `Failed to load swagger schema from \`swagger:get\`.\n${JSON.stringify(response.data, null, 2)}`;
|
|
286
|
+
}
|
|
287
|
+
export function formatMissingRuntimeEnvError(commandToken) {
|
|
288
|
+
if (!commandToken) {
|
|
289
|
+
return [
|
|
290
|
+
'No env is configured for runtime commands.',
|
|
291
|
+
'Run `nb init --ui` first.',
|
|
292
|
+
'If you configure multiple environments later, switch with `nb env use <name>`.',
|
|
293
|
+
].join('\n');
|
|
294
|
+
}
|
|
295
|
+
return [
|
|
296
|
+
`Unable to resolve runtime command \`${commandToken}\`.`,
|
|
297
|
+
'No env is configured, so the CLI cannot load runtime commands from `swagger:get`.',
|
|
298
|
+
'If this is a built-in command or a typo, run `nb --help` to inspect available commands.',
|
|
299
|
+
'If this should be an application runtime command, run `nb init --ui` to create or connect a NocoBase app first.',
|
|
300
|
+
].join('\n');
|
|
301
|
+
}
|
|
302
|
+
export async function ensureRuntimeFromArgv(argv, options) {
|
|
303
|
+
const commandToken = getCommandToken(argv);
|
|
304
|
+
const isRootInvocation = !commandToken;
|
|
305
|
+
const canContinueWithoutRuntime = shouldIgnoreBootstrapFailure(argv, commandToken);
|
|
306
|
+
setVerboseMode(hasBooleanFlag(argv, 'verbose'));
|
|
307
|
+
if (shouldSkipRuntimeBootstrap(argv)) {
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
try {
|
|
311
|
+
const envName = readFlag(argv, 'env') ?? (await getCurrentEnvName());
|
|
312
|
+
const env = await getEnv(envName);
|
|
313
|
+
const baseUrl = readFlag(argv, 'api-base-url') ?? env?.baseUrl;
|
|
314
|
+
const role = readFlag(argv, 'role');
|
|
315
|
+
const token = await resolveAccessToken({
|
|
316
|
+
envName,
|
|
317
|
+
baseUrl,
|
|
318
|
+
token: readFlag(argv, 'token'),
|
|
319
|
+
});
|
|
320
|
+
const runtimeVersion = env?.runtime?.version;
|
|
321
|
+
if (runtimeVersion && hasRuntimeSync(runtimeVersion)) {
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
if (!baseUrl) {
|
|
325
|
+
if (isRootInvocation) {
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
throw new Error(formatMissingRuntimeEnvError(commandToken));
|
|
329
|
+
}
|
|
330
|
+
updateTask('Loading command runtime...');
|
|
331
|
+
try {
|
|
332
|
+
printVerbose(`Runtime source: ${baseUrl}`);
|
|
333
|
+
const document = await fetchSwaggerSchema(baseUrl, token, role, { envName, commandToken }, isRootInvocation
|
|
334
|
+
? {
|
|
335
|
+
allowEnableApiDoc: false,
|
|
336
|
+
retryAppAvailability: false,
|
|
337
|
+
}
|
|
338
|
+
: undefined);
|
|
339
|
+
const runtime = await generateRuntime(document, options.configFile, baseUrl);
|
|
340
|
+
await saveRuntime(runtime);
|
|
341
|
+
await setEnvRuntime(envName, {
|
|
342
|
+
version: runtime.version,
|
|
343
|
+
schemaHash: runtime.schemaHash,
|
|
344
|
+
generatedAt: runtime.generatedAt,
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
finally {
|
|
348
|
+
stopTask();
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
catch (error) {
|
|
352
|
+
if (!canContinueWithoutRuntime) {
|
|
353
|
+
throw error;
|
|
354
|
+
}
|
|
355
|
+
stopTask();
|
|
356
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
357
|
+
printWarningBlock(`Unable to load runtime commands. Showing built-in help instead.\n\n${message}`);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
export async function updateEnvRuntime(options) {
|
|
361
|
+
setVerboseMode(Boolean(options.verbose));
|
|
362
|
+
const envName = options.envName ?? (await getCurrentEnvName({ scope: options.scope }));
|
|
363
|
+
const env = await getEnv(envName, { scope: options.scope });
|
|
364
|
+
const baseUrl = options.baseUrl ?? env?.baseUrl;
|
|
365
|
+
const token = await resolveAccessToken({
|
|
366
|
+
envName,
|
|
367
|
+
baseUrl,
|
|
368
|
+
token: options.token,
|
|
369
|
+
scope: options.scope,
|
|
370
|
+
});
|
|
371
|
+
if (!baseUrl) {
|
|
372
|
+
throw new Error([
|
|
373
|
+
env
|
|
374
|
+
? `Env "${envName}" is missing a base URL.`
|
|
375
|
+
: `Env "${envName}" is not configured. Run \`nb init --ui --env ${envName}\` first.`,
|
|
376
|
+
env ? `Update env "${envName}" with \`nb env update ${envName} --api-base-url <url>\` first.` : '',
|
|
377
|
+
]
|
|
378
|
+
.filter(Boolean)
|
|
379
|
+
.join('\n'));
|
|
380
|
+
}
|
|
381
|
+
if (!options.quiet) {
|
|
382
|
+
updateTask('Loading command runtime...');
|
|
383
|
+
}
|
|
384
|
+
try {
|
|
385
|
+
if (!options.quiet) {
|
|
386
|
+
printVerbose(`Runtime source: ${baseUrl}`);
|
|
387
|
+
}
|
|
388
|
+
const document = await fetchSwaggerSchema(baseUrl, token, options.role, { envName }, { quiet: options.quiet });
|
|
389
|
+
const runtime = await generateRuntime(document, options.configFile, baseUrl);
|
|
390
|
+
await saveRuntime(runtime, { scope: options.scope });
|
|
391
|
+
if (options.baseUrl !== undefined || options.token !== undefined) {
|
|
392
|
+
await updateEnvConnection(envName, {
|
|
393
|
+
apiBaseUrl: options.baseUrl,
|
|
394
|
+
accessToken: options.token,
|
|
395
|
+
}, { scope: options.scope });
|
|
396
|
+
}
|
|
397
|
+
await setEnvRuntime(envName, {
|
|
398
|
+
version: runtime.version,
|
|
399
|
+
schemaHash: runtime.schemaHash,
|
|
400
|
+
generatedAt: runtime.generatedAt,
|
|
401
|
+
}, { scope: options.scope });
|
|
402
|
+
return runtime;
|
|
403
|
+
}
|
|
404
|
+
finally {
|
|
405
|
+
if (!options.quiet) {
|
|
406
|
+
stopTask();
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
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 { promises as fs } from 'node:fs';
|
|
10
|
+
export async function loadBuildConfig(filePath) {
|
|
11
|
+
try {
|
|
12
|
+
const content = await fs.readFile(filePath, 'utf8');
|
|
13
|
+
return JSON.parse(content);
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
return {};
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -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 { buildDockerDbContainerName } from './app-runtime.js';
|
|
10
|
+
import { commandOutput } from './run-npm.js';
|
|
11
|
+
function trimValue(value) {
|
|
12
|
+
return String(value ?? '').trim();
|
|
13
|
+
}
|
|
14
|
+
export function defaultBuiltinDbPortForDialect(value) {
|
|
15
|
+
const dialect = trimValue(value) || 'postgres';
|
|
16
|
+
switch (dialect) {
|
|
17
|
+
case 'mysql':
|
|
18
|
+
case 'mariadb':
|
|
19
|
+
return '3306';
|
|
20
|
+
case 'kingbase':
|
|
21
|
+
return '54321';
|
|
22
|
+
case 'postgres':
|
|
23
|
+
default:
|
|
24
|
+
return '5432';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export function resolveBuiltinDbContainerName(runtime, dbDialect) {
|
|
28
|
+
const dialect = trimValue(dbDialect ?? runtime.env.config.dbDialect) || 'postgres';
|
|
29
|
+
return buildDockerDbContainerName(runtime.envName, dialect, runtime.dockerContainerPrefix || runtime.workspaceName);
|
|
30
|
+
}
|
|
31
|
+
export function deriveBuiltinDbConnection(runtime, overrides = {}) {
|
|
32
|
+
const dbDialect = trimValue(overrides.dbDialect ?? runtime.env.config.dbDialect) || 'postgres';
|
|
33
|
+
const containerName = resolveBuiltinDbContainerName(runtime, dbDialect);
|
|
34
|
+
const networkName = trimValue(runtime.dockerNetworkName || runtime.workspaceName) || undefined;
|
|
35
|
+
if (runtime.source === 'docker') {
|
|
36
|
+
return {
|
|
37
|
+
builtinDb: true,
|
|
38
|
+
dbDialect,
|
|
39
|
+
dbHost: containerName,
|
|
40
|
+
dbPort: defaultBuiltinDbPortForDialect(dbDialect),
|
|
41
|
+
containerName,
|
|
42
|
+
networkName,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
const dbPort = trimValue(overrides.dbPort ?? runtime.env.config.dbPort) || defaultBuiltinDbPortForDialect(dbDialect);
|
|
46
|
+
return {
|
|
47
|
+
builtinDb: true,
|
|
48
|
+
dbDialect,
|
|
49
|
+
dbHost: '127.0.0.1',
|
|
50
|
+
dbPort,
|
|
51
|
+
containerName,
|
|
52
|
+
networkName,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
export async function resolveBuiltinDbConnection(runtime) {
|
|
56
|
+
const derived = deriveBuiltinDbConnection(runtime);
|
|
57
|
+
if (runtime.source === 'docker') {
|
|
58
|
+
return derived;
|
|
59
|
+
}
|
|
60
|
+
const mappedPort = await inspectBuiltinDbPublishedPort(derived.containerName, derived.dbDialect);
|
|
61
|
+
if (mappedPort) {
|
|
62
|
+
return {
|
|
63
|
+
...derived,
|
|
64
|
+
dbPort: mappedPort,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
return derived;
|
|
68
|
+
}
|
|
69
|
+
async function inspectBuiltinDbPublishedPort(containerName, dbDialect) {
|
|
70
|
+
const containerPort = defaultBuiltinDbPortForDialect(dbDialect);
|
|
71
|
+
try {
|
|
72
|
+
const output = await commandOutput('docker', [
|
|
73
|
+
'inspect',
|
|
74
|
+
'--format',
|
|
75
|
+
`{{with index .NetworkSettings.Ports "${containerPort}/tcp"}}{{(index . 0).HostPort}}{{end}}`,
|
|
76
|
+
containerName,
|
|
77
|
+
], {
|
|
78
|
+
errorName: 'docker inspect',
|
|
79
|
+
});
|
|
80
|
+
const hostPort = trimValue(output);
|
|
81
|
+
return hostPort || undefined;
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
}
|