@nocobase/cli 2.1.0-alpha.20 → 2.1.0-alpha.21
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/README.md +256 -89
- package/README.zh-CN.md +332 -0
- package/bin/run.js +21 -2
- package/dist/commands/build.js +7 -1
- package/dist/commands/db/logs.js +85 -0
- package/dist/commands/db/ps.js +60 -0
- package/dist/commands/db/shared.js +81 -0
- package/dist/commands/db/start.js +55 -7
- package/dist/commands/db/stop.js +70 -0
- package/dist/commands/dev.js +112 -21
- package/dist/commands/down.js +193 -0
- package/dist/commands/download.js +622 -183
- package/dist/commands/env/add.js +233 -131
- package/dist/commands/env/auth.js +9 -8
- package/dist/commands/init.js +696 -103
- package/dist/commands/install.js +1588 -566
- package/dist/commands/logs.js +90 -0
- package/dist/commands/pm/disable.js +35 -3
- package/dist/commands/pm/enable.js +35 -3
- package/dist/commands/pm/list.js +37 -4
- package/dist/commands/prompts-stages.js +144 -0
- package/dist/commands/prompts-test.js +175 -0
- package/dist/commands/ps.js +116 -0
- package/dist/commands/start.js +171 -15
- package/dist/commands/stop.js +90 -0
- package/dist/commands/upgrade.js +559 -11
- package/dist/lib/app-runtime.js +142 -0
- package/dist/lib/auth-store.js +44 -3
- package/dist/lib/bootstrap.js +7 -3
- package/dist/lib/env-auth.js +427 -82
- package/dist/lib/prompt-catalog.js +552 -0
- package/dist/lib/prompt-validators.js +184 -0
- package/dist/lib/prompt-web-ui.js +2027 -0
- package/dist/lib/run-npm.js +71 -7
- package/package.json +3 -3
- package/dist/commands/restart.js +0 -32
- package/dist/lib/init-browser-wizard.js +0 -431
|
@@ -6,17 +6,65 @@
|
|
|
6
6
|
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
|
-
import {
|
|
9
|
+
import { Command, Flags } from '@oclif/core';
|
|
10
|
+
import { formatMissingManagedAppEnvMessage, startDockerContainer, } from '../../lib/app-runtime.js';
|
|
11
|
+
import { failTask, startTask, succeedTask } from '../../lib/ui.js';
|
|
12
|
+
import { formatUnmanagedDbMessage, resolveDbRuntime } from './shared.js';
|
|
13
|
+
function formatDbStartFailure(envName, message) {
|
|
14
|
+
if (/does not exist/i.test(message)) {
|
|
15
|
+
return [
|
|
16
|
+
`Can't start the database for "${envName}" yet.`,
|
|
17
|
+
'The saved built-in database container for this env could not be found on this machine.',
|
|
18
|
+
'Try reinstalling the env, or check whether the container was removed outside the CLI.',
|
|
19
|
+
`Details: ${message}`,
|
|
20
|
+
].join('\n');
|
|
21
|
+
}
|
|
22
|
+
return [
|
|
23
|
+
`Couldn't start the database for "${envName}".`,
|
|
24
|
+
'Check that the built-in database runtime for this env is still available, then try again.',
|
|
25
|
+
`Details: ${message}`,
|
|
26
|
+
].join('\n');
|
|
27
|
+
}
|
|
10
28
|
export default class DbStart extends Command {
|
|
11
|
-
static
|
|
12
|
-
file: Args.string({ description: 'file to read' }),
|
|
13
|
-
};
|
|
14
|
-
static description = 'describe the command here';
|
|
29
|
+
static description = 'Start the built-in database container for the selected env.';
|
|
15
30
|
static examples = [
|
|
16
31
|
'<%= config.bin %> <%= command.id %>',
|
|
32
|
+
'<%= config.bin %> <%= command.id %> --env app1',
|
|
33
|
+
'<%= config.bin %> <%= command.id %> --env app1 --verbose',
|
|
17
34
|
];
|
|
18
|
-
static flags = {
|
|
35
|
+
static flags = {
|
|
36
|
+
env: Flags.string({
|
|
37
|
+
char: 'e',
|
|
38
|
+
description: 'CLI env name to start the built-in database for. Defaults to the current env when omitted',
|
|
39
|
+
}),
|
|
40
|
+
verbose: Flags.boolean({
|
|
41
|
+
description: 'Show raw startup output from the underlying Docker command',
|
|
42
|
+
default: false,
|
|
43
|
+
}),
|
|
44
|
+
};
|
|
19
45
|
async run() {
|
|
20
|
-
const {
|
|
46
|
+
const { flags } = await this.parse(DbStart);
|
|
47
|
+
const requestedEnv = flags.env?.trim() || undefined;
|
|
48
|
+
const runtime = await resolveDbRuntime(requestedEnv);
|
|
49
|
+
if (!runtime) {
|
|
50
|
+
this.error(formatMissingManagedAppEnvMessage(requestedEnv));
|
|
51
|
+
}
|
|
52
|
+
if (runtime.kind !== 'builtin') {
|
|
53
|
+
this.error(formatUnmanagedDbMessage('start', runtime));
|
|
54
|
+
}
|
|
55
|
+
startTask(`Starting the built-in database for "${runtime.envName}"...`);
|
|
56
|
+
try {
|
|
57
|
+
const state = await startDockerContainer(runtime.containerName, {
|
|
58
|
+
stdio: flags.verbose ? 'inherit' : 'ignore',
|
|
59
|
+
});
|
|
60
|
+
succeedTask(state === 'already-running'
|
|
61
|
+
? `The built-in database is already running for "${runtime.envName}"${runtime.address ? ` at ${runtime.address}` : ''}.`
|
|
62
|
+
: `The built-in database is running for "${runtime.envName}"${runtime.address ? ` at ${runtime.address}` : ''}.`);
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
66
|
+
failTask(`Failed to start the built-in database for "${runtime.envName}".`);
|
|
67
|
+
this.error(formatDbStartFailure(runtime.envName, message));
|
|
68
|
+
}
|
|
21
69
|
}
|
|
22
70
|
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
import { Command, Flags } from '@oclif/core';
|
|
10
|
+
import { formatMissingManagedAppEnvMessage, stopDockerContainer, } from '../../lib/app-runtime.js';
|
|
11
|
+
import { failTask, startTask, succeedTask } from '../../lib/ui.js';
|
|
12
|
+
import { formatUnmanagedDbMessage, resolveDbRuntime } from './shared.js';
|
|
13
|
+
function formatDbStopFailure(envName, message) {
|
|
14
|
+
if (/does not exist/i.test(message)) {
|
|
15
|
+
return [
|
|
16
|
+
`Can't stop the database for "${envName}" yet.`,
|
|
17
|
+
'The saved built-in database container for this env could not be found on this machine.',
|
|
18
|
+
'If it was removed manually, reinstall or reconnect the env before trying again.',
|
|
19
|
+
`Details: ${message}`,
|
|
20
|
+
].join('\n');
|
|
21
|
+
}
|
|
22
|
+
return [
|
|
23
|
+
`Couldn't stop the database for "${envName}".`,
|
|
24
|
+
'Check that the built-in database runtime for this env is still available, then try again.',
|
|
25
|
+
`Details: ${message}`,
|
|
26
|
+
].join('\n');
|
|
27
|
+
}
|
|
28
|
+
export default class DbStop extends Command {
|
|
29
|
+
static description = 'Stop the built-in database container for the selected env.';
|
|
30
|
+
static examples = [
|
|
31
|
+
'<%= config.bin %> <%= command.id %>',
|
|
32
|
+
'<%= config.bin %> <%= command.id %> --env app1',
|
|
33
|
+
'<%= config.bin %> <%= command.id %> --env app1 --verbose',
|
|
34
|
+
];
|
|
35
|
+
static flags = {
|
|
36
|
+
env: Flags.string({
|
|
37
|
+
char: 'e',
|
|
38
|
+
description: 'CLI env name to stop the built-in database for. Defaults to the current env when omitted',
|
|
39
|
+
}),
|
|
40
|
+
verbose: Flags.boolean({
|
|
41
|
+
description: 'Show raw shutdown output from the underlying Docker command',
|
|
42
|
+
default: false,
|
|
43
|
+
}),
|
|
44
|
+
};
|
|
45
|
+
async run() {
|
|
46
|
+
const { flags } = await this.parse(DbStop);
|
|
47
|
+
const requestedEnv = flags.env?.trim() || undefined;
|
|
48
|
+
const runtime = await resolveDbRuntime(requestedEnv);
|
|
49
|
+
if (!runtime) {
|
|
50
|
+
this.error(formatMissingManagedAppEnvMessage(requestedEnv));
|
|
51
|
+
}
|
|
52
|
+
if (runtime.kind !== 'builtin') {
|
|
53
|
+
this.error(formatUnmanagedDbMessage('stop', runtime));
|
|
54
|
+
}
|
|
55
|
+
startTask(`Stopping the built-in database for "${runtime.envName}"...`);
|
|
56
|
+
try {
|
|
57
|
+
const state = await stopDockerContainer(runtime.containerName, {
|
|
58
|
+
stdio: flags.verbose ? 'inherit' : 'ignore',
|
|
59
|
+
});
|
|
60
|
+
succeedTask(state === 'already-stopped'
|
|
61
|
+
? `The built-in database is already stopped for "${runtime.envName}".`
|
|
62
|
+
: `The built-in database has stopped for "${runtime.envName}".`);
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
66
|
+
failTask(`Failed to stop the built-in database for "${runtime.envName}".`);
|
|
67
|
+
this.error(formatDbStopFailure(runtime.envName, message));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
package/dist/commands/dev.js
CHANGED
|
@@ -6,37 +6,121 @@
|
|
|
6
6
|
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
9
|
+
import { Command, Flags } from '@oclif/core';
|
|
10
|
+
import { formatMissingManagedAppEnvMessage, resolveManagedAppRuntime, runLocalNocoBaseCommand, } from '../lib/app-runtime.js';
|
|
11
|
+
import { printInfo } from '../lib/ui.js';
|
|
12
|
+
function formatUnsupportedRuntimeMessage(kind, envName) {
|
|
13
|
+
if (kind === 'docker') {
|
|
14
|
+
return [
|
|
15
|
+
`Can't run dev mode for "${envName}".`,
|
|
16
|
+
'This env is managed by Docker, but `nb dev` requires a local npm or Git source directory.',
|
|
17
|
+
`Use \`nb logs --env ${envName}\` to inspect the Docker app, or create a source-based env with \`nb init --env ${envName} --source git\`.`,
|
|
18
|
+
].join('\n');
|
|
19
|
+
}
|
|
20
|
+
return [
|
|
21
|
+
`Can't run dev mode for "${envName}".`,
|
|
22
|
+
'This env only has an API connection, but `nb dev` requires a local npm or Git source directory.',
|
|
23
|
+
`Create a source-based env with \`nb init --env ${envName} --source git\` if you want local development mode.`,
|
|
24
|
+
].join('\n');
|
|
25
|
+
}
|
|
26
|
+
function appUrlForPort(port) {
|
|
27
|
+
const value = String(port ?? '').trim();
|
|
28
|
+
if (!value) {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
return `http://127.0.0.1:${value}`;
|
|
32
|
+
}
|
|
33
|
+
async function isAppAlreadyRunning(appUrl) {
|
|
34
|
+
if (!appUrl) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
const controller = new AbortController();
|
|
38
|
+
const timeout = setTimeout(() => controller.abort(), 1500);
|
|
39
|
+
try {
|
|
40
|
+
const response = await fetch(`${appUrl}/api/__health_check`, {
|
|
41
|
+
signal: controller.signal,
|
|
42
|
+
});
|
|
43
|
+
const text = await response.text();
|
|
44
|
+
return response.ok && text.trim().toLowerCase() === 'ok';
|
|
45
|
+
}
|
|
46
|
+
catch (_error) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
clearTimeout(timeout);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
11
53
|
export default class Dev extends Command {
|
|
12
|
-
static
|
|
13
|
-
file: Args.string({ description: 'file to read' }),
|
|
14
|
-
};
|
|
15
|
-
static description = 'describe the command here';
|
|
54
|
+
static description = 'Run NocoBase in local development mode for an npm or Git source env.';
|
|
16
55
|
static examples = [
|
|
17
56
|
'<%= config.bin %> <%= command.id %>',
|
|
18
|
-
'<%= config.bin %> <%= command.id %> --
|
|
19
|
-
'<%= config.bin %> <%= command.id %> --
|
|
20
|
-
'<%= config.bin %> <%= command.id %> --
|
|
21
|
-
'<%= config.bin %> <%= command.id %> --
|
|
22
|
-
'<%= config.bin %> <%= command.id %> --
|
|
57
|
+
'<%= config.bin %> <%= command.id %> --env app1',
|
|
58
|
+
'<%= config.bin %> <%= command.id %> --env app1 --db-sync',
|
|
59
|
+
'<%= config.bin %> <%= command.id %> --env app1 --port 12000',
|
|
60
|
+
'<%= config.bin %> <%= command.id %> --env app1 --client',
|
|
61
|
+
'<%= config.bin %> <%= command.id %> --env app1 --server',
|
|
62
|
+
'<%= config.bin %> <%= command.id %> --env app1 --inspect 9229',
|
|
23
63
|
];
|
|
24
64
|
static flags = {
|
|
25
|
-
env: Flags.string({
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
65
|
+
env: Flags.string({
|
|
66
|
+
char: 'e',
|
|
67
|
+
description: 'CLI env name for dev mode. Defaults to the current env when omitted',
|
|
68
|
+
required: false,
|
|
69
|
+
}),
|
|
70
|
+
'db-sync': Flags.boolean({
|
|
71
|
+
description: 'Synchronize the database before starting dev mode',
|
|
72
|
+
required: false,
|
|
73
|
+
}),
|
|
74
|
+
port: Flags.string({
|
|
75
|
+
description: 'Development server port',
|
|
76
|
+
char: 'p',
|
|
77
|
+
required: false,
|
|
78
|
+
}),
|
|
79
|
+
client: Flags.boolean({
|
|
80
|
+
description: 'Run client dev mode only',
|
|
81
|
+
char: 'c',
|
|
82
|
+
required: false,
|
|
83
|
+
}),
|
|
84
|
+
server: Flags.boolean({
|
|
85
|
+
description: 'Run server dev mode only',
|
|
86
|
+
char: 's',
|
|
87
|
+
required: false,
|
|
88
|
+
}),
|
|
89
|
+
inspect: Flags.string({
|
|
90
|
+
description: 'Node.js inspect port for server debugging',
|
|
91
|
+
char: 'i',
|
|
92
|
+
required: false,
|
|
93
|
+
}),
|
|
31
94
|
};
|
|
32
95
|
async run() {
|
|
33
96
|
const { flags } = await this.parse(Dev);
|
|
97
|
+
const requestedEnv = flags.env?.trim() || undefined;
|
|
98
|
+
const runtime = await resolveManagedAppRuntime(requestedEnv);
|
|
99
|
+
if (!runtime) {
|
|
100
|
+
this.error(formatMissingManagedAppEnvMessage(requestedEnv));
|
|
101
|
+
}
|
|
102
|
+
if (runtime.kind === 'docker' || runtime.kind === 'remote') {
|
|
103
|
+
this.error(formatUnsupportedRuntimeMessage(runtime.kind, runtime.envName));
|
|
104
|
+
}
|
|
105
|
+
const devPort = flags.port
|
|
106
|
+
|| (runtime.env.appPort !== undefined && runtime.env.appPort !== null
|
|
107
|
+
? String(runtime.env.appPort).trim()
|
|
108
|
+
: undefined);
|
|
109
|
+
const appUrl = appUrlForPort(devPort);
|
|
110
|
+
if (await isAppAlreadyRunning(appUrl)) {
|
|
111
|
+
this.error([
|
|
112
|
+
`NocoBase is already running for "${runtime.envName}"${appUrl ? ` at ${appUrl}` : ''}.`,
|
|
113
|
+
flags.port
|
|
114
|
+
? `Choose another dev port with --port, or stop the running app with \`nb stop --env ${runtime.envName}\` first.`
|
|
115
|
+
: `Run \`nb stop --env ${runtime.envName}\` before starting dev mode, or choose another dev port with --port.`,
|
|
116
|
+
].join('\n'));
|
|
117
|
+
}
|
|
34
118
|
const npmArgs = ['dev', '--rsbuild'];
|
|
35
119
|
if (flags['db-sync']) {
|
|
36
120
|
npmArgs.push('--db-sync');
|
|
37
121
|
}
|
|
38
|
-
if (
|
|
39
|
-
npmArgs.push('--port',
|
|
122
|
+
if (devPort) {
|
|
123
|
+
npmArgs.push('--port', devPort);
|
|
40
124
|
}
|
|
41
125
|
if (flags.client) {
|
|
42
126
|
npmArgs.push('--client');
|
|
@@ -47,12 +131,19 @@ export default class Dev extends Command {
|
|
|
47
131
|
if (flags.inspect) {
|
|
48
132
|
npmArgs.push('--inspect', flags.inspect);
|
|
49
133
|
}
|
|
134
|
+
printInfo(`Starting NocoBase dev mode for "${runtime.envName}" from ${runtime.projectRoot}. Press Ctrl+C to stop.`);
|
|
50
135
|
try {
|
|
51
|
-
await
|
|
136
|
+
await runLocalNocoBaseCommand(runtime, npmArgs, {
|
|
137
|
+
stdio: 'inherit',
|
|
138
|
+
});
|
|
52
139
|
}
|
|
53
140
|
catch (error) {
|
|
54
141
|
const message = error instanceof Error ? error.message : String(error);
|
|
55
|
-
this.error(
|
|
142
|
+
this.error([
|
|
143
|
+
`Couldn't start dev mode for "${runtime.envName}".`,
|
|
144
|
+
'Check that dependencies are installed and the saved env settings are valid, then try again.',
|
|
145
|
+
`Details: ${message}`,
|
|
146
|
+
].join('\n'));
|
|
56
147
|
}
|
|
57
148
|
}
|
|
58
149
|
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
import { Command, Flags } from '@oclif/core';
|
|
10
|
+
import fsp from 'node:fs/promises';
|
|
11
|
+
import os from 'node:os';
|
|
12
|
+
import path from 'node:path';
|
|
13
|
+
import { buildDockerDbContainerName, formatMissingManagedAppEnvMessage, resolveManagedAppRuntime, runLocalNocoBaseCommand, } from '../lib/app-runtime.js';
|
|
14
|
+
import { removeEnv } from '../lib/auth-store.js';
|
|
15
|
+
import { commandSucceeds, run } from '../lib/run-npm.js';
|
|
16
|
+
import { confirmAction, failTask, isInteractiveTerminal, printInfo, startTask, succeedTask, } from '../lib/ui.js';
|
|
17
|
+
function resolveConfiguredPath(value) {
|
|
18
|
+
const text = String(value ?? '').trim();
|
|
19
|
+
if (!text) {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
return path.isAbsolute(text) ? text : path.resolve(process.cwd(), text);
|
|
23
|
+
}
|
|
24
|
+
function assertSafeRemovalPath(target, label) {
|
|
25
|
+
const resolved = path.resolve(target);
|
|
26
|
+
const cwd = path.resolve(process.cwd());
|
|
27
|
+
const home = path.resolve(os.homedir());
|
|
28
|
+
const root = path.parse(resolved).root;
|
|
29
|
+
if (resolved === root || resolved === cwd || resolved === home) {
|
|
30
|
+
throw new Error(`Refusing to remove ${label} at "${resolved}" because it is too broad.`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async function removePathIfExists(target, label) {
|
|
34
|
+
const resolved = path.resolve(target);
|
|
35
|
+
assertSafeRemovalPath(resolved, label);
|
|
36
|
+
await fsp.rm(resolved, { recursive: true, force: true });
|
|
37
|
+
}
|
|
38
|
+
async function dockerContainerExists(containerName) {
|
|
39
|
+
return await commandSucceeds('docker', ['container', 'inspect', containerName]);
|
|
40
|
+
}
|
|
41
|
+
async function removeDockerContainerIfExists(containerName) {
|
|
42
|
+
if (!(await dockerContainerExists(containerName))) {
|
|
43
|
+
return 'missing';
|
|
44
|
+
}
|
|
45
|
+
await run('docker', ['rm', '-f', containerName], {
|
|
46
|
+
errorName: 'docker rm',
|
|
47
|
+
stdio: 'ignore',
|
|
48
|
+
});
|
|
49
|
+
return 'removed';
|
|
50
|
+
}
|
|
51
|
+
function builtinDbContainerName(runtime) {
|
|
52
|
+
if (!runtime.env.config.builtinDb) {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
const dbDialect = String(runtime.env.config.dbDialect ?? 'postgres').trim() || 'postgres';
|
|
56
|
+
const workspaceName = runtime.workspaceName;
|
|
57
|
+
return buildDockerDbContainerName(runtime.envName, dbDialect, workspaceName);
|
|
58
|
+
}
|
|
59
|
+
async function confirmRemoveData(envName, yes) {
|
|
60
|
+
if (yes) {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
if (!isInteractiveTerminal()) {
|
|
64
|
+
throw new Error(`Refusing to remove user data for "${envName}" without confirmation. Re-run with --yes only if you are sure.`);
|
|
65
|
+
}
|
|
66
|
+
return await confirmAction(`Delete storage and managed database data for "${envName}"? This cannot be undone.`, { defaultValue: false });
|
|
67
|
+
}
|
|
68
|
+
function formatDownFailure(envName, message) {
|
|
69
|
+
return [
|
|
70
|
+
`Couldn't bring down NocoBase for "${envName}".`,
|
|
71
|
+
'Some local runtime resources may still exist. Check Docker or the local app process, then try again.',
|
|
72
|
+
`Details: ${message}`,
|
|
73
|
+
].join('\n');
|
|
74
|
+
}
|
|
75
|
+
export default class Down extends Command {
|
|
76
|
+
static description = 'Stop NocoBase and remove local runtime containers for the selected env. Data, source files, and env config are kept unless explicitly requested.';
|
|
77
|
+
static examples = [
|
|
78
|
+
'<%= config.bin %> <%= command.id %> --env app1',
|
|
79
|
+
'<%= config.bin %> <%= command.id %> --env app1 --remove-data',
|
|
80
|
+
'<%= config.bin %> <%= command.id %> --env app1 --remove-source --remove-env',
|
|
81
|
+
];
|
|
82
|
+
static flags = {
|
|
83
|
+
env: Flags.string({
|
|
84
|
+
char: 'e',
|
|
85
|
+
description: 'CLI env name to bring down. Defaults to the current env when omitted',
|
|
86
|
+
}),
|
|
87
|
+
'remove-data': Flags.boolean({
|
|
88
|
+
description: 'Delete storage and managed database data after confirmation',
|
|
89
|
+
default: false,
|
|
90
|
+
}),
|
|
91
|
+
'remove-source': Flags.boolean({
|
|
92
|
+
description: 'Delete the npm/git source directory for local installs',
|
|
93
|
+
default: false,
|
|
94
|
+
}),
|
|
95
|
+
'remove-env': Flags.boolean({
|
|
96
|
+
description: 'Remove the CLI env config after local resources are removed',
|
|
97
|
+
default: false,
|
|
98
|
+
}),
|
|
99
|
+
yes: Flags.boolean({
|
|
100
|
+
char: 'y',
|
|
101
|
+
description: 'Confirm destructive actions without prompting',
|
|
102
|
+
default: false,
|
|
103
|
+
}),
|
|
104
|
+
verbose: Flags.boolean({
|
|
105
|
+
description: 'Show raw output from shutdown commands',
|
|
106
|
+
default: false,
|
|
107
|
+
}),
|
|
108
|
+
};
|
|
109
|
+
async run() {
|
|
110
|
+
const { flags } = await this.parse(Down);
|
|
111
|
+
const requestedEnv = flags.env?.trim() || undefined;
|
|
112
|
+
const runtime = await resolveManagedAppRuntime(requestedEnv);
|
|
113
|
+
if (!runtime) {
|
|
114
|
+
this.error(formatMissingManagedAppEnvMessage(requestedEnv));
|
|
115
|
+
}
|
|
116
|
+
if (runtime.kind === 'remote') {
|
|
117
|
+
this.error([
|
|
118
|
+
`Can't bring down "${runtime.envName}" from this machine.`,
|
|
119
|
+
'This env only has an API connection, so there is no saved local app, Docker app, or managed database to remove here.',
|
|
120
|
+
'Use `nb env remove` if you only want to remove the CLI connection config.',
|
|
121
|
+
].join('\n'));
|
|
122
|
+
}
|
|
123
|
+
if (flags['remove-data']) {
|
|
124
|
+
let confirmed = false;
|
|
125
|
+
try {
|
|
126
|
+
confirmed = await confirmRemoveData(runtime.envName, flags.yes);
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
this.error(error instanceof Error ? error.message : String(error));
|
|
130
|
+
}
|
|
131
|
+
if (!confirmed) {
|
|
132
|
+
this.log('Canceled.');
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
try {
|
|
137
|
+
if (runtime.kind === 'docker') {
|
|
138
|
+
startTask(`Removing Docker app container for "${runtime.envName}"...`);
|
|
139
|
+
const state = await removeDockerContainerIfExists(runtime.containerName);
|
|
140
|
+
succeedTask(state === 'removed'
|
|
141
|
+
? `Docker app container removed for "${runtime.envName}".`
|
|
142
|
+
: `No Docker app container found for "${runtime.envName}".`);
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
startTask(`Stopping local NocoBase app for "${runtime.envName}"...`);
|
|
146
|
+
await runLocalNocoBaseCommand(runtime, ['pm2', 'kill'], {
|
|
147
|
+
stdio: flags.verbose ? 'inherit' : 'ignore',
|
|
148
|
+
});
|
|
149
|
+
succeedTask(`Local NocoBase app stopped for "${runtime.envName}".`);
|
|
150
|
+
}
|
|
151
|
+
const dbContainerName = builtinDbContainerName(runtime);
|
|
152
|
+
if (dbContainerName) {
|
|
153
|
+
startTask(`Removing built-in database container for "${runtime.envName}"...`);
|
|
154
|
+
const state = await removeDockerContainerIfExists(dbContainerName);
|
|
155
|
+
succeedTask(state === 'removed'
|
|
156
|
+
? `Built-in database container removed for "${runtime.envName}".`
|
|
157
|
+
: `No built-in database container found for "${runtime.envName}".`);
|
|
158
|
+
}
|
|
159
|
+
if (flags['remove-data']) {
|
|
160
|
+
const storagePath = resolveConfiguredPath(runtime.env.config.storagePath);
|
|
161
|
+
if (storagePath) {
|
|
162
|
+
startTask(`Deleting storage and managed database data for "${runtime.envName}"...`);
|
|
163
|
+
await removePathIfExists(storagePath, 'storage data');
|
|
164
|
+
succeedTask(`Storage and managed database data deleted for "${runtime.envName}".`);
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
printInfo(`No storage path is configured for "${runtime.envName}".`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (flags['remove-source']) {
|
|
171
|
+
if (runtime.kind === 'local') {
|
|
172
|
+
startTask(`Deleting source files for "${runtime.envName}"...`);
|
|
173
|
+
await removePathIfExists(runtime.projectRoot, 'source files');
|
|
174
|
+
succeedTask(`Source files deleted for "${runtime.envName}".`);
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
printInfo(`No npm/git source directory is configured for "${runtime.envName}".`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (flags['remove-env']) {
|
|
181
|
+
startTask(`Removing CLI env config for "${runtime.envName}"...`);
|
|
182
|
+
await removeEnv(runtime.envName);
|
|
183
|
+
succeedTask(`CLI env config removed for "${runtime.envName}".`);
|
|
184
|
+
}
|
|
185
|
+
printInfo(`NocoBase is down for "${runtime.envName}".`);
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
189
|
+
failTask(`Failed to bring down NocoBase for "${runtime.envName}".`);
|
|
190
|
+
this.error(formatDownFailure(runtime.envName, message));
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|