@nocobase/cli 2.1.0-beta.23 → 2.1.0-beta.25
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 +24 -0
- package/README.zh-CN.md +4 -0
- package/dist/commands/app/down.js +12 -6
- package/dist/commands/app/logs.js +2 -2
- package/dist/commands/app/start.js +2 -1
- package/dist/commands/app/stop.js +2 -1
- package/dist/commands/app/upgrade.js +116 -129
- 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 +238 -0
- package/dist/commands/db/logs.js +2 -2
- package/dist/commands/db/shared.js +6 -5
- package/dist/commands/db/start.js +2 -1
- package/dist/commands/db/stop.js +2 -1
- package/dist/commands/env/info.js +6 -2
- package/dist/commands/env/shared.js +1 -1
- package/dist/commands/init.js +0 -1
- package/dist/commands/install.js +87 -35
- 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 +56 -0
- package/dist/commands/license/index.js +20 -0
- package/dist/commands/license/plugins/clean.js +101 -0
- package/dist/commands/license/plugins/index.js +20 -0
- package/dist/commands/license/plugins/list.js +50 -0
- package/dist/commands/license/plugins/shared.js +325 -0
- package/dist/commands/license/plugins/sync.js +269 -0
- package/dist/commands/license/shared.js +414 -0
- package/dist/commands/license/status.js +50 -0
- package/dist/commands/plugin/disable.js +2 -0
- package/dist/commands/plugin/enable.js +2 -0
- package/dist/commands/source/dev.js +2 -1
- package/dist/lib/api-client.js +74 -3
- package/dist/lib/app-managed-resources.js +10 -6
- package/dist/lib/app-runtime.js +29 -11
- package/dist/lib/auth-store.js +36 -68
- package/dist/lib/bootstrap.js +0 -4
- package/dist/lib/build-config.js +8 -0
- package/dist/lib/builtin-db.js +86 -0
- package/dist/lib/cli-config.js +176 -0
- package/dist/lib/cli-home.js +6 -21
- package/dist/lib/db-connection-check.js +178 -0
- package/dist/lib/env-config.js +7 -0
- package/dist/lib/generated-command.js +24 -3
- package/dist/lib/plugin-storage.js +127 -0
- package/dist/lib/prompt-validators.js +4 -4
- package/dist/lib/run-npm.js +53 -0
- package/dist/lib/runtime-env-vars.js +32 -0
- package/dist/lib/runtime-generator.js +89 -10
- package/dist/lib/self-manager.js +57 -2
- package/dist/lib/skills-manager.js +2 -2
- package/dist/lib/startup-update.js +85 -7
- package/dist/lib/ui.js +3 -0
- package/dist/locale/en-US.json +16 -13
- package/dist/locale/zh-CN.json +16 -13
- package/nocobase-ctl.config.json +82 -0
- package/package.json +16 -4
package/dist/lib/app-runtime.js
CHANGED
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
import path from 'node:path';
|
|
10
10
|
import { resolveEnvKind } from './auth-store.js';
|
|
11
11
|
import { getEnv, loadAuthConfig } from './auth-store.js';
|
|
12
|
+
import { DEFAULT_DOCKER_CONTAINER_PREFIX, DEFAULT_DOCKER_NETWORK, getEffectiveCliConfigValue, } from './cli-config.js';
|
|
12
13
|
import { commandOutput, commandSucceeds, run, runNocoBaseCommand } from './run-npm.js';
|
|
14
|
+
import { buildRuntimeEnvVars } from './runtime-env-vars.js';
|
|
13
15
|
const DOCKER_APP_WORKDIR = '/app/nocobase';
|
|
14
16
|
function sanitizeDockerResourceName(value) {
|
|
15
17
|
const normalized = value
|
|
@@ -23,14 +25,24 @@ function sanitizeDockerResourceName(value) {
|
|
|
23
25
|
export function defaultWorkspaceName(cwd = process.cwd()) {
|
|
24
26
|
return sanitizeDockerResourceName(`nb-${path.basename(cwd)}`);
|
|
25
27
|
}
|
|
26
|
-
export function
|
|
27
|
-
const
|
|
28
|
-
|
|
28
|
+
export function defaultDockerContainerPrefix(cwd = process.cwd()) {
|
|
29
|
+
const configured = String(DEFAULT_DOCKER_CONTAINER_PREFIX ?? '').trim();
|
|
30
|
+
if (configured) {
|
|
31
|
+
return sanitizeDockerResourceName(configured);
|
|
32
|
+
}
|
|
33
|
+
return defaultWorkspaceName(cwd);
|
|
34
|
+
}
|
|
35
|
+
export function defaultDockerNetworkName() {
|
|
36
|
+
return sanitizeDockerResourceName(DEFAULT_DOCKER_NETWORK);
|
|
37
|
+
}
|
|
38
|
+
export function buildDockerAppContainerName(envName, containerPrefix) {
|
|
39
|
+
const prefix = containerPrefix?.trim() || defaultDockerContainerPrefix();
|
|
40
|
+
return sanitizeDockerResourceName(`${prefix}-${envName}-app`);
|
|
29
41
|
}
|
|
30
|
-
export function buildDockerDbContainerName(envName, dbDialect,
|
|
31
|
-
const
|
|
42
|
+
export function buildDockerDbContainerName(envName, dbDialect, containerPrefix) {
|
|
43
|
+
const prefix = containerPrefix?.trim() || defaultDockerContainerPrefix();
|
|
32
44
|
const dialect = dbDialect.trim() || 'postgres';
|
|
33
|
-
return sanitizeDockerResourceName(`${
|
|
45
|
+
return sanitizeDockerResourceName(`${prefix}-${envName}-${dialect}`);
|
|
34
46
|
}
|
|
35
47
|
function normalizeEnvSource(env) {
|
|
36
48
|
const source = String(env.config.source ?? '').trim();
|
|
@@ -51,7 +63,8 @@ export async function resolveManagedAppRuntime(envName) {
|
|
|
51
63
|
}
|
|
52
64
|
const resolvedName = env.name || envName?.trim() || config.currentEnv || 'default';
|
|
53
65
|
const source = normalizeEnvSource(env);
|
|
54
|
-
const
|
|
66
|
+
const dockerNetworkName = sanitizeDockerResourceName(getEffectiveCliConfigValue(config, 'docker.network') || defaultDockerNetworkName());
|
|
67
|
+
const dockerContainerPrefix = sanitizeDockerResourceName(getEffectiveCliConfigValue(config, 'docker.container-prefix') || defaultDockerContainerPrefix());
|
|
55
68
|
const kind = env.kind ?? resolveEnvKind(env.config);
|
|
56
69
|
if (kind === 'docker') {
|
|
57
70
|
return {
|
|
@@ -59,8 +72,10 @@ export async function resolveManagedAppRuntime(envName) {
|
|
|
59
72
|
env,
|
|
60
73
|
envName: resolvedName,
|
|
61
74
|
source: 'docker',
|
|
62
|
-
|
|
63
|
-
|
|
75
|
+
dockerNetworkName,
|
|
76
|
+
dockerContainerPrefix,
|
|
77
|
+
workspaceName: dockerNetworkName,
|
|
78
|
+
containerName: buildDockerAppContainerName(resolvedName, dockerContainerPrefix),
|
|
64
79
|
};
|
|
65
80
|
}
|
|
66
81
|
if (kind === 'local') {
|
|
@@ -70,7 +85,9 @@ export async function resolveManagedAppRuntime(envName) {
|
|
|
70
85
|
envName: resolvedName,
|
|
71
86
|
source: source === 'git' ? 'git' : source === 'npm' ? 'npm' : 'local',
|
|
72
87
|
projectRoot: env.appRootPath,
|
|
73
|
-
|
|
88
|
+
dockerNetworkName,
|
|
89
|
+
dockerContainerPrefix,
|
|
90
|
+
workspaceName: dockerNetworkName,
|
|
74
91
|
};
|
|
75
92
|
}
|
|
76
93
|
if (kind === 'ssh') {
|
|
@@ -99,9 +116,10 @@ export function formatMissingManagedAppEnvMessage(envName) {
|
|
|
99
116
|
return 'No NocoBase env is configured yet. Run `nb init` to create one first.';
|
|
100
117
|
}
|
|
101
118
|
export async function runLocalNocoBaseCommand(runtime, args, options) {
|
|
119
|
+
const envVars = await buildRuntimeEnvVars(runtime);
|
|
102
120
|
await runNocoBaseCommand(args, {
|
|
103
121
|
cwd: runtime.projectRoot,
|
|
104
|
-
env:
|
|
122
|
+
env: envVars,
|
|
105
123
|
stdio: options?.stdio,
|
|
106
124
|
});
|
|
107
125
|
}
|
package/dist/lib/auth-store.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { promises as fs } from 'node:fs';
|
|
10
10
|
import path from 'node:path';
|
|
11
|
-
import {
|
|
11
|
+
import { resolveCliHomeDir, resolveConfiguredEnvPath, resolveEnvRelativePath, } from './cli-home.js';
|
|
12
12
|
function normalizeStoredEnvKind(value) {
|
|
13
13
|
const kind = String(value ?? '').trim();
|
|
14
14
|
if (kind === 'remote') {
|
|
@@ -68,8 +68,22 @@ function normalizeEnvConfigEntry(entry) {
|
|
|
68
68
|
};
|
|
69
69
|
}
|
|
70
70
|
function normalizeAuthConfig(config) {
|
|
71
|
+
const settings = config.settings ?? {};
|
|
71
72
|
return {
|
|
72
73
|
name: config.name || config.dockerResourcePrefix,
|
|
74
|
+
settings: {
|
|
75
|
+
...(settings.license?.pkgUrl ? { license: { pkgUrl: normalizeOptionalString(settings.license.pkgUrl) } } : {}),
|
|
76
|
+
...(settings.docker?.network || settings.docker?.containerPrefix
|
|
77
|
+
? {
|
|
78
|
+
docker: {
|
|
79
|
+
...(settings.docker?.network ? { network: normalizeOptionalString(settings.docker.network) } : {}),
|
|
80
|
+
...(settings.docker?.containerPrefix
|
|
81
|
+
? { containerPrefix: normalizeOptionalString(settings.docker.containerPrefix) }
|
|
82
|
+
: {}),
|
|
83
|
+
},
|
|
84
|
+
}
|
|
85
|
+
: {}),
|
|
86
|
+
},
|
|
73
87
|
currentEnv: config.currentEnv || 'default',
|
|
74
88
|
envs: Object.fromEntries(Object.entries(config.envs || {}).map(([envName, entry]) => [envName, normalizeEnvConfigEntry(entry) ?? {}])),
|
|
75
89
|
};
|
|
@@ -83,48 +97,21 @@ function createDefaultConfig() {
|
|
|
83
97
|
envs: {},
|
|
84
98
|
};
|
|
85
99
|
}
|
|
86
|
-
function
|
|
87
|
-
return Object.keys(config.envs).length > 0;
|
|
88
|
-
}
|
|
89
|
-
function shouldFallbackToLegacyProjectScope(options = {}) {
|
|
90
|
-
const requestedScope = options.scope ?? resolveDefaultConfigScope();
|
|
91
|
-
if (requestedScope !== 'global') {
|
|
92
|
-
return false;
|
|
93
|
-
}
|
|
94
|
-
return !process.env[NB_CLI_ROOT_ENV];
|
|
95
|
-
}
|
|
96
|
-
async function loadExactAuthConfig(options = {}) {
|
|
100
|
+
async function readStoredAuthConfig(filePath) {
|
|
97
101
|
try {
|
|
98
|
-
const content = await fs.readFile(
|
|
102
|
+
const content = await fs.readFile(filePath, 'utf8');
|
|
99
103
|
const parsed = JSON.parse(content);
|
|
100
104
|
return normalizeAuthConfig(parsed);
|
|
101
105
|
}
|
|
102
106
|
catch (_error) {
|
|
103
|
-
return
|
|
107
|
+
return undefined;
|
|
104
108
|
}
|
|
105
109
|
}
|
|
106
|
-
async function
|
|
107
|
-
|
|
108
|
-
if (requestedScope !== 'global') {
|
|
109
|
-
return { ...options, scope: requestedScope };
|
|
110
|
-
}
|
|
111
|
-
const globalConfig = await loadExactAuthConfig({ scope: 'global' });
|
|
112
|
-
if (globalConfig.envs[envName]) {
|
|
113
|
-
return { ...options, scope: 'global' };
|
|
114
|
-
}
|
|
115
|
-
const projectConfig = await loadExactAuthConfig({ scope: 'project' });
|
|
116
|
-
if (projectConfig.envs[envName]) {
|
|
117
|
-
return { ...options, scope: 'project' };
|
|
118
|
-
}
|
|
119
|
-
return { ...options, scope: 'global' };
|
|
110
|
+
export async function loadExactAuthConfig(options = {}) {
|
|
111
|
+
return (await readStoredAuthConfig(getConfigFile(options))) ?? createDefaultConfig();
|
|
120
112
|
}
|
|
121
113
|
export async function loadAuthConfig(options = {}) {
|
|
122
|
-
|
|
123
|
-
if (!shouldFallbackToLegacyProjectScope(options) || hasConfiguredEnvs(config)) {
|
|
124
|
-
return config;
|
|
125
|
-
}
|
|
126
|
-
const legacyProjectConfig = await loadExactAuthConfig({ scope: 'project' });
|
|
127
|
-
return hasConfiguredEnvs(legacyProjectConfig) ? legacyProjectConfig : config;
|
|
114
|
+
return await loadExactAuthConfig(options);
|
|
128
115
|
}
|
|
129
116
|
export async function saveAuthConfig(config, options = {}) {
|
|
130
117
|
const filePath = getConfigFile(options);
|
|
@@ -143,24 +130,12 @@ export async function getCurrentEnvName(options = {}) {
|
|
|
143
130
|
return config.currentEnv || 'default';
|
|
144
131
|
}
|
|
145
132
|
export async function setCurrentEnv(envName, options = {}) {
|
|
146
|
-
const
|
|
147
|
-
const config = await loadExactAuthConfig(writeOptions);
|
|
133
|
+
const config = await loadExactAuthConfig(options);
|
|
148
134
|
if (!config.envs[envName]) {
|
|
149
135
|
throw new Error(`Env "${envName}" is not configured`);
|
|
150
136
|
}
|
|
151
137
|
config.currentEnv = envName;
|
|
152
|
-
await saveAuthConfig(config, writeOptions);
|
|
153
|
-
}
|
|
154
|
-
export async function ensureWorkspaceName(defaultName, options = {}) {
|
|
155
|
-
const config = await loadExactAuthConfig(options);
|
|
156
|
-
const existing = config.name?.trim();
|
|
157
|
-
if (existing) {
|
|
158
|
-
return existing;
|
|
159
|
-
}
|
|
160
|
-
const next = defaultName.trim();
|
|
161
|
-
config.name = next;
|
|
162
138
|
await saveAuthConfig(config, options);
|
|
163
|
-
return next;
|
|
164
139
|
}
|
|
165
140
|
export class Env {
|
|
166
141
|
config;
|
|
@@ -220,8 +195,13 @@ export class Env {
|
|
|
220
195
|
put('APP_KEY', this.config.appKey);
|
|
221
196
|
put('TZ', this.config.timezone);
|
|
222
197
|
put('DB_DIALECT', this.config.dbDialect);
|
|
223
|
-
|
|
224
|
-
|
|
198
|
+
if (!this.config.builtinDb) {
|
|
199
|
+
put('DB_HOST', this.config.dbHost);
|
|
200
|
+
put('DB_PORT', this.config.dbPort);
|
|
201
|
+
}
|
|
202
|
+
else if (String(this.config.source ?? '').trim() !== 'docker') {
|
|
203
|
+
put('DB_PORT', this.config.dbPort);
|
|
204
|
+
}
|
|
225
205
|
put('DB_DATABASE', this.config.dbDatabase);
|
|
226
206
|
put('DB_USER', this.config.dbUser);
|
|
227
207
|
put('DB_PASSWORD', this.config.dbPassword);
|
|
@@ -234,16 +214,7 @@ export async function getEnv(envName, options = {}) {
|
|
|
234
214
|
const resolved = envName?.trim() || config.currentEnv || 'default';
|
|
235
215
|
const envConfig = config.envs[resolved];
|
|
236
216
|
if (!envConfig) {
|
|
237
|
-
|
|
238
|
-
return undefined;
|
|
239
|
-
}
|
|
240
|
-
const legacyProjectConfig = await loadExactAuthConfig({ scope: 'project' });
|
|
241
|
-
const legacyResolved = envName?.trim() || legacyProjectConfig.currentEnv || 'default';
|
|
242
|
-
const legacyEnvConfig = legacyProjectConfig.envs[legacyResolved];
|
|
243
|
-
if (!legacyEnvConfig) {
|
|
244
|
-
return undefined;
|
|
245
|
-
}
|
|
246
|
-
return new Env({ ...(normalizeEnvConfigEntry(legacyEnvConfig) ?? {}), name: legacyResolved });
|
|
217
|
+
return undefined;
|
|
247
218
|
}
|
|
248
219
|
return new Env({ ...(normalizeEnvConfigEntry(envConfig) ?? {}), name: resolved });
|
|
249
220
|
}
|
|
@@ -269,12 +240,11 @@ function areAuthConfigsEquivalent(left, right) {
|
|
|
269
240
|
return false;
|
|
270
241
|
}
|
|
271
242
|
async function writeEnv(envName, updater, options = {}) {
|
|
272
|
-
const
|
|
273
|
-
const config = await loadExactAuthConfig(writeOptions);
|
|
243
|
+
const config = await loadExactAuthConfig(options);
|
|
274
244
|
const previous = config.envs[envName];
|
|
275
245
|
config.envs[envName] = updater(previous);
|
|
276
246
|
config.currentEnv = envName;
|
|
277
|
-
await saveAuthConfig(config,
|
|
247
|
+
await saveAuthConfig(config, options);
|
|
278
248
|
}
|
|
279
249
|
export async function upsertEnv(envName, config, options = {}) {
|
|
280
250
|
await writeEnv(envName, (previous) => {
|
|
@@ -330,19 +300,17 @@ export async function setEnvOauthSession(envName, auth, options = {}) {
|
|
|
330
300
|
}), options);
|
|
331
301
|
}
|
|
332
302
|
export async function setEnvRuntime(envName, runtime, options = {}) {
|
|
333
|
-
const
|
|
334
|
-
const config = await loadExactAuthConfig(writeOptions);
|
|
303
|
+
const config = await loadExactAuthConfig(options);
|
|
335
304
|
const current = config.envs[envName] ?? {};
|
|
336
305
|
config.envs[envName] = {
|
|
337
306
|
...current,
|
|
338
307
|
runtime,
|
|
339
308
|
};
|
|
340
309
|
config.currentEnv = envName;
|
|
341
|
-
await saveAuthConfig(config,
|
|
310
|
+
await saveAuthConfig(config, options);
|
|
342
311
|
}
|
|
343
312
|
export async function removeEnv(envName, options = {}) {
|
|
344
|
-
const
|
|
345
|
-
const config = await loadExactAuthConfig(writeOptions);
|
|
313
|
+
const config = await loadExactAuthConfig(options);
|
|
346
314
|
if (!config.envs[envName]) {
|
|
347
315
|
throw new Error(`Env "${envName}" is not configured`);
|
|
348
316
|
}
|
|
@@ -351,7 +319,7 @@ export async function removeEnv(envName, options = {}) {
|
|
|
351
319
|
const nextEnv = Object.keys(config.envs).sort()[0];
|
|
352
320
|
config.currentEnv = nextEnv ?? 'default';
|
|
353
321
|
}
|
|
354
|
-
await saveAuthConfig(config,
|
|
322
|
+
await saveAuthConfig(config, options);
|
|
355
323
|
return {
|
|
356
324
|
removed: envName,
|
|
357
325
|
currentEnv: config.currentEnv || 'default',
|
package/dist/lib/bootstrap.js
CHANGED
|
@@ -36,14 +36,10 @@ function hasBooleanFlag(argv, name) {
|
|
|
36
36
|
const exact = `--${name}`;
|
|
37
37
|
const negated = `--no-${name}`;
|
|
38
38
|
const prefix = `--${name}=`;
|
|
39
|
-
const alias = name === 'verbose' ? '-V' : undefined;
|
|
40
39
|
for (const value of argv) {
|
|
41
40
|
if (value === exact) {
|
|
42
41
|
return true;
|
|
43
42
|
}
|
|
44
|
-
if (alias && value === alias) {
|
|
45
|
-
return true;
|
|
46
|
-
}
|
|
47
43
|
if (value === negated) {
|
|
48
44
|
return false;
|
|
49
45
|
}
|
package/dist/lib/build-config.js
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
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
|
+
*/
|
|
1
9
|
import { promises as fs } from 'node:fs';
|
|
2
10
|
export async function loadBuildConfig(filePath) {
|
|
3
11
|
try {
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
import { buildDockerDbContainerName } from './app-runtime.js';
|
|
10
|
+
import { commandOutput } from './run-npm.js';
|
|
11
|
+
function trimValue(value) {
|
|
12
|
+
return String(value ?? '').trim();
|
|
13
|
+
}
|
|
14
|
+
export function defaultBuiltinDbPortForDialect(value) {
|
|
15
|
+
const dialect = trimValue(value) || 'postgres';
|
|
16
|
+
switch (dialect) {
|
|
17
|
+
case 'mysql':
|
|
18
|
+
case 'mariadb':
|
|
19
|
+
return '3306';
|
|
20
|
+
case 'kingbase':
|
|
21
|
+
return '54321';
|
|
22
|
+
case 'postgres':
|
|
23
|
+
default:
|
|
24
|
+
return '5432';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export function resolveBuiltinDbContainerName(runtime, dbDialect) {
|
|
28
|
+
const dialect = trimValue(dbDialect ?? runtime.env.config.dbDialect) || 'postgres';
|
|
29
|
+
return buildDockerDbContainerName(runtime.envName, dialect, runtime.dockerContainerPrefix || runtime.workspaceName);
|
|
30
|
+
}
|
|
31
|
+
export function deriveBuiltinDbConnection(runtime, overrides = {}) {
|
|
32
|
+
const dbDialect = trimValue(overrides.dbDialect ?? runtime.env.config.dbDialect) || 'postgres';
|
|
33
|
+
const containerName = resolveBuiltinDbContainerName(runtime, dbDialect);
|
|
34
|
+
const networkName = trimValue(runtime.dockerNetworkName || runtime.workspaceName) || undefined;
|
|
35
|
+
if (runtime.source === 'docker') {
|
|
36
|
+
return {
|
|
37
|
+
builtinDb: true,
|
|
38
|
+
dbDialect,
|
|
39
|
+
dbHost: containerName,
|
|
40
|
+
dbPort: defaultBuiltinDbPortForDialect(dbDialect),
|
|
41
|
+
containerName,
|
|
42
|
+
networkName,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
const dbPort = trimValue(overrides.dbPort ?? runtime.env.config.dbPort) || defaultBuiltinDbPortForDialect(dbDialect);
|
|
46
|
+
return {
|
|
47
|
+
builtinDb: true,
|
|
48
|
+
dbDialect,
|
|
49
|
+
dbHost: '127.0.0.1',
|
|
50
|
+
dbPort,
|
|
51
|
+
containerName,
|
|
52
|
+
networkName,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
export async function resolveBuiltinDbConnection(runtime) {
|
|
56
|
+
const derived = deriveBuiltinDbConnection(runtime);
|
|
57
|
+
if (runtime.source === 'docker') {
|
|
58
|
+
return derived;
|
|
59
|
+
}
|
|
60
|
+
const mappedPort = await inspectBuiltinDbPublishedPort(derived.containerName, derived.dbDialect);
|
|
61
|
+
if (mappedPort) {
|
|
62
|
+
return {
|
|
63
|
+
...derived,
|
|
64
|
+
dbPort: mappedPort,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
return derived;
|
|
68
|
+
}
|
|
69
|
+
async function inspectBuiltinDbPublishedPort(containerName, dbDialect) {
|
|
70
|
+
const containerPort = defaultBuiltinDbPortForDialect(dbDialect);
|
|
71
|
+
try {
|
|
72
|
+
const output = await commandOutput('docker', [
|
|
73
|
+
'inspect',
|
|
74
|
+
'--format',
|
|
75
|
+
`{{with index .NetworkSettings.Ports "${containerPort}/tcp"}}{{(index . 0).HostPort}}{{end}}`,
|
|
76
|
+
containerName,
|
|
77
|
+
], {
|
|
78
|
+
errorName: 'docker inspect',
|
|
79
|
+
});
|
|
80
|
+
const hostPort = trimValue(output);
|
|
81
|
+
return hostPort || undefined;
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
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 { loadExactAuthConfig, saveAuthConfig } from './auth-store.js';
|
|
10
|
+
import { resolveDefaultConfigScope } from './cli-home.js';
|
|
11
|
+
export const DEFAULT_LICENSE_PKG_URL = 'https://pkg.nocobase.com/';
|
|
12
|
+
export const DEFAULT_DOCKER_NETWORK = 'nocobase';
|
|
13
|
+
export const DEFAULT_DOCKER_CONTAINER_PREFIX = 'nb';
|
|
14
|
+
export const SUPPORTED_CLI_CONFIG_KEYS = [
|
|
15
|
+
'license.pkg-url',
|
|
16
|
+
'docker.network',
|
|
17
|
+
'docker.container-prefix',
|
|
18
|
+
];
|
|
19
|
+
function trimValue(value) {
|
|
20
|
+
const text = String(value ?? '').trim();
|
|
21
|
+
return text || undefined;
|
|
22
|
+
}
|
|
23
|
+
function resolveScope(options = {}) {
|
|
24
|
+
return {
|
|
25
|
+
scope: options.scope ?? resolveDefaultConfigScope(),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export function isSupportedCliConfigKey(value) {
|
|
29
|
+
return SUPPORTED_CLI_CONFIG_KEYS.includes(value);
|
|
30
|
+
}
|
|
31
|
+
export function assertSupportedCliConfigKey(value) {
|
|
32
|
+
if (!isSupportedCliConfigKey(value)) {
|
|
33
|
+
throw new Error(`Unsupported config key "${value}". Supported keys: ${SUPPORTED_CLI_CONFIG_KEYS.join(', ')}`);
|
|
34
|
+
}
|
|
35
|
+
return value;
|
|
36
|
+
}
|
|
37
|
+
function cloneSettings(config) {
|
|
38
|
+
return {
|
|
39
|
+
license: config.settings?.license ? { ...config.settings.license } : undefined,
|
|
40
|
+
docker: config.settings?.docker ? { ...config.settings.docker } : undefined,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function pruneSettings(config) {
|
|
44
|
+
const license = config.settings?.license;
|
|
45
|
+
if (license && !trimValue(license.pkgUrl)) {
|
|
46
|
+
delete config.settings?.license;
|
|
47
|
+
}
|
|
48
|
+
const docker = config.settings?.docker;
|
|
49
|
+
if (docker && !trimValue(docker.network) && !trimValue(docker.containerPrefix)) {
|
|
50
|
+
delete config.settings?.docker;
|
|
51
|
+
}
|
|
52
|
+
if (config.settings
|
|
53
|
+
&& !config.settings.license
|
|
54
|
+
&& !config.settings.docker) {
|
|
55
|
+
delete config.settings;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export function getExplicitCliConfigValue(config, key) {
|
|
59
|
+
switch (key) {
|
|
60
|
+
case 'license.pkg-url':
|
|
61
|
+
return trimValue(config.settings?.license?.pkgUrl);
|
|
62
|
+
case 'docker.network':
|
|
63
|
+
return trimValue(config.settings?.docker?.network);
|
|
64
|
+
case 'docker.container-prefix':
|
|
65
|
+
return trimValue(config.settings?.docker?.containerPrefix);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
export function getEffectiveCliConfigValue(config, key) {
|
|
69
|
+
const explicit = getExplicitCliConfigValue(config, key);
|
|
70
|
+
if (explicit) {
|
|
71
|
+
return explicit;
|
|
72
|
+
}
|
|
73
|
+
switch (key) {
|
|
74
|
+
case 'license.pkg-url':
|
|
75
|
+
return DEFAULT_LICENSE_PKG_URL;
|
|
76
|
+
case 'docker.network':
|
|
77
|
+
return trimValue(config.name) || DEFAULT_DOCKER_NETWORK;
|
|
78
|
+
case 'docker.container-prefix':
|
|
79
|
+
return trimValue(config.name) || DEFAULT_DOCKER_CONTAINER_PREFIX;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
export function normalizeCliConfigValue(key, value) {
|
|
83
|
+
const normalized = value.trim();
|
|
84
|
+
if (!normalized) {
|
|
85
|
+
throw new Error(`Config key "${key}" requires a non-empty value.`);
|
|
86
|
+
}
|
|
87
|
+
if (key === 'license.pkg-url') {
|
|
88
|
+
return normalized.replace(/\/+$/, '') + '/';
|
|
89
|
+
}
|
|
90
|
+
return normalized;
|
|
91
|
+
}
|
|
92
|
+
export async function loadCliConfig(options = {}) {
|
|
93
|
+
return await loadExactAuthConfig(resolveScope(options));
|
|
94
|
+
}
|
|
95
|
+
export async function getCliConfigValue(key, options = {}) {
|
|
96
|
+
const config = await loadCliConfig(options);
|
|
97
|
+
return getEffectiveCliConfigValue(config, key);
|
|
98
|
+
}
|
|
99
|
+
export async function listExplicitCliConfigValues(options = {}) {
|
|
100
|
+
const config = await loadCliConfig(options);
|
|
101
|
+
const out = {};
|
|
102
|
+
for (const key of SUPPORTED_CLI_CONFIG_KEYS) {
|
|
103
|
+
const value = getExplicitCliConfigValue(config, key);
|
|
104
|
+
if (value) {
|
|
105
|
+
out[key] = value;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return out;
|
|
109
|
+
}
|
|
110
|
+
export async function setCliConfigValue(key, value, options = {}) {
|
|
111
|
+
const scope = resolveScope(options);
|
|
112
|
+
const config = await loadExactAuthConfig(scope);
|
|
113
|
+
const normalized = normalizeCliConfigValue(key, value);
|
|
114
|
+
config.settings = cloneSettings(config);
|
|
115
|
+
switch (key) {
|
|
116
|
+
case 'license.pkg-url':
|
|
117
|
+
config.settings.license = {
|
|
118
|
+
...(config.settings.license ?? {}),
|
|
119
|
+
pkgUrl: normalized,
|
|
120
|
+
};
|
|
121
|
+
break;
|
|
122
|
+
case 'docker.network':
|
|
123
|
+
config.settings.docker = {
|
|
124
|
+
...(config.settings.docker ?? {}),
|
|
125
|
+
network: normalized,
|
|
126
|
+
};
|
|
127
|
+
break;
|
|
128
|
+
case 'docker.container-prefix':
|
|
129
|
+
config.settings.docker = {
|
|
130
|
+
...(config.settings.docker ?? {}),
|
|
131
|
+
containerPrefix: normalized,
|
|
132
|
+
};
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
pruneSettings(config);
|
|
136
|
+
await saveAuthConfig(config, scope);
|
|
137
|
+
return normalized;
|
|
138
|
+
}
|
|
139
|
+
export async function deleteCliConfigValue(key, options = {}) {
|
|
140
|
+
const scope = resolveScope(options);
|
|
141
|
+
const config = await loadExactAuthConfig(scope);
|
|
142
|
+
const hadValue = Boolean(getExplicitCliConfigValue(config, key));
|
|
143
|
+
if (!hadValue) {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
config.settings = cloneSettings(config);
|
|
147
|
+
switch (key) {
|
|
148
|
+
case 'license.pkg-url':
|
|
149
|
+
if (config.settings.license) {
|
|
150
|
+
delete config.settings.license.pkgUrl;
|
|
151
|
+
}
|
|
152
|
+
break;
|
|
153
|
+
case 'docker.network':
|
|
154
|
+
if (config.settings.docker) {
|
|
155
|
+
delete config.settings.docker.network;
|
|
156
|
+
}
|
|
157
|
+
break;
|
|
158
|
+
case 'docker.container-prefix':
|
|
159
|
+
if (config.settings.docker) {
|
|
160
|
+
delete config.settings.docker.containerPrefix;
|
|
161
|
+
}
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
pruneSettings(config);
|
|
165
|
+
await saveAuthConfig(config, scope);
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
export async function resolveDockerNetworkName(options = {}) {
|
|
169
|
+
return await getCliConfigValue('docker.network', options);
|
|
170
|
+
}
|
|
171
|
+
export async function resolveDockerContainerPrefix(options = {}) {
|
|
172
|
+
return await getCliConfigValue('docker.container-prefix', options);
|
|
173
|
+
}
|
|
174
|
+
export async function resolveLicensePkgUrlFromConfig(options = {}) {
|
|
175
|
+
return await getCliConfigValue('license.pkg-url', options);
|
|
176
|
+
}
|
package/dist/lib/cli-home.js
CHANGED
|
@@ -6,15 +6,12 @@
|
|
|
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 fs from 'node:fs';
|
|
10
9
|
import os from 'node:os';
|
|
11
10
|
import path from 'node:path';
|
|
12
11
|
export const CLI_HOME_DIRNAME = '.nocobase';
|
|
13
|
-
export const NB_CONFIG_SCOPE_ENV = 'NB_CONFIG_SCOPE';
|
|
14
12
|
export const NB_CLI_ROOT_ENV = 'NB_CLI_ROOT';
|
|
15
13
|
export function resolveDefaultConfigScope() {
|
|
16
|
-
|
|
17
|
-
return raw === 'project' ? 'project' : 'global';
|
|
14
|
+
return 'global';
|
|
18
15
|
}
|
|
19
16
|
function readConfiguredPath(name) {
|
|
20
17
|
const value = String(process.env[name] ?? '').trim();
|
|
@@ -24,28 +21,15 @@ function resolveGlobalCliHomeRoot() {
|
|
|
24
21
|
return readConfiguredPath(NB_CLI_ROOT_ENV) ?? os.homedir();
|
|
25
22
|
}
|
|
26
23
|
export function resolveCliHomeRoot(scope = resolveDefaultConfigScope()) {
|
|
27
|
-
|
|
28
|
-
if (scope === 'project') {
|
|
29
|
-
return cwdRoot;
|
|
30
|
-
}
|
|
31
|
-
if (scope === 'global') {
|
|
32
|
-
return resolveGlobalCliHomeRoot();
|
|
33
|
-
}
|
|
34
|
-
const cwdCliHome = path.join(cwdRoot, CLI_HOME_DIRNAME);
|
|
35
|
-
if (fs.existsSync(cwdCliHome)) {
|
|
36
|
-
return cwdRoot;
|
|
37
|
-
}
|
|
24
|
+
void scope;
|
|
38
25
|
return resolveGlobalCliHomeRoot();
|
|
39
26
|
}
|
|
40
27
|
export function resolveCliHomeDir(scope = resolveDefaultConfigScope()) {
|
|
41
28
|
return path.join(resolveCliHomeRoot(scope), CLI_HOME_DIRNAME);
|
|
42
29
|
}
|
|
43
30
|
export function resolveEnvRoot(scope = resolveDefaultConfigScope()) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
return path.resolve(envRoot);
|
|
47
|
-
}
|
|
48
|
-
return resolveCliHomeRoot(scope);
|
|
31
|
+
void scope;
|
|
32
|
+
return resolveCliHomeRoot();
|
|
49
33
|
}
|
|
50
34
|
export function resolveEnvRelativePath(relativePath, scope = resolveDefaultConfigScope()) {
|
|
51
35
|
return path.resolve(resolveEnvRoot(scope), relativePath);
|
|
@@ -58,5 +42,6 @@ export function resolveConfiguredEnvPath(value, scope = resolveDefaultConfigScop
|
|
|
58
42
|
return path.isAbsolute(text) ? text : resolveEnvRelativePath(text, scope);
|
|
59
43
|
}
|
|
60
44
|
export function formatCliHomeScope(scope) {
|
|
61
|
-
|
|
45
|
+
void scope;
|
|
46
|
+
return 'global';
|
|
62
47
|
}
|