@nocobase/cli 2.1.19 → 2.1.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/env-proxy/nginx/snippets/proxy-location.conf +1 -0
- package/dist/commands/app/start.js +4 -1
- package/dist/commands/install.js +58 -129
- package/dist/commands/proxy/caddy/generate.js +23 -15
- package/dist/commands/proxy/nginx/generate.js +23 -15
- package/dist/commands/source/download.js +18 -14
- package/dist/lib/app-managed-resources.js +3 -2
- package/dist/lib/auth-store.js +68 -0
- package/dist/lib/cli-config.js +52 -1
- package/dist/lib/docker-image.js +94 -6
- package/dist/lib/env-config.js +5 -0
- package/dist/lib/env-proxy-config.js +48 -0
- package/dist/lib/env-proxy.js +29 -3
- package/dist/lib/proxy-caddy.js +53 -7
- package/dist/lib/proxy-nginx.js +53 -7
- package/dist/locale/en-US.json +37 -37
- package/dist/locale/zh-CN.json +37 -37
- package/package.json +2 -2
|
@@ -37,6 +37,9 @@ function buildLicenseSyncArgv(envName, options) {
|
|
|
37
37
|
}
|
|
38
38
|
function resolveHookCommand(value) {
|
|
39
39
|
const text = String(value ?? '').trim();
|
|
40
|
+
if (text === 'init') {
|
|
41
|
+
return text;
|
|
42
|
+
}
|
|
40
43
|
if (text === 'app:restart' || text === 'app:upgrade') {
|
|
41
44
|
return text;
|
|
42
45
|
}
|
|
@@ -225,7 +228,7 @@ export default class AppStart extends Command {
|
|
|
225
228
|
}),
|
|
226
229
|
'hook-command': Flags.string({
|
|
227
230
|
hidden: true,
|
|
228
|
-
options: ['app:start', 'app:restart', 'app:upgrade'],
|
|
231
|
+
options: ['init', 'app:start', 'app:restart', 'app:upgrade'],
|
|
229
232
|
}),
|
|
230
233
|
};
|
|
231
234
|
async run() {
|
package/dist/commands/install.js
CHANGED
|
@@ -13,11 +13,11 @@ import path from 'node:path';
|
|
|
13
13
|
import { exit } from 'node:process';
|
|
14
14
|
import { appendAppPublicPath } from '../lib/app-public-path.js';
|
|
15
15
|
import { runPromptCatalog, } from "../lib/prompt-catalog.js";
|
|
16
|
-
import { applyCliLocale, localeText,
|
|
16
|
+
import { applyCliLocale, localeText, translateCli } from "../lib/cli-locale.js";
|
|
17
17
|
import { resolveConfiguredEnvPath, resolveDefaultConfigScope, resolveEnvRoot, resolveEnvRelativePath, } from '../lib/cli-home.js';
|
|
18
18
|
import { defaultDockerContainerPrefix, defaultDockerNetworkName, managedAppLifecycleEnvVars, } from '../lib/app-runtime.js';
|
|
19
|
-
import { resolveDefaultApiHost, resolveDockerContainerPrefix, resolveDockerNetworkName } from '../lib/cli-config.js';
|
|
20
|
-
import { DEFAULT_DOCKER_VERSION, resolveDockerImageRef } from "../lib/docker-image.js";
|
|
19
|
+
import { getCliConfigValue, resolveDefaultApiHost, resolveDockerContainerPrefix, resolveDockerNetworkName, } from '../lib/cli-config.js';
|
|
20
|
+
import { DEFAULT_DOCKER_VERSION, DEFAULT_NB_IMAGE_VARIANT, inferNbImageRegistryFromRepository, normalizeNbImageVariant, resolveBuiltinDbImage, resolveDockerImageContainerPort, resolveDockerImageRef, resolveOfficialDockerRegistry, } from "../lib/docker-image.js";
|
|
21
21
|
import { findAvailableTcpPort, validateAppPublicPath, validateAvailableTcpPort, validateTcpPort, validateEnvKey, } from "../lib/prompt-validators.js";
|
|
22
22
|
import { validateExternalDbConfig, validateMysqlLowerCaseTableNamesCompatibility } from "../lib/db-connection-check.js";
|
|
23
23
|
import { formatMissingManagedAppEnvMessage } from '../lib/app-runtime.js';
|
|
@@ -31,7 +31,7 @@ import { startDockerLogFollower } from '../lib/docker-log-stream.js';
|
|
|
31
31
|
import { buildInitAppEnvVarsFromConfig } from '../lib/managed-init-env.js';
|
|
32
32
|
import { buildHookContext, persistHookScript, resolveHookScriptPath, runHookScriptHook, } from '../lib/hook-script.js';
|
|
33
33
|
import { areConfiguredPathsEquivalent, deriveConfiguredSourcePath, deriveConfiguredStoragePath, inferConfiguredAppPathFromLegacyConfig, } from '../lib/env-paths.js';
|
|
34
|
-
import Download
|
|
34
|
+
import Download from './download.js';
|
|
35
35
|
import EnvAdd from "./env/add.js";
|
|
36
36
|
import { resolveAppUrlFromApiBaseUrl } from "./env/shared.js";
|
|
37
37
|
const DEFAULT_INSTALL_ENV_NAME = 'local';
|
|
@@ -45,18 +45,6 @@ const DEFAULT_INSTALL_DB_PORTS = {
|
|
|
45
45
|
mariadb: '3306',
|
|
46
46
|
kingbase: '54321',
|
|
47
47
|
};
|
|
48
|
-
const DEFAULT_INSTALL_BUILTIN_DB_IMAGES = {
|
|
49
|
-
postgres: 'postgres:16',
|
|
50
|
-
mysql: 'mysql:8',
|
|
51
|
-
mariadb: 'mariadb:11',
|
|
52
|
-
kingbase: 'registry.cn-shanghai.aliyuncs.com/nocobase/kingbase:v009r001c001b0030_single_x86',
|
|
53
|
-
};
|
|
54
|
-
const DEFAULT_INSTALL_BUILTIN_DB_IMAGES_ZH_CN = {
|
|
55
|
-
postgres: 'registry.cn-shanghai.aliyuncs.com/nocobase/postgres:16',
|
|
56
|
-
mysql: 'registry.cn-shanghai.aliyuncs.com/nocobase/mysql:8',
|
|
57
|
-
mariadb: 'registry.cn-shanghai.aliyuncs.com/nocobase/mariadb:11',
|
|
58
|
-
kingbase: 'registry.cn-shanghai.aliyuncs.com/nocobase/kingbase:v009r001c001b0030_single_x86',
|
|
59
|
-
};
|
|
60
48
|
const DEFAULT_INSTALL_DB_DATABASE = 'nocobase';
|
|
61
49
|
const DEFAULT_INSTALL_DB_USER = 'nocobase';
|
|
62
50
|
const DEFAULT_INSTALL_DB_PASSWORD = 'nocobase';
|
|
@@ -179,22 +167,23 @@ function downloadVersionPromptValue(version) {
|
|
|
179
167
|
}
|
|
180
168
|
function supportsBuiltinDbDialect(value) {
|
|
181
169
|
const dialect = String(value ?? '').trim();
|
|
182
|
-
return
|
|
170
|
+
return INSTALL_DB_DIALECTS.includes(dialect);
|
|
183
171
|
}
|
|
184
172
|
export function defaultDbPortForDialect(value) {
|
|
185
173
|
const dialect = String(value ?? 'postgres').trim();
|
|
186
174
|
return DEFAULT_INSTALL_DB_PORTS[isInstallDbDialect(dialect) ? dialect : 'postgres'];
|
|
187
175
|
}
|
|
188
|
-
function defaultBuiltinDbImageForDialect(value) {
|
|
176
|
+
function defaultBuiltinDbImageForDialect(value, options) {
|
|
189
177
|
const dialect = String(value ?? 'postgres').trim();
|
|
190
|
-
|
|
191
|
-
? DEFAULT_INSTALL_BUILTIN_DB_IMAGES_ZH_CN
|
|
192
|
-
: DEFAULT_INSTALL_BUILTIN_DB_IMAGES;
|
|
193
|
-
return supportsBuiltinDbDialect(dialect) ? defaults[dialect] : defaults.postgres;
|
|
178
|
+
return resolveBuiltinDbImage(dialect, { registry: options?.registry });
|
|
194
179
|
}
|
|
195
180
|
function defaultDbDatabaseForDialect(value) {
|
|
196
181
|
return String(value ?? '').trim() === 'kingbase' ? 'kingbase' : DEFAULT_INSTALL_DB_DATABASE;
|
|
197
182
|
}
|
|
183
|
+
function supportsDbSchemaPrompt(value) {
|
|
184
|
+
const dialect = String(value ?? '').trim();
|
|
185
|
+
return dialect === 'postgres' || dialect === 'kingbase';
|
|
186
|
+
}
|
|
198
187
|
function defaultDbHostForBuiltinDb(values) {
|
|
199
188
|
return values.builtinDb ? DEFAULT_INSTALL_BUILTIN_DB_HOST : DEFAULT_INSTALL_DB_HOST;
|
|
200
189
|
}
|
|
@@ -601,7 +590,7 @@ export default class Install extends Command {
|
|
|
601
590
|
type: 'text',
|
|
602
591
|
message: installText('prompts.dbSchema.message'),
|
|
603
592
|
placeholder: installText('prompts.dbSchema.placeholder'),
|
|
604
|
-
hidden: (values) =>
|
|
593
|
+
hidden: (values) => !supportsDbSchemaPrompt(values.dbDialect),
|
|
605
594
|
},
|
|
606
595
|
dbTablePrefix: {
|
|
607
596
|
type: 'text',
|
|
@@ -1279,19 +1268,25 @@ export default class Install extends Command {
|
|
|
1279
1268
|
return builtinDb && Install.shouldPublishBuiltinDbPort(values.source);
|
|
1280
1269
|
}
|
|
1281
1270
|
static async buildDbPromptInitialValues(params) {
|
|
1282
|
-
|
|
1283
|
-
return {};
|
|
1284
|
-
}
|
|
1271
|
+
const configuredRegistry = await getCliConfigValue('nb-image-registry');
|
|
1285
1272
|
const values = {
|
|
1286
1273
|
...params.downloadResults,
|
|
1287
1274
|
...params.dbPreset,
|
|
1288
1275
|
};
|
|
1276
|
+
const dockerRegistry = String(values.dockerRegistry ?? '').trim() || resolveOfficialDockerRegistry(configuredRegistry);
|
|
1277
|
+
const dialect = String(values.dbDialect ?? 'postgres').trim() || 'postgres';
|
|
1278
|
+
const initialValues = values.builtinDb !== false && params.dbPreset.builtinDbImage === undefined
|
|
1279
|
+
? { builtinDbImage: defaultBuiltinDbImageForDialect(dialect, { registry: dockerRegistry }) }
|
|
1280
|
+
: {};
|
|
1281
|
+
if (params.flags['db-port'] !== undefined) {
|
|
1282
|
+
return initialValues;
|
|
1283
|
+
}
|
|
1289
1284
|
if (!Install.shouldPublishBuiltinDbPortForValues(values)) {
|
|
1290
|
-
return
|
|
1285
|
+
return initialValues;
|
|
1291
1286
|
}
|
|
1292
|
-
const dialect = String(values.dbDialect ?? 'postgres').trim() || 'postgres';
|
|
1293
1287
|
const defaultPort = defaultDbPortForDialect(dialect);
|
|
1294
1288
|
return {
|
|
1289
|
+
...initialValues,
|
|
1295
1290
|
dbPort: await Install.resolveAvailableDefaultPort(defaultPort, {
|
|
1296
1291
|
label: `Default ${dialect} port`,
|
|
1297
1292
|
warn: params.warnOnPortFallback ?? true,
|
|
@@ -1302,12 +1297,13 @@ export default class Install extends Command {
|
|
|
1302
1297
|
* When install runs {@link Download.prompts} after app prompts, align the download
|
|
1303
1298
|
* output directory with app settings, while Docker registry defaults follow the CLI locale.
|
|
1304
1299
|
*/
|
|
1305
|
-
static buildDownloadPromptOptionsForInstall(appResults, envName) {
|
|
1300
|
+
static async buildDownloadPromptOptionsForInstall(appResults, envName) {
|
|
1306
1301
|
const appRoot = resolveConfiguredSourcePathValue(appResults, envName);
|
|
1307
1302
|
const lang = String(appResults.lang ?? DEFAULT_INSTALL_LANG).trim() || DEFAULT_INSTALL_LANG;
|
|
1303
|
+
const dockerRegistry = resolveOfficialDockerRegistry(await getCliConfigValue('nb-image-registry'));
|
|
1308
1304
|
const initialValues = {
|
|
1309
1305
|
lang,
|
|
1310
|
-
dockerRegistry
|
|
1306
|
+
dockerRegistry,
|
|
1311
1307
|
outputDir: appRoot,
|
|
1312
1308
|
};
|
|
1313
1309
|
const values = {
|
|
@@ -1760,12 +1756,15 @@ export default class Install extends Command {
|
|
|
1760
1756
|
return plan;
|
|
1761
1757
|
}
|
|
1762
1758
|
static async buildDockerAppPlan(params) {
|
|
1759
|
+
const configuredRegistry = await getCliConfigValue('nb-image-registry');
|
|
1760
|
+
const configuredVariant = normalizeNbImageVariant(await getCliConfigValue('nb-image-variant')) ?? DEFAULT_NB_IMAGE_VARIANT;
|
|
1763
1761
|
const dockerRegistry = String(downloadResultsValue(params.downloadResults, 'dockerRegistry') ?? '').trim() ||
|
|
1764
|
-
|
|
1762
|
+
resolveOfficialDockerRegistry(configuredRegistry);
|
|
1765
1763
|
const version = String(downloadResultsValue(params.downloadResults, 'version') ?? '').trim() || DEFAULT_DOCKER_VERSION;
|
|
1766
1764
|
const imageRef = resolveDockerImageRef(dockerRegistry, version, {
|
|
1767
|
-
defaultRegistry:
|
|
1765
|
+
defaultRegistry: resolveOfficialDockerRegistry(configuredRegistry),
|
|
1768
1766
|
defaultVersion: DEFAULT_DOCKER_VERSION,
|
|
1767
|
+
variant: inferNbImageRegistryFromRepository(dockerRegistry) ? configuredVariant : undefined,
|
|
1769
1768
|
});
|
|
1770
1769
|
const appPort = String(params.appResults.appPort ?? DEFAULT_INSTALL_APP_PORT).trim() || DEFAULT_INSTALL_APP_PORT;
|
|
1771
1770
|
const configuredStoragePath = resolveConfiguredStoragePathValue(params.appResults, params.envName);
|
|
@@ -1792,6 +1791,7 @@ export default class Install extends Command {
|
|
|
1792
1791
|
appResults: params.appResults,
|
|
1793
1792
|
rootResults: params.rootResults,
|
|
1794
1793
|
});
|
|
1794
|
+
const containerPort = resolveDockerImageContainerPort(imageRef);
|
|
1795
1795
|
const args = [
|
|
1796
1796
|
'run',
|
|
1797
1797
|
'-d',
|
|
@@ -1800,7 +1800,7 @@ export default class Install extends Command {
|
|
|
1800
1800
|
'--network',
|
|
1801
1801
|
params.networkName,
|
|
1802
1802
|
'-p',
|
|
1803
|
-
`${appPort}
|
|
1803
|
+
`${appPort}:${containerPort}`,
|
|
1804
1804
|
];
|
|
1805
1805
|
if (envFile) {
|
|
1806
1806
|
args.push('--env-file', envFile);
|
|
@@ -1990,12 +1990,15 @@ export default class Install extends Command {
|
|
|
1990
1990
|
}
|
|
1991
1991
|
async ensureSkippedDownloadInputsReady(params) {
|
|
1992
1992
|
if (params.source === 'docker') {
|
|
1993
|
+
const configuredRegistry = await getCliConfigValue('nb-image-registry');
|
|
1994
|
+
const configuredVariant = normalizeNbImageVariant(await getCliConfigValue('nb-image-variant')) ?? DEFAULT_NB_IMAGE_VARIANT;
|
|
1993
1995
|
const dockerRegistry = String(downloadResultsValue(params.downloadResults, 'dockerRegistry') ?? '').trim() ||
|
|
1994
|
-
|
|
1996
|
+
resolveOfficialDockerRegistry(configuredRegistry);
|
|
1995
1997
|
const version = String(downloadResultsValue(params.downloadResults, 'version') ?? '').trim() || DEFAULT_DOCKER_VERSION;
|
|
1996
1998
|
const imageRef = resolveDockerImageRef(dockerRegistry, version, {
|
|
1997
|
-
defaultRegistry:
|
|
1999
|
+
defaultRegistry: resolveOfficialDockerRegistry(configuredRegistry),
|
|
1998
2000
|
defaultVersion: DEFAULT_DOCKER_VERSION,
|
|
2001
|
+
variant: inferNbImageRegistryFromRepository(dockerRegistry) ? configuredVariant : undefined,
|
|
1999
2002
|
});
|
|
2000
2003
|
const imageExists = await commandSucceeds('docker', ['image', 'inspect', imageRef]);
|
|
2001
2004
|
if (!imageExists) {
|
|
@@ -2254,6 +2257,13 @@ export default class Install extends Command {
|
|
|
2254
2257
|
}
|
|
2255
2258
|
await this.config.runCommand('env:update', [params.envName]);
|
|
2256
2259
|
}
|
|
2260
|
+
buildAppStartArgv(params) {
|
|
2261
|
+
const argv = ['--env', params.envName, '--yes', '--no-sync-licensed-plugins', '--hook-command', 'init'];
|
|
2262
|
+
if (params.verbose) {
|
|
2263
|
+
argv.push('--verbose');
|
|
2264
|
+
}
|
|
2265
|
+
return argv;
|
|
2266
|
+
}
|
|
2257
2267
|
async runInstallHookIfNeeded(params) {
|
|
2258
2268
|
const appPath = Install.resolveAbsoluteAppPath(params.envName, params.appResults);
|
|
2259
2269
|
const savedHookScript = Install.toOptionalPromptString(params.appResults.hookScript);
|
|
@@ -2391,7 +2401,7 @@ export default class Install extends Command {
|
|
|
2391
2401
|
if (resumePreset?.appPreset?.hookScript !== undefined && appResults.hookScript === undefined) {
|
|
2392
2402
|
appResults.hookScript = resumePreset.appPreset.hookScript;
|
|
2393
2403
|
}
|
|
2394
|
-
const downloadOpts = Install.buildDownloadPromptOptionsForInstall(appResults, envName);
|
|
2404
|
+
const downloadOpts = await Install.buildDownloadPromptOptionsForInstall(appResults, envName);
|
|
2395
2405
|
downloadOpts.values = {
|
|
2396
2406
|
...(resumePreset?.downloadPreset ?? {}),
|
|
2397
2407
|
...downloadOpts.values,
|
|
@@ -2498,7 +2508,6 @@ export default class Install extends Command {
|
|
|
2498
2508
|
const parsed = {
|
|
2499
2509
|
...flags,
|
|
2500
2510
|
};
|
|
2501
|
-
const defaultApiHost = await resolveDefaultApiHost();
|
|
2502
2511
|
if (parsed['skip-auth'] && (parsed['access-token'] !== undefined || parsed.token !== undefined)) {
|
|
2503
2512
|
this.error('--skip-auth cannot be used with --access-token or --token.');
|
|
2504
2513
|
}
|
|
@@ -2578,8 +2587,7 @@ export default class Install extends Command {
|
|
|
2578
2587
|
dbResults.dbUser = builtinDbPlan.dbUser;
|
|
2579
2588
|
dbResults.dbPassword = builtinDbPlan.dbPassword;
|
|
2580
2589
|
}
|
|
2581
|
-
let
|
|
2582
|
-
let localAppPlan;
|
|
2590
|
+
let shouldStartApp = false;
|
|
2583
2591
|
if (source === 'docker' || source === 'npm' || source === 'git') {
|
|
2584
2592
|
this.logStage('Preparing application');
|
|
2585
2593
|
if (source === 'docker') {
|
|
@@ -2591,111 +2599,28 @@ export default class Install extends Command {
|
|
|
2591
2599
|
printInfo('Application image ready.');
|
|
2592
2600
|
}
|
|
2593
2601
|
if (!parsed['prepare-only']) {
|
|
2594
|
-
|
|
2595
|
-
hookName: 'beforeAppInstall',
|
|
2596
|
-
envName,
|
|
2597
|
-
source,
|
|
2598
|
-
appResults,
|
|
2599
|
-
downloadResults,
|
|
2600
|
-
dbResults,
|
|
2601
|
-
rootResults,
|
|
2602
|
-
envAddResults,
|
|
2603
|
-
defaultApiHost,
|
|
2604
|
-
});
|
|
2605
|
-
dockerAppPlan = await this.installDockerApp({
|
|
2606
|
-
envName,
|
|
2607
|
-
dockerNetworkName,
|
|
2608
|
-
dockerContainerPrefix,
|
|
2609
|
-
appResults,
|
|
2610
|
-
downloadResults,
|
|
2611
|
-
dbResults,
|
|
2612
|
-
rootResults,
|
|
2613
|
-
builtinDbPlan,
|
|
2614
|
-
force: parsed.force,
|
|
2615
|
-
commandStdio,
|
|
2616
|
-
});
|
|
2617
|
-
appResults.appKey = dockerAppPlan.appKey;
|
|
2618
|
-
appResults.timeZone = dockerAppPlan.timeZone;
|
|
2602
|
+
shouldStartApp = true;
|
|
2619
2603
|
}
|
|
2620
2604
|
}
|
|
2621
2605
|
else if (source === 'npm' || source === 'git') {
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
? Install.resolveLocalProjectRoot({
|
|
2625
|
-
envName,
|
|
2626
|
-
appResults,
|
|
2627
|
-
downloadResults,
|
|
2628
|
-
})
|
|
2629
|
-
: await this.downloadLocalApp({
|
|
2606
|
+
if (!parsed['skip-download'] && !parsed['prepare-only']) {
|
|
2607
|
+
await this.downloadLocalApp({
|
|
2630
2608
|
envName,
|
|
2631
2609
|
appResults,
|
|
2632
2610
|
downloadResults,
|
|
2633
2611
|
verbose: parsed.verbose,
|
|
2634
2612
|
});
|
|
2635
|
-
if (!parsed['skip-download'] && !parsed['prepare-only']) {
|
|
2636
2613
|
printInfo('Application files ready.');
|
|
2637
2614
|
}
|
|
2638
2615
|
if (!parsed['prepare-only']) {
|
|
2639
|
-
|
|
2640
|
-
hookName: 'beforeAppInstall',
|
|
2641
|
-
envName,
|
|
2642
|
-
source,
|
|
2643
|
-
appResults,
|
|
2644
|
-
downloadResults,
|
|
2645
|
-
dbResults,
|
|
2646
|
-
rootResults,
|
|
2647
|
-
envAddResults,
|
|
2648
|
-
projectRoot,
|
|
2649
|
-
defaultApiHost,
|
|
2650
|
-
});
|
|
2651
|
-
localAppPlan = await this.startLocalApp({
|
|
2652
|
-
envName,
|
|
2653
|
-
source: localSource,
|
|
2654
|
-
projectRoot,
|
|
2655
|
-
appResults,
|
|
2656
|
-
dbResults,
|
|
2657
|
-
rootResults,
|
|
2658
|
-
commandStdio,
|
|
2659
|
-
});
|
|
2660
|
-
appResults.appKey = localAppPlan.appKey;
|
|
2661
|
-
appResults.timeZone = localAppPlan.timeZone;
|
|
2616
|
+
shouldStartApp = true;
|
|
2662
2617
|
}
|
|
2663
2618
|
}
|
|
2664
2619
|
}
|
|
2665
2620
|
else {
|
|
2666
2621
|
this.logDetail('Skipped app download and install.');
|
|
2667
2622
|
}
|
|
2668
|
-
if (
|
|
2669
|
-
this.logStage('Starting NocoBase');
|
|
2670
|
-
await this.waitForAppHealthCheck(Install.resolveApiBaseUrl({
|
|
2671
|
-
appResults,
|
|
2672
|
-
envAddResults,
|
|
2673
|
-
defaultApiHost,
|
|
2674
|
-
}), {
|
|
2675
|
-
containerName: dockerAppPlan?.containerName,
|
|
2676
|
-
verbose: parsed.verbose,
|
|
2677
|
-
});
|
|
2678
|
-
const displayApiBaseUrl = Install.resolveApiBaseUrl({
|
|
2679
|
-
appResults,
|
|
2680
|
-
envAddResults,
|
|
2681
|
-
defaultApiHost,
|
|
2682
|
-
});
|
|
2683
|
-
printInfo(`NocoBase is ready at ${formatInstallDisplayUrl(displayApiBaseUrl)}`);
|
|
2684
|
-
appResults.setupState = 'installed';
|
|
2685
|
-
await this.runInstallHookIfNeeded({
|
|
2686
|
-
hookName: 'afterAppStart',
|
|
2687
|
-
envName,
|
|
2688
|
-
source,
|
|
2689
|
-
appResults,
|
|
2690
|
-
downloadResults,
|
|
2691
|
-
dbResults,
|
|
2692
|
-
rootResults,
|
|
2693
|
-
envAddResults,
|
|
2694
|
-
projectRoot: localAppPlan?.projectRoot,
|
|
2695
|
-
defaultApiHost,
|
|
2696
|
-
});
|
|
2697
|
-
}
|
|
2698
|
-
if (dockerAppPlan || localAppPlan || builtinDbPlan) {
|
|
2623
|
+
if (shouldStartApp || builtinDbPlan) {
|
|
2699
2624
|
await this.saveInstalledEnv({
|
|
2700
2625
|
envName,
|
|
2701
2626
|
appResults,
|
|
@@ -2705,10 +2630,14 @@ export default class Install extends Command {
|
|
|
2705
2630
|
envAddResults,
|
|
2706
2631
|
});
|
|
2707
2632
|
}
|
|
2633
|
+
if (shouldStartApp) {
|
|
2634
|
+
this.logStage('Starting NocoBase');
|
|
2635
|
+
await this.config.runCommand('app:start', this.buildAppStartArgv({ envName, verbose: parsed.verbose }));
|
|
2636
|
+
}
|
|
2708
2637
|
await this.syncInstalledEnvConnection({
|
|
2709
2638
|
envName,
|
|
2710
2639
|
envAddResults,
|
|
2711
|
-
appReady:
|
|
2640
|
+
appReady: shouldStartApp,
|
|
2712
2641
|
skipAuth: Boolean(parsed['skip-auth']),
|
|
2713
2642
|
});
|
|
2714
2643
|
if (!parsed['prepare-only']) {
|
|
@@ -2717,7 +2646,7 @@ export default class Install extends Command {
|
|
|
2717
2646
|
if (parsed['prepare-only']) {
|
|
2718
2647
|
printInfo(`Preparation complete for "${envName}". Activate the license, then run \`nb app start --env ${envName}\`.`);
|
|
2719
2648
|
}
|
|
2720
|
-
else if (!
|
|
2649
|
+
else if (!shouldStartApp) {
|
|
2721
2650
|
printInfo(`Install config for "${envName}" has been saved.`);
|
|
2722
2651
|
}
|
|
2723
2652
|
}
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { Command, Flags } from '@oclif/core';
|
|
10
10
|
import { formatMissingManagedAppEnvMessage, resolveManagedAppRuntime, } from '../../../lib/app-runtime.js';
|
|
11
|
+
import { resolveEnvProxyEntry, setEnvProxyEntry } from '../../../lib/auth-store.js';
|
|
12
|
+
import { resolveDefaultConfigScope } from '../../../lib/cli-home.js';
|
|
11
13
|
import { getCaddyProxyDriver, writeManualCaddyProxyBundle, writeCaddyProxyBundle, resolveCaddyProxyRuntimeContext, } from '../../../lib/proxy-caddy.js';
|
|
12
14
|
import { normalizeProxyListenPort } from '../../../lib/proxy-nginx.js';
|
|
13
15
|
import { announceTargetEnv, failTask, startTask, succeedTask } from '../../../lib/ui.js';
|
|
@@ -17,7 +19,7 @@ export default class ProxyCaddyGenerate extends Command {
|
|
|
17
19
|
'<%= config.bin %> proxy caddy generate --host app1.example.com',
|
|
18
20
|
'<%= config.bin %> proxy caddy generate --env app1 --host app1.example.com',
|
|
19
21
|
'<%= config.bin %> proxy caddy generate --env app1 --host app1.example.com --port 8080',
|
|
20
|
-
'<%= config.bin %> proxy caddy generate --manual --name default --
|
|
22
|
+
'<%= config.bin %> proxy caddy generate --manual --name default --storage-path /path/to/storage --dist-root-path /path/to/dist-client --runtime-version 2.1.0 --upstream-port 13000',
|
|
21
23
|
];
|
|
22
24
|
static flags = {
|
|
23
25
|
env: Flags.string({
|
|
@@ -31,9 +33,6 @@ export default class ProxyCaddyGenerate extends Command {
|
|
|
31
33
|
name: Flags.string({
|
|
32
34
|
description: 'Output bundle name used under .nocobase/proxy/caddy in manual mode',
|
|
33
35
|
}),
|
|
34
|
-
'app-port': Flags.string({
|
|
35
|
-
description: 'Upstream NocoBase app port in manual mode',
|
|
36
|
-
}),
|
|
37
36
|
'storage-path': Flags.string({
|
|
38
37
|
description: 'Path to the NocoBase storage directory in manual mode',
|
|
39
38
|
}),
|
|
@@ -49,6 +48,9 @@ export default class ProxyCaddyGenerate extends Command {
|
|
|
49
48
|
'upstream-host': Flags.string({
|
|
50
49
|
description: 'Upstream host used by caddy reverse_proxy in manual mode',
|
|
51
50
|
}),
|
|
51
|
+
'upstream-port': Flags.string({
|
|
52
|
+
description: 'Upstream port used by caddy reverse_proxy in manual mode',
|
|
53
|
+
}),
|
|
52
54
|
'cdn-base-url': Flags.string({
|
|
53
55
|
description: 'Client asset CDN base URL used when generating runtime HTML',
|
|
54
56
|
}),
|
|
@@ -73,16 +75,16 @@ export default class ProxyCaddyGenerate extends Command {
|
|
|
73
75
|
}
|
|
74
76
|
if (manual) {
|
|
75
77
|
const name = flags.name?.trim() || undefined;
|
|
76
|
-
const
|
|
77
|
-
const
|
|
78
|
+
const requestedUpstreamPort = flags['upstream-port']?.trim() || undefined;
|
|
79
|
+
const upstreamPort = normalizeProxyListenPort(requestedUpstreamPort);
|
|
78
80
|
const storagePath = flags['storage-path']?.trim() || undefined;
|
|
79
81
|
const distRootPath = flags['dist-root-path']?.trim() || undefined;
|
|
80
82
|
const runtimeVersion = flags['runtime-version']?.trim() || undefined;
|
|
81
|
-
if (
|
|
82
|
-
this.error(`Invalid manual
|
|
83
|
+
if (requestedUpstreamPort && !upstreamPort) {
|
|
84
|
+
this.error(`Invalid manual upstream port "${requestedUpstreamPort}". Use an integer between 1 and 65535.`);
|
|
83
85
|
}
|
|
84
|
-
if (!name || !
|
|
85
|
-
this.error('Manual mode requires `--name`, `--
|
|
86
|
+
if (!name || !upstreamPort || !storagePath || !distRootPath || !runtimeVersion) {
|
|
87
|
+
this.error('Manual mode requires `--name`, `--upstream-port`, `--storage-path`, `--dist-root-path`, and `--runtime-version`.');
|
|
86
88
|
}
|
|
87
89
|
const driver = await getCaddyProxyDriver();
|
|
88
90
|
const runtimeContext = await resolveCaddyProxyRuntimeContext();
|
|
@@ -91,12 +93,12 @@ export default class ProxyCaddyGenerate extends Command {
|
|
|
91
93
|
try {
|
|
92
94
|
const { bundle, status } = await writeManualCaddyProxyBundle({
|
|
93
95
|
name,
|
|
94
|
-
appPort,
|
|
95
96
|
storagePath,
|
|
96
97
|
distRootPath,
|
|
97
98
|
runtimeVersion,
|
|
98
99
|
appPublicPath: flags['app-public-path']?.trim() || undefined,
|
|
99
100
|
upstreamHost: flags['upstream-host']?.trim() || undefined,
|
|
101
|
+
upstreamPort,
|
|
100
102
|
cdnBaseUrl: flags['cdn-base-url']?.trim() || undefined,
|
|
101
103
|
}, {
|
|
102
104
|
host: flags.host?.trim() || undefined,
|
|
@@ -129,12 +131,18 @@ export default class ProxyCaddyGenerate extends Command {
|
|
|
129
131
|
announceTargetEnv(runtime.envName);
|
|
130
132
|
startTask(`Generating caddy proxy config for env "${runtime.envName}" with the ${driver} driver...`);
|
|
131
133
|
try {
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
134
|
+
const savedAppEntryOptions = resolveEnvProxyEntry(runtime.env.config, 'caddy');
|
|
135
|
+
const appEntryOptions = {
|
|
136
|
+
host: flags.host?.trim() || savedAppEntryOptions?.host,
|
|
137
|
+
port: normalizedPort ?? (savedAppEntryOptions?.port !== undefined ? String(savedAppEntryOptions.port) : undefined),
|
|
138
|
+
};
|
|
139
|
+
const { bundle, status } = await writeCaddyProxyBundle(runtime, appEntryOptions, runtimeContext, {
|
|
136
140
|
cdnBaseUrl: flags['cdn-base-url']?.trim() || undefined,
|
|
137
141
|
});
|
|
142
|
+
await setEnvProxyEntry(runtime.envName, 'caddy', {
|
|
143
|
+
host: appEntryOptions.host,
|
|
144
|
+
port: appEntryOptions.port ? Number(appEntryOptions.port) : undefined,
|
|
145
|
+
}, { scope: resolveDefaultConfigScope() });
|
|
138
146
|
succeedTask(status === 'created'
|
|
139
147
|
? `Saved caddy proxy files for env "${runtime.envName}" under ${bundle.entryDir}, and created app.caddy at ${bundle.appConfigPath}.`
|
|
140
148
|
: `Saved caddy proxy files for env "${runtime.envName}" under ${bundle.entryDir}, and refreshed app.caddy at ${bundle.appConfigPath}.`);
|
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { Command, Flags } from '@oclif/core';
|
|
10
10
|
import { formatMissingManagedAppEnvMessage, resolveManagedAppRuntime, } from '../../../lib/app-runtime.js';
|
|
11
|
+
import { resolveDefaultConfigScope } from '../../../lib/cli-home.js';
|
|
11
12
|
import { getNginxProxyDriver, normalizeProxyListenPort, resolveNginxProxyRuntimeContext, writeManualNginxProxyBundle, writeNginxProxyBundle, } from '../../../lib/proxy-nginx.js';
|
|
13
|
+
import { resolveEnvProxyEntry, setEnvProxyEntry } from '../../../lib/auth-store.js';
|
|
12
14
|
import { announceTargetEnv, failTask, startTask, succeedTask } from '../../../lib/ui.js';
|
|
13
15
|
export default class ProxyNginxGenerate extends Command {
|
|
14
16
|
static summary = 'Generate nginx proxy files for one managed env';
|
|
@@ -16,7 +18,7 @@ export default class ProxyNginxGenerate extends Command {
|
|
|
16
18
|
'<%= config.bin %> proxy nginx generate --host app1.example.com',
|
|
17
19
|
'<%= config.bin %> proxy nginx generate --env app1 --host app1.example.com',
|
|
18
20
|
'<%= config.bin %> proxy nginx generate --env app1 --host app1.example.com --port 8080',
|
|
19
|
-
'<%= config.bin %> proxy nginx generate --manual --name default --
|
|
21
|
+
'<%= config.bin %> proxy nginx generate --manual --name default --storage-path /path/to/storage --dist-root-path /path/to/dist-client --runtime-version 2.1.0 --upstream-port 13000',
|
|
20
22
|
];
|
|
21
23
|
static flags = {
|
|
22
24
|
env: Flags.string({
|
|
@@ -30,9 +32,6 @@ export default class ProxyNginxGenerate extends Command {
|
|
|
30
32
|
name: Flags.string({
|
|
31
33
|
description: 'Output bundle name used under .nocobase/proxy/nginx in manual mode',
|
|
32
34
|
}),
|
|
33
|
-
'app-port': Flags.string({
|
|
34
|
-
description: 'Upstream NocoBase app port in manual mode',
|
|
35
|
-
}),
|
|
36
35
|
'storage-path': Flags.string({
|
|
37
36
|
description: 'Path to the NocoBase storage directory in manual mode',
|
|
38
37
|
}),
|
|
@@ -48,6 +47,9 @@ export default class ProxyNginxGenerate extends Command {
|
|
|
48
47
|
'upstream-host': Flags.string({
|
|
49
48
|
description: 'Upstream host used by nginx proxy_pass in manual mode',
|
|
50
49
|
}),
|
|
50
|
+
'upstream-port': Flags.string({
|
|
51
|
+
description: 'Upstream port used by nginx proxy_pass in manual mode',
|
|
52
|
+
}),
|
|
51
53
|
'cdn-base-url': Flags.string({
|
|
52
54
|
description: 'Client asset CDN base URL used when generating runtime HTML',
|
|
53
55
|
}),
|
|
@@ -76,16 +78,16 @@ export default class ProxyNginxGenerate extends Command {
|
|
|
76
78
|
}
|
|
77
79
|
if (manual) {
|
|
78
80
|
const name = flags.name?.trim() || undefined;
|
|
79
|
-
const
|
|
80
|
-
const
|
|
81
|
+
const requestedUpstreamPort = flags['upstream-port']?.trim() || undefined;
|
|
82
|
+
const upstreamPort = normalizeProxyListenPort(requestedUpstreamPort);
|
|
81
83
|
const storagePath = flags['storage-path']?.trim() || undefined;
|
|
82
84
|
const distRootPath = flags['dist-root-path']?.trim() || undefined;
|
|
83
85
|
const runtimeVersion = flags['runtime-version']?.trim() || undefined;
|
|
84
|
-
if (
|
|
85
|
-
this.error(`Invalid manual
|
|
86
|
+
if (requestedUpstreamPort && !upstreamPort) {
|
|
87
|
+
this.error(`Invalid manual upstream port "${requestedUpstreamPort}". Use an integer between 1 and 65535.`);
|
|
86
88
|
}
|
|
87
|
-
if (!name || !
|
|
88
|
-
this.error('Manual mode requires `--name`, `--
|
|
89
|
+
if (!name || !upstreamPort || !storagePath || !distRootPath || !runtimeVersion) {
|
|
90
|
+
this.error('Manual mode requires `--name`, `--upstream-port`, `--storage-path`, `--dist-root-path`, and `--runtime-version`.');
|
|
89
91
|
}
|
|
90
92
|
const driver = await getNginxProxyDriver();
|
|
91
93
|
const runtimeContext = await resolveNginxProxyRuntimeContext();
|
|
@@ -94,12 +96,12 @@ export default class ProxyNginxGenerate extends Command {
|
|
|
94
96
|
try {
|
|
95
97
|
const { bundle, status } = await writeManualNginxProxyBundle({
|
|
96
98
|
name,
|
|
97
|
-
appPort,
|
|
98
99
|
storagePath,
|
|
99
100
|
distRootPath,
|
|
100
101
|
runtimeVersion,
|
|
101
102
|
appPublicPath: flags['app-public-path']?.trim() || undefined,
|
|
102
103
|
upstreamHost: flags['upstream-host']?.trim() || undefined,
|
|
104
|
+
upstreamPort,
|
|
103
105
|
cdnBaseUrl: flags['cdn-base-url']?.trim() || undefined,
|
|
104
106
|
}, {
|
|
105
107
|
host: flags.host?.trim() || undefined,
|
|
@@ -132,13 +134,19 @@ export default class ProxyNginxGenerate extends Command {
|
|
|
132
134
|
announceTargetEnv(runtime.envName);
|
|
133
135
|
startTask(`Generating nginx proxy config for env "${runtime.envName}" with the ${driver} driver...`);
|
|
134
136
|
try {
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
137
|
+
const savedAppEntryOptions = resolveEnvProxyEntry(runtime.env.config, 'nginx');
|
|
138
|
+
const appEntryOptions = {
|
|
139
|
+
host: flags.host?.trim() || savedAppEntryOptions?.host,
|
|
140
|
+
port: normalizedPort ?? (savedAppEntryOptions?.port !== undefined ? String(savedAppEntryOptions.port) : undefined),
|
|
141
|
+
};
|
|
142
|
+
const { bundle, status } = await writeNginxProxyBundle(runtime, appEntryOptions, runtimeContext, {
|
|
139
143
|
cdnBaseUrl: flags['cdn-base-url']?.trim() || undefined,
|
|
140
144
|
force: flags.force,
|
|
141
145
|
});
|
|
146
|
+
await setEnvProxyEntry(runtime.envName, 'nginx', {
|
|
147
|
+
host: appEntryOptions.host,
|
|
148
|
+
port: appEntryOptions.port ? Number(appEntryOptions.port) : undefined,
|
|
149
|
+
}, { scope: resolveDefaultConfigScope() });
|
|
142
150
|
succeedTask(status === 'created'
|
|
143
151
|
? `Saved nginx proxy files for env "${runtime.envName}" under ${bundle.entryDir}, and created editable app entry config at ${bundle.appConfigPath}.`
|
|
144
152
|
: `Saved nginx proxy files for env "${runtime.envName}" under ${bundle.entryDir}, and refreshed editable app entry config at ${bundle.appConfigPath}.`);
|