@nocobase/cli 2.1.0-alpha.4 → 2.1.0-alpha.40
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/LICENSE.txt +107 -0
- package/README.md +393 -19
- package/README.zh-CN.md +343 -0
- package/bin/run.cmd +3 -0
- package/bin/run.js +135 -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/down.js +301 -0
- package/dist/commands/app/logs.js +114 -0
- package/dist/commands/app/restart.js +158 -0
- package/dist/commands/app/start.js +305 -0
- package/dist/commands/app/stop.js +115 -0
- package/dist/commands/app/upgrade.js +636 -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 +30 -0
- package/dist/commands/config/get.js +29 -0
- package/dist/commands/config/index.js +20 -0
- package/dist/commands/config/list.js +29 -0
- package/dist/commands/config/set.js +35 -0
- package/dist/commands/db/check.js +240 -0
- package/dist/commands/db/logs.js +85 -0
- package/dist/commands/db/ps.js +60 -0
- package/dist/commands/db/shared.js +96 -0
- package/dist/commands/db/start.js +71 -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 +366 -0
- package/dist/commands/env/auth.js +130 -0
- package/dist/commands/env/current.js +21 -0
- package/dist/commands/env/info.js +157 -0
- package/dist/commands/env/list.js +44 -0
- package/dist/commands/env/remove.js +84 -0
- package/dist/commands/env/shared.js +158 -0
- package/dist/commands/env/status.js +90 -0
- package/dist/commands/env/update.js +74 -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 +1092 -0
- package/dist/commands/install.js +2378 -0
- package/dist/commands/license/activate.js +360 -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 +325 -0
- package/dist/commands/license/plugins/sync.js +285 -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/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/restart.js +12 -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 +95 -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 +880 -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 +477 -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 +133 -0
- package/dist/help/runtime-help.js +23 -0
- package/dist/lib/api-client.js +329 -0
- package/dist/lib/app-health.js +126 -0
- package/dist/lib/app-managed-resources.js +316 -0
- package/dist/lib/app-runtime.js +180 -0
- package/dist/lib/auth-store.js +368 -0
- package/dist/lib/backup.js +171 -0
- package/dist/lib/bootstrap.js +403 -0
- package/dist/lib/build-config.js +18 -0
- package/dist/lib/builtin-db.js +86 -0
- package/dist/lib/cli-config.js +176 -0
- package/dist/lib/cli-home.js +47 -0
- package/dist/lib/cli-locale.js +129 -0
- package/dist/lib/command-discovery.js +39 -0
- package/dist/lib/db-connection-check.js +158 -0
- package/dist/lib/docker-env-file.js +52 -0
- package/dist/lib/docker-image.js +37 -0
- package/dist/lib/env-auth.js +873 -0
- package/dist/lib/env-config.js +94 -0
- package/dist/lib/env-guard.js +62 -0
- package/dist/lib/generated-command.js +186 -0
- package/dist/lib/http-request.js +49 -0
- package/dist/lib/inquirer-theme.js +17 -0
- package/dist/lib/inquirer.js +244 -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-storage.js +64 -0
- package/dist/lib/post-processors.js +23 -0
- package/dist/lib/prompt-catalog-core.js +185 -0
- package/dist/lib/prompt-catalog-terminal.js +375 -0
- package/{src/index.js → dist/lib/prompt-catalog.js} +2 -6
- package/dist/lib/prompt-validators.js +240 -0
- package/dist/lib/prompt-web-ui.js +2103 -0
- package/dist/lib/resource-command.js +357 -0
- package/dist/lib/resource-request.js +104 -0
- package/dist/lib/run-npm.js +275 -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 +360 -0
- package/dist/lib/source-publish.js +306 -0
- package/dist/lib/source-registry.js +188 -0
- package/dist/lib/startup-update.js +285 -0
- package/dist/lib/ui.js +155 -0
- package/dist/locale/en-US.json +344 -0
- package/dist/locale/zh-CN.json +344 -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 +100 -26
- package/LICENSE +0 -661
- 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,44 @@
|
|
|
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 } from '@oclif/core';
|
|
10
|
+
import { getCurrentEnvName, listEnvs } from '../../lib/auth-store.js';
|
|
11
|
+
import { resolveDefaultConfigScope } from '../../lib/cli-home.js';
|
|
12
|
+
import { renderTable } from '../../lib/ui.js';
|
|
13
|
+
import { resolveApiBaseUrl } from './shared.js';
|
|
14
|
+
export default class EnvList extends Command {
|
|
15
|
+
static summary = 'List configured environments';
|
|
16
|
+
static examples = [
|
|
17
|
+
'<%= config.bin %> <%= command.id %>',
|
|
18
|
+
];
|
|
19
|
+
async run() {
|
|
20
|
+
await this.parse(EnvList);
|
|
21
|
+
const scope = resolveDefaultConfigScope();
|
|
22
|
+
const { envs } = await listEnvs({ scope });
|
|
23
|
+
const currentEnv = await getCurrentEnvName({ scope });
|
|
24
|
+
const names = Object.keys(envs).sort();
|
|
25
|
+
if (!names.length) {
|
|
26
|
+
this.log('No envs configured.');
|
|
27
|
+
this.log('Run `nb env add <name> --api-base-url <url>` to add one.');
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const rows = [];
|
|
31
|
+
for (const name of names) {
|
|
32
|
+
const env = envs[name];
|
|
33
|
+
rows.push([
|
|
34
|
+
name === currentEnv ? '*' : '',
|
|
35
|
+
name,
|
|
36
|
+
env.kind ?? '-',
|
|
37
|
+
resolveApiBaseUrl(env),
|
|
38
|
+
env.auth?.type ?? '',
|
|
39
|
+
env.runtime?.version ?? '',
|
|
40
|
+
]);
|
|
41
|
+
}
|
|
42
|
+
this.log(renderTable(['Current', 'Name', 'Kind', 'API Base URL', 'Auth', 'Runtime'], rows));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
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 { getCurrentEnvName, loadAuthConfig, removeEnv } from '../../lib/auth-store.js';
|
|
11
|
+
import { resolveDefaultConfigScope } from '../../lib/cli-home.js';
|
|
12
|
+
import { confirm } from "../../lib/inquirer.js";
|
|
13
|
+
import { isInteractiveTerminal, printVerbose, setVerboseMode } from '../../lib/ui.js';
|
|
14
|
+
export default class EnvRemove extends Command {
|
|
15
|
+
static summary = 'Remove a configured environment';
|
|
16
|
+
static description = 'Remove the saved CLI env config for an environment. This command does not clean local app files, containers, or storage data.';
|
|
17
|
+
static examples = [
|
|
18
|
+
'<%= config.bin %> <%= command.id %> staging',
|
|
19
|
+
'<%= config.bin %> <%= command.id %> staging --yes',
|
|
20
|
+
];
|
|
21
|
+
static flags = {
|
|
22
|
+
yes: Flags.boolean({
|
|
23
|
+
char: 'y',
|
|
24
|
+
description: 'Skip confirmation and remove the saved CLI env config',
|
|
25
|
+
default: false,
|
|
26
|
+
}),
|
|
27
|
+
force: Flags.boolean({
|
|
28
|
+
char: 'f',
|
|
29
|
+
hidden: true,
|
|
30
|
+
default: false,
|
|
31
|
+
}),
|
|
32
|
+
verbose: Flags.boolean({
|
|
33
|
+
description: 'Show detailed progress output',
|
|
34
|
+
default: false,
|
|
35
|
+
}),
|
|
36
|
+
};
|
|
37
|
+
static args = {
|
|
38
|
+
name: Args.string({
|
|
39
|
+
description: 'Configured environment name to remove',
|
|
40
|
+
required: true,
|
|
41
|
+
}),
|
|
42
|
+
};
|
|
43
|
+
async run() {
|
|
44
|
+
const { args, flags } = await this.parse(EnvRemove);
|
|
45
|
+
setVerboseMode(flags.verbose);
|
|
46
|
+
const scope = resolveDefaultConfigScope();
|
|
47
|
+
const config = await loadAuthConfig({ scope });
|
|
48
|
+
if (!config.envs[args.name]) {
|
|
49
|
+
this.error(`Env "${args.name}" is not configured`);
|
|
50
|
+
}
|
|
51
|
+
const currentEnv = await getCurrentEnvName({ scope });
|
|
52
|
+
const skipConfirmation = flags.yes || flags.force;
|
|
53
|
+
if (!skipConfirmation) {
|
|
54
|
+
if (!isInteractiveTerminal()) {
|
|
55
|
+
this.error(`Refusing to remove env "${args.name}" without confirmation in non-interactive mode. Re-run with \`--yes\` to remove only the saved CLI env config.`);
|
|
56
|
+
}
|
|
57
|
+
const subject = args.name === currentEnv ? `current env "${args.name}"` : `env "${args.name}"`;
|
|
58
|
+
let confirmed = false;
|
|
59
|
+
try {
|
|
60
|
+
confirmed = await confirm({
|
|
61
|
+
message: `Remove ${subject}? Only the saved CLI env config will be removed.`,
|
|
62
|
+
default: false,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (!confirmed) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
printVerbose(`Removing env "${args.name}"`);
|
|
73
|
+
const result = await removeEnv(args.name, { scope });
|
|
74
|
+
if (result.hasEnvs) {
|
|
75
|
+
if (args.name === currentEnv) {
|
|
76
|
+
this.log(`Removed env "${result.removed}". Switched current env to "${await getCurrentEnvName({ scope })}".`);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
this.log(`Removed env "${result.removed}".`);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
this.log(`Removed env "${result.removed}". No envs configured.`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -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 { buildDockerDbContainerName, dockerContainerExists, dockerContainerIsRunning, } from '../../lib/app-runtime.js';
|
|
10
|
+
import { executeRawApiRequest } from '../../lib/api-client.js';
|
|
11
|
+
export function resolveApiBaseUrl(config) {
|
|
12
|
+
return String(config.apiBaseUrl ?? config.baseUrl ?? config.apibaseUrl ?? '').trim();
|
|
13
|
+
}
|
|
14
|
+
export function appUrl(runtime) {
|
|
15
|
+
const port = String(runtime.env.config.appPort ?? '').trim();
|
|
16
|
+
if (port) {
|
|
17
|
+
return `http://127.0.0.1:${port}`;
|
|
18
|
+
}
|
|
19
|
+
const baseUrl = resolveApiBaseUrl(runtime.env.config);
|
|
20
|
+
return baseUrl.replace(/\/api\/?$/, '');
|
|
21
|
+
}
|
|
22
|
+
export function appRootPath(runtime) {
|
|
23
|
+
if (runtime.kind === 'http' || runtime.kind === 'docker') {
|
|
24
|
+
return '-';
|
|
25
|
+
}
|
|
26
|
+
if (runtime.kind === 'local') {
|
|
27
|
+
return String(runtime.projectRoot ?? runtime.env.appRootPath ?? '').trim() || '-';
|
|
28
|
+
}
|
|
29
|
+
return String(runtime.env.config.appRootPath ?? '').trim() || '-';
|
|
30
|
+
}
|
|
31
|
+
export function storagePath(runtime) {
|
|
32
|
+
if (runtime.kind === 'http') {
|
|
33
|
+
return '-';
|
|
34
|
+
}
|
|
35
|
+
const value = String(runtime.env.storagePath ?? runtime.env.config.storagePath ?? '').trim();
|
|
36
|
+
return value || '-';
|
|
37
|
+
}
|
|
38
|
+
function collectErrorCodes(value) {
|
|
39
|
+
if (!value || typeof value !== 'object') {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
if (Array.isArray(value)) {
|
|
43
|
+
return value.flatMap((item) => collectErrorCodes(item));
|
|
44
|
+
}
|
|
45
|
+
const out = [];
|
|
46
|
+
const record = value;
|
|
47
|
+
if (typeof record.code === 'string') {
|
|
48
|
+
out.push(record.code);
|
|
49
|
+
}
|
|
50
|
+
for (const key of ['data', 'error', 'errors']) {
|
|
51
|
+
out.push(...collectErrorCodes(record[key]));
|
|
52
|
+
}
|
|
53
|
+
return out;
|
|
54
|
+
}
|
|
55
|
+
function isAuthFailureData(value) {
|
|
56
|
+
const codes = collectErrorCodes(value);
|
|
57
|
+
return codes.some((code) => ['EMPTY_TOKEN', 'INVALID_TOKEN', 'EXPIRED_TOKEN', 'BLOCKED_TOKEN', 'EXPIRED_SESSION', 'NOT_EXIST_USER'].includes(code));
|
|
58
|
+
}
|
|
59
|
+
function isNetworkFailure(error) {
|
|
60
|
+
if (!(error instanceof Error)) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
return (error.name === 'AbortError'
|
|
64
|
+
|| /fetch failed|network|timeout|timed out|abort|ECONNREFUSED|ECONNRESET|ENOTFOUND|ETIMEDOUT|EAI_AGAIN|ENETUNREACH/i.test(error.message));
|
|
65
|
+
}
|
|
66
|
+
function isUnconfiguredFailure(error) {
|
|
67
|
+
return error instanceof Error && /missing (a )?base url|missing base URL/i.test(error.message);
|
|
68
|
+
}
|
|
69
|
+
function isAuthFailure(error) {
|
|
70
|
+
return (error instanceof Error
|
|
71
|
+
&& /EMPTY_TOKEN|INVALID_TOKEN|EXPIRED_TOKEN|BLOCKED_TOKEN|EXPIRED_SESSION|NOT_EXIST_USER|invalid_grant|sign in|signin|authentication failed/i.test(error.message));
|
|
72
|
+
}
|
|
73
|
+
export async function apiStatus(envName, config, options = {}) {
|
|
74
|
+
if (!resolveApiBaseUrl(config)) {
|
|
75
|
+
return 'unconfigured';
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
const response = await executeRawApiRequest({
|
|
79
|
+
envName,
|
|
80
|
+
scope: options.scope,
|
|
81
|
+
method: 'GET',
|
|
82
|
+
path: '/auth:check',
|
|
83
|
+
timeoutMs: options.timeoutMs ?? 2000,
|
|
84
|
+
});
|
|
85
|
+
if (response.ok) {
|
|
86
|
+
return 'ok';
|
|
87
|
+
}
|
|
88
|
+
if (response.status === 401 || response.status === 403 || isAuthFailureData(response.data)) {
|
|
89
|
+
return 'auth failed';
|
|
90
|
+
}
|
|
91
|
+
return 'error';
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
if (isUnconfiguredFailure(error)) {
|
|
95
|
+
return 'unconfigured';
|
|
96
|
+
}
|
|
97
|
+
if (isAuthFailure(error)) {
|
|
98
|
+
return 'auth failed';
|
|
99
|
+
}
|
|
100
|
+
if (isNetworkFailure(error)) {
|
|
101
|
+
return 'unreachable';
|
|
102
|
+
}
|
|
103
|
+
return 'error';
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
async function isLocalAppHealthy(runtime) {
|
|
107
|
+
const port = String(runtime.env.config.appPort ?? '').trim();
|
|
108
|
+
if (!port) {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
const controller = new AbortController();
|
|
112
|
+
const timeout = setTimeout(() => controller.abort(), 1500);
|
|
113
|
+
try {
|
|
114
|
+
const response = await fetch(`http://127.0.0.1:${port}/api/__health_check`, {
|
|
115
|
+
signal: controller.signal,
|
|
116
|
+
});
|
|
117
|
+
const text = await response.text();
|
|
118
|
+
return response.ok && text.trim().toLowerCase() === 'ok';
|
|
119
|
+
}
|
|
120
|
+
catch (_error) {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
finally {
|
|
124
|
+
clearTimeout(timeout);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
async function dockerStatus(containerName) {
|
|
128
|
+
if (!(await dockerContainerExists(containerName))) {
|
|
129
|
+
return 'missing';
|
|
130
|
+
}
|
|
131
|
+
return await dockerContainerIsRunning(containerName) ? 'running' : 'stopped';
|
|
132
|
+
}
|
|
133
|
+
export async function dbStatus(runtime) {
|
|
134
|
+
if (!runtime.env.config.builtinDb) {
|
|
135
|
+
return runtime.kind === 'http' ? 'external' : '-';
|
|
136
|
+
}
|
|
137
|
+
if (runtime.kind === 'http') {
|
|
138
|
+
return 'external';
|
|
139
|
+
}
|
|
140
|
+
if (runtime.kind === 'ssh') {
|
|
141
|
+
return '-';
|
|
142
|
+
}
|
|
143
|
+
const dbDialect = String(runtime.env.config.dbDialect ?? 'postgres').trim() || 'postgres';
|
|
144
|
+
const containerName = buildDockerDbContainerName(runtime.envName, dbDialect, runtime.dockerContainerPrefix || runtime.workspaceName);
|
|
145
|
+
return await dockerStatus(containerName);
|
|
146
|
+
}
|
|
147
|
+
export async function runtimeStatus(runtime) {
|
|
148
|
+
if (runtime.kind === 'http') {
|
|
149
|
+
return 'http';
|
|
150
|
+
}
|
|
151
|
+
if (runtime.kind === 'ssh') {
|
|
152
|
+
return 'ssh';
|
|
153
|
+
}
|
|
154
|
+
if (runtime.kind === 'docker') {
|
|
155
|
+
return await dockerStatus(runtime.containerName);
|
|
156
|
+
}
|
|
157
|
+
return await isLocalAppHealthy(runtime) ? 'running' : 'stopped';
|
|
158
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
import { Args, Command, Flags } from '@oclif/core';
|
|
10
|
+
import { formatMissingManagedAppEnvMessage, resolveManagedAppRuntime } from '../../lib/app-runtime.js';
|
|
11
|
+
import { getCurrentEnvName, listEnvs } from '../../lib/auth-store.js';
|
|
12
|
+
import { resolveDefaultConfigScope } from '../../lib/cli-home.js';
|
|
13
|
+
import { renderTable } from '../../lib/ui.js';
|
|
14
|
+
import { apiStatus, runtimeStatus } from './shared.js';
|
|
15
|
+
export default class EnvStatus extends Command {
|
|
16
|
+
static summary = 'Show application runtime status for the current env, one env, or all envs';
|
|
17
|
+
static examples = [
|
|
18
|
+
'<%= config.bin %> <%= command.id %>',
|
|
19
|
+
'<%= config.bin %> <%= command.id %> app1',
|
|
20
|
+
'<%= config.bin %> <%= command.id %> --all',
|
|
21
|
+
'<%= config.bin %> <%= command.id %> --all --json-output',
|
|
22
|
+
];
|
|
23
|
+
static args = {
|
|
24
|
+
name: Args.string({
|
|
25
|
+
description: 'Configured environment name to inspect. Defaults to the current env when omitted; cannot be used with --all',
|
|
26
|
+
required: false,
|
|
27
|
+
}),
|
|
28
|
+
};
|
|
29
|
+
static flags = {
|
|
30
|
+
all: Flags.boolean({
|
|
31
|
+
description: 'Show status for all configured envs',
|
|
32
|
+
default: false,
|
|
33
|
+
}),
|
|
34
|
+
'json-output': Flags.boolean({
|
|
35
|
+
description: 'Output the result as JSON',
|
|
36
|
+
default: false,
|
|
37
|
+
}),
|
|
38
|
+
};
|
|
39
|
+
async run() {
|
|
40
|
+
const { args, flags } = await this.parse(EnvStatus);
|
|
41
|
+
const scope = resolveDefaultConfigScope();
|
|
42
|
+
const requestedEnv = args.name?.trim() || undefined;
|
|
43
|
+
if (requestedEnv && flags.all) {
|
|
44
|
+
this.error('`nb env status <name>` and `nb env status --all` cannot be used together.');
|
|
45
|
+
}
|
|
46
|
+
const { envs } = await listEnvs({ scope });
|
|
47
|
+
const configuredEnvNames = Object.keys(envs).sort();
|
|
48
|
+
if (!configuredEnvNames.length) {
|
|
49
|
+
this.log('No envs configured.');
|
|
50
|
+
this.log('Run `nb env add <name> --api-base-url <url>` to add one.');
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const envNames = flags.all
|
|
54
|
+
? configuredEnvNames
|
|
55
|
+
: [requestedEnv || (await getCurrentEnvName({ scope }))];
|
|
56
|
+
const rows = [];
|
|
57
|
+
for (const envName of envNames) {
|
|
58
|
+
const runtime = await resolveManagedAppRuntime(envName);
|
|
59
|
+
if (!runtime) {
|
|
60
|
+
if (!flags.all) {
|
|
61
|
+
this.error(formatMissingManagedAppEnvMessage(envName));
|
|
62
|
+
}
|
|
63
|
+
rows.push({
|
|
64
|
+
env: envName,
|
|
65
|
+
status: 'missing',
|
|
66
|
+
apiBaseUrl: '',
|
|
67
|
+
});
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
const status = runtime.kind === 'http' || runtime.kind === 'ssh'
|
|
71
|
+
? await apiStatus(envName, {
|
|
72
|
+
...envs[envName],
|
|
73
|
+
...(runtime.env.config ?? {}),
|
|
74
|
+
}, { scope })
|
|
75
|
+
: await runtimeStatus(runtime);
|
|
76
|
+
rows.push({
|
|
77
|
+
env: runtime.envName,
|
|
78
|
+
status,
|
|
79
|
+
apiBaseUrl: runtime.env.apiBaseUrl
|
|
80
|
+
|| String(runtime.env.config.apiBaseUrl ?? runtime.env.config.baseUrl ?? envs[envName]?.apiBaseUrl ?? envs[envName]?.baseUrl ?? '').trim(),
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
if (flags['json-output']) {
|
|
84
|
+
this.log(JSON.stringify(flags.all ? rows : rows[0], null, 2));
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const tableRows = rows.map((row) => [row.env, row.status, row.apiBaseUrl]);
|
|
88
|
+
this.log(renderTable(['Env', 'Status', 'API Base URL'], tableRows));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
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 path from 'node:path';
|
|
10
|
+
import { fileURLToPath } from 'node:url';
|
|
11
|
+
import { Args, Command, Flags } from '@oclif/core';
|
|
12
|
+
import { getCurrentEnvName } from '../../lib/auth-store.js';
|
|
13
|
+
import { updateEnvRuntime } from '../../lib/bootstrap.js';
|
|
14
|
+
import { resolveDefaultConfigScope } from '../../lib/cli-home.js';
|
|
15
|
+
import { failTask, printVerbose, setVerboseMode, startTask, stopTask, succeedTask } from '../../lib/ui.js';
|
|
16
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
export default class EnvUpdate extends Command {
|
|
18
|
+
static summary = 'Refresh an environment runtime from swagger:get and persist connection overrides';
|
|
19
|
+
static examples = [
|
|
20
|
+
'<%= config.bin %> <%= command.id %>',
|
|
21
|
+
'<%= config.bin %> <%= command.id %> prod',
|
|
22
|
+
];
|
|
23
|
+
static args = {
|
|
24
|
+
name: Args.string({
|
|
25
|
+
description: 'Configured environment name to refresh. Defaults to the current env when omitted',
|
|
26
|
+
required: false,
|
|
27
|
+
}),
|
|
28
|
+
};
|
|
29
|
+
static flags = {
|
|
30
|
+
verbose: Flags.boolean({
|
|
31
|
+
description: 'Show detailed progress output',
|
|
32
|
+
default: false,
|
|
33
|
+
}),
|
|
34
|
+
'api-base-url': Flags.string({
|
|
35
|
+
description: 'NocoBase API base URL override. When provided, persist it to the target env before saving the refreshed runtime.',
|
|
36
|
+
}),
|
|
37
|
+
role: Flags.string({
|
|
38
|
+
description: 'Role override, sent as X-Role',
|
|
39
|
+
}),
|
|
40
|
+
token: Flags.string({
|
|
41
|
+
char: 't',
|
|
42
|
+
description: 'API key override. When provided, persist it to the target env before saving the refreshed runtime.',
|
|
43
|
+
}),
|
|
44
|
+
};
|
|
45
|
+
async run() {
|
|
46
|
+
const { args, flags } = await this.parse(EnvUpdate);
|
|
47
|
+
setVerboseMode(Boolean(flags.verbose));
|
|
48
|
+
const envName = args.name;
|
|
49
|
+
const envLabel = envName ?? (await getCurrentEnvName({ scope: resolveDefaultConfigScope() }));
|
|
50
|
+
startTask(`Updating env runtime: ${envLabel}`);
|
|
51
|
+
try {
|
|
52
|
+
const runtime = await updateEnvRuntime({
|
|
53
|
+
envName,
|
|
54
|
+
scope: resolveDefaultConfigScope(),
|
|
55
|
+
baseUrl: flags['api-base-url'],
|
|
56
|
+
role: flags.role,
|
|
57
|
+
token: flags.token,
|
|
58
|
+
configFile: path.join(path.dirname(path.dirname(path.dirname(__dirname))), 'nocobase-ctl.config.json'),
|
|
59
|
+
verbose: flags.verbose,
|
|
60
|
+
});
|
|
61
|
+
if (flags.verbose) {
|
|
62
|
+
succeedTask(`Updated env "${envLabel}" to runtime "${runtime.version}".`);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
stopTask();
|
|
66
|
+
printVerbose(`Updated env "${envLabel}" to runtime "${runtime.version}".`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
failTask(`Failed to update env "${envLabel}".`);
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
import { Args, Command } from '@oclif/core';
|
|
10
|
+
import { setCurrentEnv } from '../../lib/auth-store.js';
|
|
11
|
+
import { resolveDefaultConfigScope } from '../../lib/cli-home.js';
|
|
12
|
+
import { resolveSessionIdentity } from '../../lib/session-id.js';
|
|
13
|
+
import { isInteractiveTerminal, printInfo } from '../../lib/ui.js';
|
|
14
|
+
export default class EnvUse extends Command {
|
|
15
|
+
static summary = 'Switch the current environment';
|
|
16
|
+
static examples = [
|
|
17
|
+
'<%= config.bin %> <%= command.id %> local',
|
|
18
|
+
];
|
|
19
|
+
static args = {
|
|
20
|
+
name: Args.string({
|
|
21
|
+
description: 'Configured environment name to switch to',
|
|
22
|
+
required: true,
|
|
23
|
+
}),
|
|
24
|
+
};
|
|
25
|
+
async run() {
|
|
26
|
+
const { args } = await this.parse(EnvUse);
|
|
27
|
+
await setCurrentEnv(args.name, { scope: resolveDefaultConfigScope() });
|
|
28
|
+
this.log(`Current env: ${args.name}`);
|
|
29
|
+
const identity = resolveSessionIdentity();
|
|
30
|
+
if (identity || !isInteractiveTerminal()) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
this.log('Session mode is not enabled for the current shell or runtime.');
|
|
34
|
+
this.log('Without session mode, switching the current env here can affect other sessions running in parallel.');
|
|
35
|
+
this.log('');
|
|
36
|
+
printInfo('Run `nb session setup` to enable session mode for this shell or runtime.');
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
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 { runPromptCatalog, } from "../../lib/prompt-catalog.js";
|
|
11
|
+
import { applyCliLocale, CLI_LOCALE_FLAG_DESCRIPTION, CLI_LOCALE_FLAG_OPTIONS, } from "../../lib/cli-locale.js";
|
|
12
|
+
import { runPromptCatalogWebUI } from "../../lib/prompt-web-ui.js";
|
|
13
|
+
import PromptsTest from "./prompts-test.js";
|
|
14
|
+
function buildWebUiStagesFromTestPrompts(c) {
|
|
15
|
+
return [
|
|
16
|
+
{
|
|
17
|
+
sectionTitle: 'Step 1',
|
|
18
|
+
sectionDescription: 'intro, file, name, and boolean',
|
|
19
|
+
catalog: {
|
|
20
|
+
intro1: c.intro1,
|
|
21
|
+
file: c.file,
|
|
22
|
+
text: c.text,
|
|
23
|
+
boolean: c.boolean,
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
sectionTitle: 'Step 2',
|
|
28
|
+
sectionDescription: 'select, then the option1 run hook',
|
|
29
|
+
catalog: {
|
|
30
|
+
select: c.select,
|
|
31
|
+
onOption1: c.onOption1,
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
sectionTitle: 'Step 3',
|
|
36
|
+
sectionDescription: 'password, integer, and outro',
|
|
37
|
+
catalog: {
|
|
38
|
+
password: c.password,
|
|
39
|
+
integer: c.integer,
|
|
40
|
+
outro1: c.outro1,
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
];
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* With `--ui`: `runPromptCatalogWebUI` using `stages` + `sectionTitle`, then
|
|
47
|
+
* `runPromptCatalog` on the same catalog as `prompts-test`. Without `--ui`, the same terminal prompt
|
|
48
|
+
* behavior as `prompts-test` (handy for comparing).
|
|
49
|
+
*/
|
|
50
|
+
export default class PromptsStages extends Command {
|
|
51
|
+
static hidden = true;
|
|
52
|
+
static args = {
|
|
53
|
+
file: Args.string({ description: 'file to read', required: false }),
|
|
54
|
+
};
|
|
55
|
+
static description = 'Demo: with --ui, `runPromptCatalogWebUI` with `stages` (section headings in the form); without --ui, same as `prompts-test`. The submitted preset runs through the same `PromptsTest.prompts` catalog as `prompts-test`.';
|
|
56
|
+
static examples = [
|
|
57
|
+
'<%= config.bin %> <%= command.id %> # same terminal flow as `prompts-test`',
|
|
58
|
+
'<%= config.bin %> <%= command.id %> --ui # `runPromptCatalogWebUI` with `stages` + `sectionTitle`',
|
|
59
|
+
'<%= config.bin %> <%= command.id %> -y ./README.md -p secret --ui',
|
|
60
|
+
];
|
|
61
|
+
static flags = {
|
|
62
|
+
ui: Flags.boolean({
|
|
63
|
+
description: 'Open the localhost form: `runPromptCatalogWebUI` with `stages` and `sectionTitle` (vs `prompts-test --ui`, which uses one `catalog`). Without --ui, behavior matches `prompts-test` (terminal prompt layer in TTY / presets).',
|
|
64
|
+
default: false,
|
|
65
|
+
}),
|
|
66
|
+
yes: Flags.boolean({
|
|
67
|
+
char: 'y',
|
|
68
|
+
description: 'Accept defaults only in the terminal (no extra interactive prompts after submit); same semantics as `prompts-test` when used with the submitted or default preset.',
|
|
69
|
+
default: false,
|
|
70
|
+
}),
|
|
71
|
+
locale: Flags.string({
|
|
72
|
+
description: CLI_LOCALE_FLAG_DESCRIPTION,
|
|
73
|
+
options: CLI_LOCALE_FLAG_OPTIONS,
|
|
74
|
+
}),
|
|
75
|
+
file: Flags.string({
|
|
76
|
+
char: 'f',
|
|
77
|
+
description: 'Merged into initialValues.file (default for the text prompt when set)',
|
|
78
|
+
}),
|
|
79
|
+
text: Flags.string({
|
|
80
|
+
char: 't',
|
|
81
|
+
description: 'Merged into initialValues.text (default for the text prompt when set)',
|
|
82
|
+
}),
|
|
83
|
+
boolean: Flags.boolean({
|
|
84
|
+
char: 'b',
|
|
85
|
+
description: 'Merged into initialValues.boolean (default for confirm when set)',
|
|
86
|
+
allowNo: true,
|
|
87
|
+
}),
|
|
88
|
+
select: Flags.string({
|
|
89
|
+
char: 's',
|
|
90
|
+
description: 'Merged into initialValues.select (default for select when set)',
|
|
91
|
+
options: ['option1', 'option2', 'option3'],
|
|
92
|
+
}),
|
|
93
|
+
password: Flags.string({
|
|
94
|
+
char: 'p',
|
|
95
|
+
description: 'Merged into initialValues.password (required when -y / non-TTY if password is required)',
|
|
96
|
+
}),
|
|
97
|
+
integer: Flags.integer({
|
|
98
|
+
char: 'i',
|
|
99
|
+
description: 'Merged into initialValues.integer (default for integer prompt when set)',
|
|
100
|
+
}),
|
|
101
|
+
};
|
|
102
|
+
async run() {
|
|
103
|
+
const { args, flags } = await this.parse(PromptsStages);
|
|
104
|
+
applyCliLocale(flags.locale);
|
|
105
|
+
let presetValues = this.buildPresetValuesFromParsed(args, flags);
|
|
106
|
+
if (flags.ui) {
|
|
107
|
+
presetValues = await runPromptCatalogWebUI({
|
|
108
|
+
stages: buildWebUiStagesFromTestPrompts(PromptsTest.prompts),
|
|
109
|
+
values: presetValues,
|
|
110
|
+
pageTitle: 'nb prompts-stages — Web UI',
|
|
111
|
+
documentHeading: 'nb prompts-stages — `stages` demo',
|
|
112
|
+
onServerStart: ({ host, port, url }) => {
|
|
113
|
+
this.log(`Local Web UI (multi-stage) ready — ${url} (listening on ${host}:${port}). Submit the form in the browser to continue.`);
|
|
114
|
+
},
|
|
115
|
+
onOpenBrowserError: (url, err) => {
|
|
116
|
+
this.log(`Open this URL in a browser: ${url} (${err instanceof Error ? err.message : String(err)})`);
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
const results = await runPromptCatalog(PromptsTest.prompts, {
|
|
121
|
+
initialValues: {},
|
|
122
|
+
values: presetValues,
|
|
123
|
+
yes: flags.yes || flags.ui,
|
|
124
|
+
});
|
|
125
|
+
this.log(JSON.stringify(results, null, 2));
|
|
126
|
+
}
|
|
127
|
+
buildPresetValuesFromParsed(args, flags) {
|
|
128
|
+
const preset = {};
|
|
129
|
+
const file = args.file ?? flags.file;
|
|
130
|
+
if (file !== undefined) {
|
|
131
|
+
preset.file = file;
|
|
132
|
+
}
|
|
133
|
+
if (flags.text !== undefined) {
|
|
134
|
+
preset.text = flags.text;
|
|
135
|
+
}
|
|
136
|
+
if (flags.boolean !== undefined) {
|
|
137
|
+
preset.boolean = flags.boolean;
|
|
138
|
+
}
|
|
139
|
+
if (flags.select !== undefined) {
|
|
140
|
+
preset.select = flags.select;
|
|
141
|
+
}
|
|
142
|
+
if (flags.password !== undefined) {
|
|
143
|
+
preset.password = flags.password;
|
|
144
|
+
}
|
|
145
|
+
if (flags.integer !== undefined) {
|
|
146
|
+
preset.integer = flags.integer;
|
|
147
|
+
}
|
|
148
|
+
return preset;
|
|
149
|
+
}
|
|
150
|
+
}
|