@nocobase/cli 2.3.0-beta.6 → 2.4.0-alpha.2
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/dist/commands/api/swagger/get.js +96 -0
- package/dist/commands/api/swagger/index.js +20 -0
- package/dist/commands/api/swagger/list.js +92 -0
- package/dist/commands/config/set.js +1 -0
- package/dist/commands/env/add.js +6 -0
- package/dist/commands/env/update.js +13 -0
- package/dist/commands/init.js +51 -5
- package/dist/commands/install.js +60 -3
- package/dist/commands/portal/config.js +99 -0
- package/dist/commands/portal/create.js +96 -0
- package/dist/commands/portal/deploy.js +81 -0
- package/dist/commands/portal/destroy.js +126 -0
- package/dist/commands/portal/dev.js +73 -0
- package/dist/commands/portal/index.js +20 -0
- package/dist/commands/portal/info.js +82 -0
- package/dist/commands/portal/list.js +98 -0
- package/dist/commands/portal/pull.js +113 -0
- package/dist/commands/portal/push.js +79 -0
- package/dist/commands/source/dev.js +1 -1
- package/dist/lib/api-client.js +35 -9
- package/dist/lib/app-client-entry-mode.js +28 -0
- package/dist/lib/app-managed-resources.js +2 -0
- package/dist/lib/auth-store.js +55 -2
- package/dist/lib/bootstrap.js +3 -1
- package/dist/lib/cli-config.js +20 -1
- package/dist/lib/env-auth.js +2 -36
- package/dist/lib/env-command-config.js +1 -0
- package/dist/lib/env-config.js +11 -0
- package/dist/lib/env-portal-config.js +30 -0
- package/dist/lib/env-proxy.js +154 -7
- package/dist/lib/managed-env-file.js +119 -9
- package/dist/lib/managed-init-env.js +4 -1
- package/dist/lib/portal-build-html.js +27 -0
- package/dist/lib/portal-command-env.js +31 -0
- package/dist/lib/portal-config.js +119 -0
- package/dist/lib/portal-configure.js +110 -0
- package/dist/lib/portal-create.js +515 -0
- package/dist/lib/portal-deploy.js +266 -0
- package/dist/lib/portal-destroy.js +114 -0
- package/dist/lib/portal-dev.js +78 -0
- package/dist/lib/portal-env-files.js +54 -0
- package/dist/lib/portal-info.js +28 -0
- package/dist/lib/portal-list.js +205 -0
- package/dist/lib/portal-path-safety.js +76 -0
- package/dist/lib/portal-source.js +692 -0
- package/dist/lib/prompt-catalog-core.js +2 -2
- package/dist/lib/prompt-catalog-terminal.js +4 -5
- package/dist/lib/prompt-web-ui.js +12 -6
- package/dist/lib/proxy-caddy.js +2 -0
- package/dist/lib/proxy-nginx.js +1 -0
- package/dist/lib/run-npm.js +85 -20
- package/dist/lib/swagger-command.js +52 -0
- package/dist/lib/ui.js +28 -1
- package/dist/locale/en-US.json +245 -1
- package/dist/locale/zh-CN.json +245 -1
- package/package.json +5 -2
|
@@ -0,0 +1,99 @@
|
|
|
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, getEnv, setEnvPortalPath } from '../../lib/auth-store.js';
|
|
11
|
+
import { resolveDefaultConfigScope } from '../../lib/cli-home.js';
|
|
12
|
+
import { translateCli } from '../../lib/cli-locale.js';
|
|
13
|
+
import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
|
|
14
|
+
import { configurePortalWorkspace } from '../../lib/portal-configure.js';
|
|
15
|
+
import { printInfo, printSuccess } from '../../lib/ui.js';
|
|
16
|
+
const portalConfigureText = (key, values, fallback) => translateCli(`commands.portalConfigure.${key}`, values, { fallback });
|
|
17
|
+
export default class PortalConfig extends Command {
|
|
18
|
+
static summary = 'Update portal source configuration';
|
|
19
|
+
static examples = [
|
|
20
|
+
'<%= config.bin %> <%= command.id %> customer --path ./portals/customer',
|
|
21
|
+
'<%= config.bin %> <%= command.id %> customer --source-storage nocobase',
|
|
22
|
+
'<%= config.bin %> <%= command.id %> customer --source-storage git --git-repo git@github.com:nocobase/customer-portal.git',
|
|
23
|
+
'<%= config.bin %> <%= command.id %> customer --git-branch main --git-path portals/customer',
|
|
24
|
+
];
|
|
25
|
+
static args = {
|
|
26
|
+
portal: Args.string({
|
|
27
|
+
required: true,
|
|
28
|
+
description: 'Portal name',
|
|
29
|
+
}),
|
|
30
|
+
};
|
|
31
|
+
static flags = {
|
|
32
|
+
env: Flags.string({
|
|
33
|
+
char: 'e',
|
|
34
|
+
description: 'CLI env name; omitted uses the current env',
|
|
35
|
+
}),
|
|
36
|
+
yes: Flags.boolean({
|
|
37
|
+
char: 'y',
|
|
38
|
+
description: 'Confirm using --env when it targets a different env than the current env',
|
|
39
|
+
default: false,
|
|
40
|
+
}),
|
|
41
|
+
'source-storage': Flags.string({
|
|
42
|
+
description: 'Where portal source code is managed',
|
|
43
|
+
options: ['nocobase', 'git'],
|
|
44
|
+
}),
|
|
45
|
+
path: Flags.string({
|
|
46
|
+
description: 'Portal development workspace directory',
|
|
47
|
+
}),
|
|
48
|
+
'git-repo': Flags.string({
|
|
49
|
+
description: 'Git repository URL used when --source-storage=git',
|
|
50
|
+
}),
|
|
51
|
+
'git-branch': Flags.string({
|
|
52
|
+
description: 'Git branch used when --source-storage=git',
|
|
53
|
+
}),
|
|
54
|
+
'git-path': Flags.string({
|
|
55
|
+
description: 'Directory inside the Git repository for this portal; defaults to the repository root',
|
|
56
|
+
}),
|
|
57
|
+
};
|
|
58
|
+
async run() {
|
|
59
|
+
const { args, flags } = await this.parse(PortalConfig);
|
|
60
|
+
const requestedEnv = hasExplicitEnvSelection(this.argv) ? flags.env : undefined;
|
|
61
|
+
const confirmed = await ensureCrossEnvConfirmed({
|
|
62
|
+
command: this,
|
|
63
|
+
requestedEnv,
|
|
64
|
+
yes: flags.yes,
|
|
65
|
+
});
|
|
66
|
+
if (!confirmed) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const scope = resolveDefaultConfigScope();
|
|
70
|
+
const envName = requestedEnv ?? (await getCurrentEnvName({ scope }));
|
|
71
|
+
const env = await getEnv(envName, { scope });
|
|
72
|
+
if (!env) {
|
|
73
|
+
this.error(portalConfigureText(requestedEnv ? 'errors.envNotConfigured' : 'errors.noEnvConfigured', { envName }, requestedEnv
|
|
74
|
+
? `Env "${envName}" is not configured. Run \`nb env add ${envName} --api-base-url <url>\` first.`
|
|
75
|
+
: 'No NocoBase env is configured yet. Run `nb init --ui` to create one first.'));
|
|
76
|
+
}
|
|
77
|
+
const result = await configurePortalWorkspace({
|
|
78
|
+
portal: args.portal,
|
|
79
|
+
env,
|
|
80
|
+
envName,
|
|
81
|
+
cliVersion: String(this.config.pjson.version ?? '').trim(),
|
|
82
|
+
sourceStorage: flags['source-storage'],
|
|
83
|
+
gitRepo: flags['git-repo'],
|
|
84
|
+
gitBranch: flags['git-branch'],
|
|
85
|
+
gitPath: flags['git-path'],
|
|
86
|
+
sourcePath: flags.path,
|
|
87
|
+
});
|
|
88
|
+
if (flags.path) {
|
|
89
|
+
await setEnvPortalPath(envName, result.portal, result.portalDir, { scope });
|
|
90
|
+
}
|
|
91
|
+
printSuccess(portalConfigureText('messages.updated', { portal: result.portal }, `Portal "${result.portal}" configuration updated.`));
|
|
92
|
+
if (result.pathUpdated) {
|
|
93
|
+
printInfo(portalConfigureText('messages.pathUpdated', { portalDir: result.portalDir }, `Development path: ${result.portalDir}`));
|
|
94
|
+
}
|
|
95
|
+
if (result.config) {
|
|
96
|
+
printInfo(portalConfigureText('messages.remoteSynced', undefined, 'Remote portal record: synced'));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
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, getEnv, setEnvPortalPath } from '../../lib/auth-store.js';
|
|
11
|
+
import { resolveDefaultConfigScope } from '../../lib/cli-home.js';
|
|
12
|
+
import { translateCli } from '../../lib/cli-locale.js';
|
|
13
|
+
import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
|
|
14
|
+
import { createPortalWorkspace } from '../../lib/portal-create.js';
|
|
15
|
+
import { printInfo, printSuccess, printWarning } from '../../lib/ui.js';
|
|
16
|
+
const DEFAULT_PORTAL_TEMPLATE = '@nocobase/portal-template-default';
|
|
17
|
+
const portalCreateText = (key, values, fallback) => translateCli(`commands.portalCreate.${key}`, values, { fallback });
|
|
18
|
+
export default class PortalCreate extends Command {
|
|
19
|
+
static summary = 'Create a local AI portal from a template';
|
|
20
|
+
static examples = [
|
|
21
|
+
'<%= config.bin %> <%= command.id %> customer',
|
|
22
|
+
'<%= config.bin %> <%= command.id %> customer --path ./portals/customer',
|
|
23
|
+
'<%= config.bin %> <%= command.id %> customer --template @nocobase/portal-template-default',
|
|
24
|
+
'<%= config.bin %> <%= command.id %> customer --env dev --yes',
|
|
25
|
+
];
|
|
26
|
+
static args = {
|
|
27
|
+
portal: Args.string({
|
|
28
|
+
required: true,
|
|
29
|
+
description: 'AI Portal name',
|
|
30
|
+
}),
|
|
31
|
+
};
|
|
32
|
+
static flags = {
|
|
33
|
+
template: Flags.string({
|
|
34
|
+
description: 'Template package, local path, or file:// URL',
|
|
35
|
+
default: DEFAULT_PORTAL_TEMPLATE,
|
|
36
|
+
}),
|
|
37
|
+
env: Flags.string({
|
|
38
|
+
char: 'e',
|
|
39
|
+
description: 'CLI env name; omitted uses the current env',
|
|
40
|
+
}),
|
|
41
|
+
yes: Flags.boolean({
|
|
42
|
+
char: 'y',
|
|
43
|
+
description: 'Confirm using --env when it targets a different env than the current env',
|
|
44
|
+
default: false,
|
|
45
|
+
}),
|
|
46
|
+
title: Flags.string({
|
|
47
|
+
description: 'Portal display title; defaults to a title generated from the portal slug',
|
|
48
|
+
}),
|
|
49
|
+
path: Flags.string({
|
|
50
|
+
description: 'Portal workspace directory; defaults to ./<portal>',
|
|
51
|
+
}),
|
|
52
|
+
force: Flags.boolean({
|
|
53
|
+
description: 'Delete the existing portal and recreate it',
|
|
54
|
+
default: false,
|
|
55
|
+
}),
|
|
56
|
+
};
|
|
57
|
+
async run() {
|
|
58
|
+
const { args, flags } = await this.parse(PortalCreate);
|
|
59
|
+
const requestedEnv = hasExplicitEnvSelection(this.argv) ? flags.env : undefined;
|
|
60
|
+
const confirmed = await ensureCrossEnvConfirmed({
|
|
61
|
+
command: this,
|
|
62
|
+
requestedEnv,
|
|
63
|
+
yes: flags.yes,
|
|
64
|
+
});
|
|
65
|
+
if (!confirmed) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const scope = resolveDefaultConfigScope();
|
|
69
|
+
const envName = requestedEnv ?? (await getCurrentEnvName({ scope }));
|
|
70
|
+
const env = await getEnv(envName, { scope });
|
|
71
|
+
if (!env) {
|
|
72
|
+
this.error(requestedEnv
|
|
73
|
+
? portalCreateText('errors.envNotConfigured', { envName }, `Env "${envName}" is not configured. Run \`nb env add ${envName} --api-base-url <url>\` first.`)
|
|
74
|
+
: portalCreateText('errors.noEnvConfigured', undefined, 'No NocoBase env is configured yet. Run `nb init --ui` to create one first.'));
|
|
75
|
+
}
|
|
76
|
+
const result = await createPortalWorkspace({
|
|
77
|
+
portal: args.portal,
|
|
78
|
+
title: flags.title,
|
|
79
|
+
template: flags.template,
|
|
80
|
+
env,
|
|
81
|
+
envName,
|
|
82
|
+
cliVersion: String(this.config.pjson.version ?? '').trim(),
|
|
83
|
+
force: flags.force,
|
|
84
|
+
sourcePath: flags.path,
|
|
85
|
+
onSkipInstall: (message) => printInfo(message),
|
|
86
|
+
});
|
|
87
|
+
await setEnvPortalPath(envName, result.portal, result.portalDir, { scope });
|
|
88
|
+
printSuccess(portalCreateText('messages.created', { portal: result.portal, portalDir: result.portalDir }, `Portal "${result.portal}" created at ${result.portalDir}`));
|
|
89
|
+
printInfo(portalCreateText('messages.app', { app: result.app }, `App: ${result.app}`));
|
|
90
|
+
printInfo(portalCreateText('messages.base', { base: result.portalBase }, `Base: ${result.portalBase}`));
|
|
91
|
+
printInfo(portalCreateText('messages.sourceStorage', { sourceStorage: result.sourceStorage }, `Source storage: ${result.sourceStorage}`));
|
|
92
|
+
if (result.installFailed) {
|
|
93
|
+
printWarning(portalCreateText('messages.installFailed', { portalDir: result.portalDir }, `Dependency installation did not finish successfully. Run \`pnpm install\` manually in ${result.portalDir}.`));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
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, getEnv } from '../../lib/auth-store.js';
|
|
11
|
+
import { resolveDefaultConfigScope } from '../../lib/cli-home.js';
|
|
12
|
+
import { translateCli } from '../../lib/cli-locale.js';
|
|
13
|
+
import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
|
|
14
|
+
import { findPortalListItem, formatPortalInfo } from '../../lib/portal-info.js';
|
|
15
|
+
import { deployPortalWorkspace } from '../../lib/portal-deploy.js';
|
|
16
|
+
import { listPortalWorkspaces } from '../../lib/portal-list.js';
|
|
17
|
+
import { printSuccess } from '../../lib/ui.js';
|
|
18
|
+
const portalDeployText = (key, values, fallback) => translateCli(`commands.portalDeploy.${key}`, values, { fallback });
|
|
19
|
+
export default class PortalDeploy extends Command {
|
|
20
|
+
static summary = 'Build and deploy a portal';
|
|
21
|
+
static examples = [
|
|
22
|
+
'<%= config.bin %> <%= command.id %> customer',
|
|
23
|
+
'<%= config.bin %> <%= command.id %> customer --env dev --yes',
|
|
24
|
+
];
|
|
25
|
+
static args = {
|
|
26
|
+
portal: Args.string({
|
|
27
|
+
required: true,
|
|
28
|
+
description: 'Portal name',
|
|
29
|
+
}),
|
|
30
|
+
};
|
|
31
|
+
static flags = {
|
|
32
|
+
env: Flags.string({
|
|
33
|
+
char: 'e',
|
|
34
|
+
description: 'CLI env name; omitted uses the current env',
|
|
35
|
+
}),
|
|
36
|
+
yes: Flags.boolean({
|
|
37
|
+
char: 'y',
|
|
38
|
+
description: 'Confirm using --env when it targets a different env than the current env',
|
|
39
|
+
default: false,
|
|
40
|
+
}),
|
|
41
|
+
};
|
|
42
|
+
async run() {
|
|
43
|
+
const { args, flags } = await this.parse(PortalDeploy);
|
|
44
|
+
const requestedEnv = hasExplicitEnvSelection(this.argv) ? flags.env : undefined;
|
|
45
|
+
const confirmed = await ensureCrossEnvConfirmed({
|
|
46
|
+
command: this,
|
|
47
|
+
requestedEnv,
|
|
48
|
+
yes: flags.yes,
|
|
49
|
+
});
|
|
50
|
+
if (!confirmed) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const scope = resolveDefaultConfigScope();
|
|
54
|
+
const envName = requestedEnv ?? (await getCurrentEnvName({ scope }));
|
|
55
|
+
const env = await getEnv(envName, { scope });
|
|
56
|
+
if (!env) {
|
|
57
|
+
this.error(portalDeployText(requestedEnv ? 'errors.envNotConfigured' : 'errors.noEnvConfigured', { envName }, requestedEnv
|
|
58
|
+
? `Env "${envName}" is not configured. Run \`nb env add ${envName} --api-base-url <url>\` first.`
|
|
59
|
+
: 'No NocoBase env is configured yet. Run `nb init --ui` to create one first.'));
|
|
60
|
+
}
|
|
61
|
+
const result = await deployPortalWorkspace({
|
|
62
|
+
portal: args.portal,
|
|
63
|
+
env,
|
|
64
|
+
envName,
|
|
65
|
+
cliVersion: String(this.config.pjson.version ?? '').trim(),
|
|
66
|
+
});
|
|
67
|
+
printSuccess(portalDeployText('messages.deployed', { portal: result.portal }, `Portal "${result.portal}" deployed.`));
|
|
68
|
+
const info = await listPortalWorkspaces({
|
|
69
|
+
env,
|
|
70
|
+
envName,
|
|
71
|
+
cliVersion: String(this.config.pjson.version ?? '').trim(),
|
|
72
|
+
});
|
|
73
|
+
const portal = findPortalListItem(info.items, result.portal);
|
|
74
|
+
if (!portal) {
|
|
75
|
+
this.error(translateCli(`commands.portalInfo.errors.notFound`, { portal: result.portal }, {
|
|
76
|
+
fallback: `Portal "${result.portal}" was not found.`,
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
this.log(formatPortalInfo(portal));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
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, getEnv, unsetEnvPortalPath } from '../../lib/auth-store.js';
|
|
11
|
+
import { resolveDefaultConfigScope } from '../../lib/cli-home.js';
|
|
12
|
+
import { translateCli } from '../../lib/cli-locale.js';
|
|
13
|
+
import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
|
|
14
|
+
import { confirm } from "../../lib/inquirer.js";
|
|
15
|
+
import { destroyPortalWorkspace } from '../../lib/portal-destroy.js';
|
|
16
|
+
import { isInteractiveTerminal, printInfo, printSuccess } from '../../lib/ui.js';
|
|
17
|
+
const portalDestroyText = (key, values, fallback) => translateCli(`commands.portalDestroy.${key}`, values, { fallback });
|
|
18
|
+
function portalDestroyStatus(status) {
|
|
19
|
+
return portalDestroyText(`statuses.${status}`, undefined, status);
|
|
20
|
+
}
|
|
21
|
+
async function ensureDestroyConfirmed(options) {
|
|
22
|
+
if (options.yes) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
if (!isInteractiveTerminal()) {
|
|
26
|
+
options.command.error(portalDestroyText('errors.confirmationRequired', undefined, 'Refusing to destroy a portal in non-interactive mode without --yes.'));
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
return Boolean(await confirm({
|
|
30
|
+
message: portalDestroyText('prompts.confirm', { portal: options.portal }, `Destroy portal "${options.portal}" and delete its deployment directory?`),
|
|
31
|
+
default: false,
|
|
32
|
+
}));
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export default class PortalDestroy extends Command {
|
|
39
|
+
static summary = 'Destroy a portal record and deployed files';
|
|
40
|
+
static examples = [
|
|
41
|
+
'<%= config.bin %> <%= command.id %> customer --yes',
|
|
42
|
+
'<%= config.bin %> <%= command.id %> customer --delete-dev-path --yes',
|
|
43
|
+
'<%= config.bin %> <%= command.id %> customer --env dev --yes',
|
|
44
|
+
'<%= config.bin %> <%= command.id %> customer --force --yes',
|
|
45
|
+
];
|
|
46
|
+
static args = {
|
|
47
|
+
portal: Args.string({
|
|
48
|
+
required: true,
|
|
49
|
+
description: 'Portal name',
|
|
50
|
+
}),
|
|
51
|
+
};
|
|
52
|
+
static flags = {
|
|
53
|
+
env: Flags.string({
|
|
54
|
+
char: 'e',
|
|
55
|
+
description: 'CLI env name; omitted uses the current env',
|
|
56
|
+
}),
|
|
57
|
+
yes: Flags.boolean({
|
|
58
|
+
char: 'y',
|
|
59
|
+
description: 'Skip confirmation prompts',
|
|
60
|
+
default: false,
|
|
61
|
+
}),
|
|
62
|
+
force: Flags.boolean({
|
|
63
|
+
description: 'Ignore missing portal records or deployment files',
|
|
64
|
+
default: false,
|
|
65
|
+
}),
|
|
66
|
+
'delete-dev-path': Flags.boolean({
|
|
67
|
+
char: 'D',
|
|
68
|
+
description: 'Delete the portal development directory in addition to the deployed portal',
|
|
69
|
+
default: false,
|
|
70
|
+
}),
|
|
71
|
+
};
|
|
72
|
+
async run() {
|
|
73
|
+
const { args, flags } = await this.parse(PortalDestroy);
|
|
74
|
+
const requestedEnv = hasExplicitEnvSelection(this.argv) ? flags.env : undefined;
|
|
75
|
+
const crossEnvConfirmed = await ensureCrossEnvConfirmed({
|
|
76
|
+
command: this,
|
|
77
|
+
requestedEnv,
|
|
78
|
+
yes: flags.yes,
|
|
79
|
+
});
|
|
80
|
+
if (!crossEnvConfirmed) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const destroyConfirmed = await ensureDestroyConfirmed({
|
|
84
|
+
command: this,
|
|
85
|
+
portal: args.portal,
|
|
86
|
+
yes: flags.yes,
|
|
87
|
+
});
|
|
88
|
+
if (!destroyConfirmed) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const scope = resolveDefaultConfigScope();
|
|
92
|
+
const envName = requestedEnv ?? (await getCurrentEnvName({ scope }));
|
|
93
|
+
const env = await getEnv(envName, { scope });
|
|
94
|
+
if (!env) {
|
|
95
|
+
this.error(portalDestroyText(requestedEnv ? 'errors.envNotConfigured' : 'errors.noEnvConfigured', { envName }, requestedEnv
|
|
96
|
+
? `Env "${envName}" is not configured. Run \`nb env add ${envName} --api-base-url <url>\` first.`
|
|
97
|
+
: 'No NocoBase env is configured yet. Run `nb init --ui` to create one first.'));
|
|
98
|
+
}
|
|
99
|
+
const result = await destroyPortalWorkspace({
|
|
100
|
+
portal: args.portal,
|
|
101
|
+
env,
|
|
102
|
+
envName,
|
|
103
|
+
cliVersion: String(this.config.pjson.version ?? '').trim(),
|
|
104
|
+
force: flags.force,
|
|
105
|
+
deleteDevPath: flags['delete-dev-path'],
|
|
106
|
+
});
|
|
107
|
+
await unsetEnvPortalPath(envName, result.portal, { scope });
|
|
108
|
+
printSuccess(portalDestroyText('messages.destroyed', { portal: result.portal }, `Portal "${result.portal}" destroyed.`));
|
|
109
|
+
printInfo(portalDestroyText('messages.mode', { mode: result.mode }, `Mode: ${result.mode}`));
|
|
110
|
+
printInfo(portalDestroyText('messages.app', { app: result.app }, `App: ${result.app}`));
|
|
111
|
+
printInfo(portalDestroyText('messages.base', { base: result.portalBase }, `Base: ${result.portalBase}`));
|
|
112
|
+
printInfo(portalDestroyText('messages.record', { status: portalDestroyStatus(result.recordDeleted ? 'deleted' : 'missing') }, `Record: ${result.recordDeleted ? 'deleted' : 'missing'}`));
|
|
113
|
+
const deploymentPathStatus = result.deploymentPathDeleted ? 'deleted' : 'missing';
|
|
114
|
+
printInfo(portalDestroyText('messages.deploymentPath', { dir: result.deploymentPath, status: portalDestroyStatus(deploymentPathStatus) }, `Deployment path: ${deploymentPathStatus} (${result.deploymentPath})`));
|
|
115
|
+
let developmentPathStatus = 'missing';
|
|
116
|
+
if (result.developmentPath) {
|
|
117
|
+
developmentPathStatus = result.developmentPathDeleted ? 'deleted' : 'retained';
|
|
118
|
+
}
|
|
119
|
+
printInfo(portalDestroyText(result.developmentPath ? 'messages.developmentPath' : 'messages.developmentPathMissing', {
|
|
120
|
+
dir: result.developmentPath,
|
|
121
|
+
status: portalDestroyStatus(developmentPathStatus),
|
|
122
|
+
}, result.developmentPath
|
|
123
|
+
? `Development path: ${developmentPathStatus} (${result.developmentPath})`
|
|
124
|
+
: 'Development path: missing'));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
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, getEnv } from '../../lib/auth-store.js';
|
|
11
|
+
import { resolveDefaultConfigScope } from '../../lib/cli-home.js';
|
|
12
|
+
import { translateCli } from '../../lib/cli-locale.js';
|
|
13
|
+
import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
|
|
14
|
+
import { devPortalWorkspace } from '../../lib/portal-dev.js';
|
|
15
|
+
import { printInfo } from '../../lib/ui.js';
|
|
16
|
+
const portalDevText = (key, values, fallback) => translateCli(`commands.portalDev.${key}`, values, { fallback });
|
|
17
|
+
export default class PortalDev extends Command {
|
|
18
|
+
static summary = 'Start a portal in development mode';
|
|
19
|
+
static examples = [
|
|
20
|
+
'<%= config.bin %> <%= command.id %> customer',
|
|
21
|
+
'<%= config.bin %> <%= command.id %> customer --env dev --yes',
|
|
22
|
+
];
|
|
23
|
+
static args = {
|
|
24
|
+
portal: Args.string({
|
|
25
|
+
required: true,
|
|
26
|
+
description: 'Portal name',
|
|
27
|
+
}),
|
|
28
|
+
};
|
|
29
|
+
static flags = {
|
|
30
|
+
env: Flags.string({
|
|
31
|
+
char: 'e',
|
|
32
|
+
description: 'CLI env name; omitted uses the current env',
|
|
33
|
+
}),
|
|
34
|
+
yes: Flags.boolean({
|
|
35
|
+
char: 'y',
|
|
36
|
+
description: 'Confirm using --env when it targets a different env than the current env',
|
|
37
|
+
default: false,
|
|
38
|
+
}),
|
|
39
|
+
};
|
|
40
|
+
async run() {
|
|
41
|
+
const { args, flags } = await this.parse(PortalDev);
|
|
42
|
+
const requestedEnv = hasExplicitEnvSelection(this.argv) ? flags.env : undefined;
|
|
43
|
+
const confirmed = await ensureCrossEnvConfirmed({
|
|
44
|
+
command: this,
|
|
45
|
+
requestedEnv,
|
|
46
|
+
yes: flags.yes,
|
|
47
|
+
});
|
|
48
|
+
if (!confirmed) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const scope = resolveDefaultConfigScope();
|
|
52
|
+
const envName = requestedEnv ?? (await getCurrentEnvName({ scope }));
|
|
53
|
+
const env = await getEnv(envName, { scope });
|
|
54
|
+
if (!env) {
|
|
55
|
+
this.error(portalDevText(requestedEnv ? 'errors.envNotConfigured' : 'errors.noEnvConfigured', { envName }, requestedEnv
|
|
56
|
+
? `Env "${envName}" is not configured. Run \`nb env add ${envName} --api-base-url <url>\` first.`
|
|
57
|
+
: 'No NocoBase env is configured yet. Run `nb init --ui` to create one first.'));
|
|
58
|
+
}
|
|
59
|
+
await devPortalWorkspace({
|
|
60
|
+
portal: args.portal,
|
|
61
|
+
env,
|
|
62
|
+
envName,
|
|
63
|
+
cliVersion: String(this.config?.pjson?.version ?? '').trim(),
|
|
64
|
+
onStart: (result) => {
|
|
65
|
+
printInfo(portalDevText('messages.starting', { portal: result.portal }, `Starting portal "${result.portal}"...`));
|
|
66
|
+
printInfo(portalDevText('messages.mode', { mode: result.mode }, `Mode: ${result.mode}`));
|
|
67
|
+
printInfo(portalDevText('messages.app', { app: result.app }, `App: ${result.app}`));
|
|
68
|
+
printInfo(portalDevText('messages.base', { base: result.portalBase }, `Base: ${result.portalBase}`));
|
|
69
|
+
printInfo(portalDevText('messages.dir', { dir: result.portalDir }, `Dir: ${result.portalDir}`));
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
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, loadHelpClass } from '@oclif/core';
|
|
10
|
+
export default class Portal extends Command {
|
|
11
|
+
static summary = 'Manage portals';
|
|
12
|
+
async run() {
|
|
13
|
+
await this.parse(Portal);
|
|
14
|
+
const Help = await loadHelpClass(this.config);
|
|
15
|
+
await new Help(this.config, this.config.pjson.oclif.helpOptions ?? this.config.pjson.helpOptions).showHelp([
|
|
16
|
+
this.id ?? 'portal',
|
|
17
|
+
...this.argv,
|
|
18
|
+
]);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
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, getEnv } from '../../lib/auth-store.js';
|
|
11
|
+
import { resolveDefaultConfigScope } from '../../lib/cli-home.js';
|
|
12
|
+
import { translateCli } from '../../lib/cli-locale.js';
|
|
13
|
+
import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
|
|
14
|
+
import { findPortalListItem, formatPortalInfo } from '../../lib/portal-info.js';
|
|
15
|
+
import { listPortalWorkspaces, toPortalOutputItem } from '../../lib/portal-list.js';
|
|
16
|
+
const portalInfoText = (key, values, fallback) => translateCli(`commands.portalInfo.${key}`, values, { fallback });
|
|
17
|
+
export default class PortalInfo extends Command {
|
|
18
|
+
static summary = 'Show portal record and local file details';
|
|
19
|
+
static examples = [
|
|
20
|
+
'<%= config.bin %> <%= command.id %> customer',
|
|
21
|
+
'<%= config.bin %> <%= command.id %> customer --env dev --yes',
|
|
22
|
+
'<%= config.bin %> <%= command.id %> customer --json',
|
|
23
|
+
];
|
|
24
|
+
static args = {
|
|
25
|
+
portal: Args.string({
|
|
26
|
+
required: true,
|
|
27
|
+
description: 'Portal name',
|
|
28
|
+
}),
|
|
29
|
+
};
|
|
30
|
+
static flags = {
|
|
31
|
+
env: Flags.string({
|
|
32
|
+
char: 'e',
|
|
33
|
+
description: 'CLI env name; omitted uses the current env',
|
|
34
|
+
}),
|
|
35
|
+
yes: Flags.boolean({
|
|
36
|
+
char: 'y',
|
|
37
|
+
description: 'Confirm using --env when it targets a different env than the current env',
|
|
38
|
+
default: false,
|
|
39
|
+
}),
|
|
40
|
+
'json-output': Flags.boolean({
|
|
41
|
+
char: 'j',
|
|
42
|
+
aliases: ['json'],
|
|
43
|
+
description: 'Print portal details as JSON',
|
|
44
|
+
default: false,
|
|
45
|
+
}),
|
|
46
|
+
};
|
|
47
|
+
async run() {
|
|
48
|
+
const { args, flags } = await this.parse(PortalInfo);
|
|
49
|
+
const requestedEnv = hasExplicitEnvSelection(this.argv) ? flags.env : undefined;
|
|
50
|
+
const confirmed = await ensureCrossEnvConfirmed({
|
|
51
|
+
command: this,
|
|
52
|
+
requestedEnv,
|
|
53
|
+
yes: flags.yes,
|
|
54
|
+
});
|
|
55
|
+
if (!confirmed) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const scope = resolveDefaultConfigScope();
|
|
59
|
+
const envName = requestedEnv ?? (await getCurrentEnvName({ scope }));
|
|
60
|
+
const env = await getEnv(envName, { scope });
|
|
61
|
+
if (!env) {
|
|
62
|
+
this.error(portalInfoText(requestedEnv ? 'errors.envNotConfigured' : 'errors.noEnvConfigured', { envName }, requestedEnv
|
|
63
|
+
? `Env "${envName}" is not configured. Run \`nb env add ${envName} --api-base-url <url>\` first.`
|
|
64
|
+
: 'No NocoBase env is configured yet. Run `nb init --ui` to create one first.'));
|
|
65
|
+
}
|
|
66
|
+
const result = await listPortalWorkspaces({
|
|
67
|
+
env,
|
|
68
|
+
envName,
|
|
69
|
+
cliVersion: String(this.config.pjson.version ?? '').trim(),
|
|
70
|
+
});
|
|
71
|
+
const portal = findPortalListItem(result.items, args.portal);
|
|
72
|
+
if (!portal) {
|
|
73
|
+
this.error(portalInfoText('errors.notFound', { portal: args.portal }, `Portal "${args.portal}" was not found.`));
|
|
74
|
+
}
|
|
75
|
+
const outputItem = toPortalOutputItem(portal);
|
|
76
|
+
if (flags['json-output']) {
|
|
77
|
+
this.log(JSON.stringify(outputItem, null, 2));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
this.log(formatPortalInfo(portal));
|
|
81
|
+
}
|
|
82
|
+
}
|