@nocobase/cli 2.2.0-beta.8 → 2.3.0-alpha.1
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/app.conf.tpl +23 -0
- package/assets/env-proxy/nginx/nocobase.conf.tpl +5 -0
- package/assets/env-proxy/nginx/snippets/dist-location.conf +5 -0
- package/assets/env-proxy/nginx/snippets/gzip.conf +17 -0
- package/assets/env-proxy/nginx/snippets/log-format-http.conf +13 -0
- package/assets/env-proxy/nginx/snippets/maps-http.conf +14 -0
- package/assets/env-proxy/nginx/snippets/mime-types.conf +98 -0
- package/assets/env-proxy/nginx/snippets/proxy-location.conf +18 -0
- package/assets/env-proxy/nginx/snippets/spa-location.conf +6 -0
- package/assets/env-proxy/nginx/snippets/uploads-location.conf +21 -0
- package/dist/commands/app/start.js +4 -1
- package/dist/commands/env/info.js +11 -1
- package/dist/commands/init.js +11 -1
- package/dist/commands/install.js +61 -130
- package/dist/commands/proxy/caddy/generate.js +93 -7
- package/dist/commands/proxy/nginx/generate.js +98 -7
- package/dist/commands/revision/create.js +1 -1
- 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 +54 -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 +193 -59
- package/dist/lib/prompt-catalog-terminal.js +32 -19
- package/dist/lib/prompt-web-ui.js +13 -2
- package/dist/lib/proxy-caddy.js +77 -9
- package/dist/lib/proxy-nginx.js +71 -11
- package/dist/locale/en-US.json +38 -38
- package/dist/locale/zh-CN.json +38 -38
- package/package.json +3 -2
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
|
}
|
|
@@ -551,7 +540,9 @@ export default class Install extends Command {
|
|
|
551
540
|
type: 'text',
|
|
552
541
|
message: installText('prompts.builtinDbImage.message'),
|
|
553
542
|
placeholder: installText('prompts.builtinDbImage.placeholder'),
|
|
554
|
-
initialValue: (values) => defaultBuiltinDbImageForDialect(values.dbDialect
|
|
543
|
+
initialValue: (values) => defaultBuiltinDbImageForDialect(values.dbDialect, {
|
|
544
|
+
registry: String(values.builtinDbImageRegistry ?? '').trim() || undefined,
|
|
545
|
+
}),
|
|
555
546
|
hidden: (values) => !values.builtinDb || !supportsBuiltinDbDialect(values.dbDialect),
|
|
556
547
|
required: true,
|
|
557
548
|
},
|
|
@@ -601,7 +592,7 @@ export default class Install extends Command {
|
|
|
601
592
|
type: 'text',
|
|
602
593
|
message: installText('prompts.dbSchema.message'),
|
|
603
594
|
placeholder: installText('prompts.dbSchema.placeholder'),
|
|
604
|
-
hidden: (values) =>
|
|
595
|
+
hidden: (values) => !supportsDbSchemaPrompt(values.dbDialect),
|
|
605
596
|
},
|
|
606
597
|
dbTablePrefix: {
|
|
607
598
|
type: 'text',
|
|
@@ -1279,19 +1270,25 @@ export default class Install extends Command {
|
|
|
1279
1270
|
return builtinDb && Install.shouldPublishBuiltinDbPort(values.source);
|
|
1280
1271
|
}
|
|
1281
1272
|
static async buildDbPromptInitialValues(params) {
|
|
1282
|
-
|
|
1283
|
-
return {};
|
|
1284
|
-
}
|
|
1273
|
+
const configuredRegistry = await getCliConfigValue('nb-image-registry');
|
|
1285
1274
|
const values = {
|
|
1286
1275
|
...params.downloadResults,
|
|
1287
1276
|
...params.dbPreset,
|
|
1288
1277
|
};
|
|
1278
|
+
const dockerRegistry = String(values.dockerRegistry ?? '').trim() || resolveOfficialDockerRegistry(configuredRegistry);
|
|
1279
|
+
const dialect = String(values.dbDialect ?? 'postgres').trim() || 'postgres';
|
|
1280
|
+
const initialValues = values.builtinDb !== false && params.dbPreset.builtinDbImage === undefined
|
|
1281
|
+
? { builtinDbImage: defaultBuiltinDbImageForDialect(dialect, { registry: dockerRegistry }) }
|
|
1282
|
+
: {};
|
|
1283
|
+
if (params.flags['db-port'] !== undefined) {
|
|
1284
|
+
return initialValues;
|
|
1285
|
+
}
|
|
1289
1286
|
if (!Install.shouldPublishBuiltinDbPortForValues(values)) {
|
|
1290
|
-
return
|
|
1287
|
+
return initialValues;
|
|
1291
1288
|
}
|
|
1292
|
-
const dialect = String(values.dbDialect ?? 'postgres').trim() || 'postgres';
|
|
1293
1289
|
const defaultPort = defaultDbPortForDialect(dialect);
|
|
1294
1290
|
return {
|
|
1291
|
+
...initialValues,
|
|
1295
1292
|
dbPort: await Install.resolveAvailableDefaultPort(defaultPort, {
|
|
1296
1293
|
label: `Default ${dialect} port`,
|
|
1297
1294
|
warn: params.warnOnPortFallback ?? true,
|
|
@@ -1302,12 +1299,13 @@ export default class Install extends Command {
|
|
|
1302
1299
|
* When install runs {@link Download.prompts} after app prompts, align the download
|
|
1303
1300
|
* output directory with app settings, while Docker registry defaults follow the CLI locale.
|
|
1304
1301
|
*/
|
|
1305
|
-
static buildDownloadPromptOptionsForInstall(appResults, envName) {
|
|
1302
|
+
static async buildDownloadPromptOptionsForInstall(appResults, envName) {
|
|
1306
1303
|
const appRoot = resolveConfiguredSourcePathValue(appResults, envName);
|
|
1307
1304
|
const lang = String(appResults.lang ?? DEFAULT_INSTALL_LANG).trim() || DEFAULT_INSTALL_LANG;
|
|
1305
|
+
const dockerRegistry = resolveOfficialDockerRegistry(await getCliConfigValue('nb-image-registry'));
|
|
1308
1306
|
const initialValues = {
|
|
1309
1307
|
lang,
|
|
1310
|
-
dockerRegistry
|
|
1308
|
+
dockerRegistry,
|
|
1311
1309
|
outputDir: appRoot,
|
|
1312
1310
|
};
|
|
1313
1311
|
const values = {
|
|
@@ -1760,12 +1758,15 @@ export default class Install extends Command {
|
|
|
1760
1758
|
return plan;
|
|
1761
1759
|
}
|
|
1762
1760
|
static async buildDockerAppPlan(params) {
|
|
1761
|
+
const configuredRegistry = await getCliConfigValue('nb-image-registry');
|
|
1762
|
+
const configuredVariant = normalizeNbImageVariant(await getCliConfigValue('nb-image-variant')) ?? DEFAULT_NB_IMAGE_VARIANT;
|
|
1763
1763
|
const dockerRegistry = String(downloadResultsValue(params.downloadResults, 'dockerRegistry') ?? '').trim() ||
|
|
1764
|
-
|
|
1764
|
+
resolveOfficialDockerRegistry(configuredRegistry);
|
|
1765
1765
|
const version = String(downloadResultsValue(params.downloadResults, 'version') ?? '').trim() || DEFAULT_DOCKER_VERSION;
|
|
1766
1766
|
const imageRef = resolveDockerImageRef(dockerRegistry, version, {
|
|
1767
|
-
defaultRegistry:
|
|
1767
|
+
defaultRegistry: resolveOfficialDockerRegistry(configuredRegistry),
|
|
1768
1768
|
defaultVersion: DEFAULT_DOCKER_VERSION,
|
|
1769
|
+
variant: inferNbImageRegistryFromRepository(dockerRegistry) ? configuredVariant : undefined,
|
|
1769
1770
|
});
|
|
1770
1771
|
const appPort = String(params.appResults.appPort ?? DEFAULT_INSTALL_APP_PORT).trim() || DEFAULT_INSTALL_APP_PORT;
|
|
1771
1772
|
const configuredStoragePath = resolveConfiguredStoragePathValue(params.appResults, params.envName);
|
|
@@ -1792,6 +1793,7 @@ export default class Install extends Command {
|
|
|
1792
1793
|
appResults: params.appResults,
|
|
1793
1794
|
rootResults: params.rootResults,
|
|
1794
1795
|
});
|
|
1796
|
+
const containerPort = resolveDockerImageContainerPort(imageRef);
|
|
1795
1797
|
const args = [
|
|
1796
1798
|
'run',
|
|
1797
1799
|
'-d',
|
|
@@ -1800,7 +1802,7 @@ export default class Install extends Command {
|
|
|
1800
1802
|
'--network',
|
|
1801
1803
|
params.networkName,
|
|
1802
1804
|
'-p',
|
|
1803
|
-
`${appPort}
|
|
1805
|
+
`${appPort}:${containerPort}`,
|
|
1804
1806
|
];
|
|
1805
1807
|
if (envFile) {
|
|
1806
1808
|
args.push('--env-file', envFile);
|
|
@@ -1990,12 +1992,15 @@ export default class Install extends Command {
|
|
|
1990
1992
|
}
|
|
1991
1993
|
async ensureSkippedDownloadInputsReady(params) {
|
|
1992
1994
|
if (params.source === 'docker') {
|
|
1995
|
+
const configuredRegistry = await getCliConfigValue('nb-image-registry');
|
|
1996
|
+
const configuredVariant = normalizeNbImageVariant(await getCliConfigValue('nb-image-variant')) ?? DEFAULT_NB_IMAGE_VARIANT;
|
|
1993
1997
|
const dockerRegistry = String(downloadResultsValue(params.downloadResults, 'dockerRegistry') ?? '').trim() ||
|
|
1994
|
-
|
|
1998
|
+
resolveOfficialDockerRegistry(configuredRegistry);
|
|
1995
1999
|
const version = String(downloadResultsValue(params.downloadResults, 'version') ?? '').trim() || DEFAULT_DOCKER_VERSION;
|
|
1996
2000
|
const imageRef = resolveDockerImageRef(dockerRegistry, version, {
|
|
1997
|
-
defaultRegistry:
|
|
2001
|
+
defaultRegistry: resolveOfficialDockerRegistry(configuredRegistry),
|
|
1998
2002
|
defaultVersion: DEFAULT_DOCKER_VERSION,
|
|
2003
|
+
variant: inferNbImageRegistryFromRepository(dockerRegistry) ? configuredVariant : undefined,
|
|
1999
2004
|
});
|
|
2000
2005
|
const imageExists = await commandSucceeds('docker', ['image', 'inspect', imageRef]);
|
|
2001
2006
|
if (!imageExists) {
|
|
@@ -2254,6 +2259,13 @@ export default class Install extends Command {
|
|
|
2254
2259
|
}
|
|
2255
2260
|
await this.config.runCommand('env:update', [params.envName]);
|
|
2256
2261
|
}
|
|
2262
|
+
buildAppStartArgv(params) {
|
|
2263
|
+
const argv = ['--env', params.envName, '--yes', '--no-sync-licensed-plugins', '--hook-command', 'init'];
|
|
2264
|
+
if (params.verbose) {
|
|
2265
|
+
argv.push('--verbose');
|
|
2266
|
+
}
|
|
2267
|
+
return argv;
|
|
2268
|
+
}
|
|
2257
2269
|
async runInstallHookIfNeeded(params) {
|
|
2258
2270
|
const appPath = Install.resolveAbsoluteAppPath(params.envName, params.appResults);
|
|
2259
2271
|
const savedHookScript = Install.toOptionalPromptString(params.appResults.hookScript);
|
|
@@ -2391,7 +2403,7 @@ export default class Install extends Command {
|
|
|
2391
2403
|
if (resumePreset?.appPreset?.hookScript !== undefined && appResults.hookScript === undefined) {
|
|
2392
2404
|
appResults.hookScript = resumePreset.appPreset.hookScript;
|
|
2393
2405
|
}
|
|
2394
|
-
const downloadOpts = Install.buildDownloadPromptOptionsForInstall(appResults, envName);
|
|
2406
|
+
const downloadOpts = await Install.buildDownloadPromptOptionsForInstall(appResults, envName);
|
|
2395
2407
|
downloadOpts.values = {
|
|
2396
2408
|
...(resumePreset?.downloadPreset ?? {}),
|
|
2397
2409
|
...downloadOpts.values,
|
|
@@ -2498,7 +2510,6 @@ export default class Install extends Command {
|
|
|
2498
2510
|
const parsed = {
|
|
2499
2511
|
...flags,
|
|
2500
2512
|
};
|
|
2501
|
-
const defaultApiHost = await resolveDefaultApiHost();
|
|
2502
2513
|
if (parsed['skip-auth'] && (parsed['access-token'] !== undefined || parsed.token !== undefined)) {
|
|
2503
2514
|
this.error('--skip-auth cannot be used with --access-token or --token.');
|
|
2504
2515
|
}
|
|
@@ -2578,8 +2589,7 @@ export default class Install extends Command {
|
|
|
2578
2589
|
dbResults.dbUser = builtinDbPlan.dbUser;
|
|
2579
2590
|
dbResults.dbPassword = builtinDbPlan.dbPassword;
|
|
2580
2591
|
}
|
|
2581
|
-
let
|
|
2582
|
-
let localAppPlan;
|
|
2592
|
+
let shouldStartApp = false;
|
|
2583
2593
|
if (source === 'docker' || source === 'npm' || source === 'git') {
|
|
2584
2594
|
this.logStage('Preparing application');
|
|
2585
2595
|
if (source === 'docker') {
|
|
@@ -2591,111 +2601,28 @@ export default class Install extends Command {
|
|
|
2591
2601
|
printInfo('Application image ready.');
|
|
2592
2602
|
}
|
|
2593
2603
|
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;
|
|
2604
|
+
shouldStartApp = true;
|
|
2619
2605
|
}
|
|
2620
2606
|
}
|
|
2621
2607
|
else if (source === 'npm' || source === 'git') {
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
? Install.resolveLocalProjectRoot({
|
|
2625
|
-
envName,
|
|
2626
|
-
appResults,
|
|
2627
|
-
downloadResults,
|
|
2628
|
-
})
|
|
2629
|
-
: await this.downloadLocalApp({
|
|
2608
|
+
if (!parsed['skip-download'] && !parsed['prepare-only']) {
|
|
2609
|
+
await this.downloadLocalApp({
|
|
2630
2610
|
envName,
|
|
2631
2611
|
appResults,
|
|
2632
2612
|
downloadResults,
|
|
2633
2613
|
verbose: parsed.verbose,
|
|
2634
2614
|
});
|
|
2635
|
-
if (!parsed['skip-download'] && !parsed['prepare-only']) {
|
|
2636
2615
|
printInfo('Application files ready.');
|
|
2637
2616
|
}
|
|
2638
2617
|
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;
|
|
2618
|
+
shouldStartApp = true;
|
|
2662
2619
|
}
|
|
2663
2620
|
}
|
|
2664
2621
|
}
|
|
2665
2622
|
else {
|
|
2666
2623
|
this.logDetail('Skipped app download and install.');
|
|
2667
2624
|
}
|
|
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) {
|
|
2625
|
+
if (shouldStartApp || builtinDbPlan) {
|
|
2699
2626
|
await this.saveInstalledEnv({
|
|
2700
2627
|
envName,
|
|
2701
2628
|
appResults,
|
|
@@ -2705,10 +2632,14 @@ export default class Install extends Command {
|
|
|
2705
2632
|
envAddResults,
|
|
2706
2633
|
});
|
|
2707
2634
|
}
|
|
2635
|
+
if (shouldStartApp) {
|
|
2636
|
+
this.logStage('Starting NocoBase');
|
|
2637
|
+
await this.config.runCommand('app:start', this.buildAppStartArgv({ envName, verbose: parsed.verbose }));
|
|
2638
|
+
}
|
|
2708
2639
|
await this.syncInstalledEnvConnection({
|
|
2709
2640
|
envName,
|
|
2710
2641
|
envAddResults,
|
|
2711
|
-
appReady:
|
|
2642
|
+
appReady: shouldStartApp,
|
|
2712
2643
|
skipAuth: Boolean(parsed['skip-auth']),
|
|
2713
2644
|
});
|
|
2714
2645
|
if (!parsed['prepare-only']) {
|
|
@@ -2717,7 +2648,7 @@ export default class Install extends Command {
|
|
|
2717
2648
|
if (parsed['prepare-only']) {
|
|
2718
2649
|
printInfo(`Preparation complete for "${envName}". Activate the license, then run \`nb app start --env ${envName}\`.`);
|
|
2719
2650
|
}
|
|
2720
|
-
else if (!
|
|
2651
|
+
else if (!shouldStartApp) {
|
|
2721
2652
|
printInfo(`Install config for "${envName}" has been saved.`);
|
|
2722
2653
|
}
|
|
2723
2654
|
}
|
|
@@ -8,20 +8,51 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { Command, Flags } from '@oclif/core';
|
|
10
10
|
import { formatMissingManagedAppEnvMessage, resolveManagedAppRuntime, } from '../../../lib/app-runtime.js';
|
|
11
|
-
import {
|
|
11
|
+
import { resolveEnvProxyEntry, setEnvProxyEntry } from '../../../lib/auth-store.js';
|
|
12
|
+
import { resolveDefaultConfigScope } from '../../../lib/cli-home.js';
|
|
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';
|
|
14
16
|
export default class ProxyCaddyGenerate extends Command {
|
|
15
17
|
static summary = 'Generate caddy proxy files for one managed env';
|
|
16
18
|
static examples = [
|
|
19
|
+
'<%= config.bin %> proxy caddy generate --host app1.example.com',
|
|
17
20
|
'<%= config.bin %> proxy caddy generate --env app1 --host app1.example.com',
|
|
18
21
|
'<%= config.bin %> proxy caddy generate --env app1 --host app1.example.com --port 8080',
|
|
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',
|
|
19
23
|
];
|
|
20
24
|
static flags = {
|
|
21
25
|
env: Flags.string({
|
|
22
26
|
char: 'e',
|
|
23
|
-
description: 'CLI env name to generate proxy files for',
|
|
24
|
-
|
|
27
|
+
description: 'CLI env name to generate proxy files for. Defaults to the current env when omitted',
|
|
28
|
+
}),
|
|
29
|
+
manual: Flags.boolean({
|
|
30
|
+
description: 'Generate proxy files from explicit runtime flags instead of a saved env',
|
|
31
|
+
default: false,
|
|
32
|
+
}),
|
|
33
|
+
name: Flags.string({
|
|
34
|
+
description: 'Output bundle name used under .nocobase/proxy/caddy in manual mode',
|
|
35
|
+
}),
|
|
36
|
+
'storage-path': Flags.string({
|
|
37
|
+
description: 'Path to the NocoBase storage directory in manual mode',
|
|
38
|
+
}),
|
|
39
|
+
'dist-root-path': Flags.string({
|
|
40
|
+
description: 'Path to the dist-client root directory used to generate index-v1.html and index-v2.html in manual mode',
|
|
41
|
+
}),
|
|
42
|
+
'runtime-version': Flags.string({
|
|
43
|
+
description: 'Frontend runtime version under dist-root-path in manual mode',
|
|
44
|
+
}),
|
|
45
|
+
'app-public-path': Flags.string({
|
|
46
|
+
description: 'Public base path served by the proxied app in manual mode. Defaults to /',
|
|
47
|
+
}),
|
|
48
|
+
'upstream-host': Flags.string({
|
|
49
|
+
description: 'Upstream host used by caddy reverse_proxy in manual mode',
|
|
50
|
+
}),
|
|
51
|
+
'upstream-port': Flags.string({
|
|
52
|
+
description: 'Upstream port used by caddy reverse_proxy in manual mode',
|
|
53
|
+
}),
|
|
54
|
+
'cdn-base-url': Flags.string({
|
|
55
|
+
description: 'Client asset CDN base URL used when generating runtime HTML',
|
|
25
56
|
}),
|
|
26
57
|
host: Flags.string({
|
|
27
58
|
description: 'Host exposed by the caddy site block, such as example.com or localhost',
|
|
@@ -35,9 +66,56 @@ export default class ProxyCaddyGenerate extends Command {
|
|
|
35
66
|
const requestedEnv = flags.env?.trim() || undefined;
|
|
36
67
|
const requestedPort = flags.port?.trim() || undefined;
|
|
37
68
|
const normalizedPort = normalizeProxyListenPort(requestedPort);
|
|
69
|
+
const manual = Boolean(flags.manual);
|
|
38
70
|
if (requestedPort && !normalizedPort) {
|
|
39
71
|
this.error(`Invalid proxy entry port "${requestedPort}". Use an integer between 1 and 65535.`);
|
|
40
72
|
}
|
|
73
|
+
if (manual && requestedEnv) {
|
|
74
|
+
this.error('`--manual` cannot be combined with `--env`.');
|
|
75
|
+
}
|
|
76
|
+
if (manual) {
|
|
77
|
+
const name = flags.name?.trim() || undefined;
|
|
78
|
+
const requestedUpstreamPort = flags['upstream-port']?.trim() || undefined;
|
|
79
|
+
const upstreamPort = normalizeProxyListenPort(requestedUpstreamPort);
|
|
80
|
+
const storagePath = flags['storage-path']?.trim() || undefined;
|
|
81
|
+
const distRootPath = flags['dist-root-path']?.trim() || undefined;
|
|
82
|
+
const runtimeVersion = flags['runtime-version']?.trim() || undefined;
|
|
83
|
+
if (requestedUpstreamPort && !upstreamPort) {
|
|
84
|
+
this.error(`Invalid manual upstream port "${requestedUpstreamPort}". Use an integer between 1 and 65535.`);
|
|
85
|
+
}
|
|
86
|
+
if (!name || !upstreamPort || !storagePath || !distRootPath || !runtimeVersion) {
|
|
87
|
+
this.error('Manual mode requires `--name`, `--upstream-port`, `--storage-path`, `--dist-root-path`, and `--runtime-version`.');
|
|
88
|
+
}
|
|
89
|
+
const driver = await getCaddyProxyDriver();
|
|
90
|
+
const runtimeContext = await resolveCaddyProxyRuntimeContext();
|
|
91
|
+
announceTargetEnv(name);
|
|
92
|
+
startTask(`Generating caddy proxy config for env "${name}" with the ${driver} driver...`);
|
|
93
|
+
try {
|
|
94
|
+
const { bundle, status } = await writeManualCaddyProxyBundle({
|
|
95
|
+
name,
|
|
96
|
+
storagePath,
|
|
97
|
+
distRootPath,
|
|
98
|
+
runtimeVersion,
|
|
99
|
+
appPublicPath: flags['app-public-path']?.trim() || undefined,
|
|
100
|
+
upstreamHost: flags['upstream-host']?.trim() || undefined,
|
|
101
|
+
upstreamPort,
|
|
102
|
+
cdnBaseUrl: flags['cdn-base-url']?.trim() || undefined,
|
|
103
|
+
}, {
|
|
104
|
+
host: flags.host?.trim() || undefined,
|
|
105
|
+
port: normalizedPort,
|
|
106
|
+
}, runtimeContext, {
|
|
107
|
+
cdnBaseUrl: flags['cdn-base-url']?.trim() || undefined,
|
|
108
|
+
});
|
|
109
|
+
succeedTask(status === 'created'
|
|
110
|
+
? `Saved caddy proxy files for env "${name}" under ${bundle.entryDir}, and created app.caddy at ${bundle.appConfigPath}.`
|
|
111
|
+
: `Saved caddy proxy files for env "${name}" under ${bundle.entryDir}, and refreshed app.caddy at ${bundle.appConfigPath}.`);
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
failTask(`Failed to generate caddy proxy config for env "${name}".`);
|
|
115
|
+
this.error(error instanceof Error ? error.message : String(error));
|
|
116
|
+
}
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
41
119
|
const runtime = await resolveManagedAppRuntime(requestedEnv);
|
|
42
120
|
if (!runtime) {
|
|
43
121
|
this.error(formatMissingManagedAppEnvMessage(requestedEnv));
|
|
@@ -53,10 +131,18 @@ export default class ProxyCaddyGenerate extends Command {
|
|
|
53
131
|
announceTargetEnv(runtime.envName);
|
|
54
132
|
startTask(`Generating caddy proxy config for env "${runtime.envName}" with the ${driver} driver...`);
|
|
55
133
|
try {
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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, {
|
|
140
|
+
cdnBaseUrl: flags['cdn-base-url']?.trim() || undefined,
|
|
141
|
+
});
|
|
142
|
+
await setEnvProxyEntry(runtime.envName, 'caddy', {
|
|
143
|
+
host: appEntryOptions.host,
|
|
144
|
+
port: appEntryOptions.port ? Number(appEntryOptions.port) : undefined,
|
|
145
|
+
}, { scope: resolveDefaultConfigScope() });
|
|
60
146
|
succeedTask(status === 'created'
|
|
61
147
|
? `Saved caddy proxy files for env "${runtime.envName}" under ${bundle.entryDir}, and created app.caddy at ${bundle.appConfigPath}.`
|
|
62
148
|
: `Saved caddy proxy files for env "${runtime.envName}" under ${bundle.entryDir}, and refreshed app.caddy at ${bundle.appConfigPath}.`);
|
|
@@ -8,19 +8,54 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { Command, Flags } from '@oclif/core';
|
|
10
10
|
import { formatMissingManagedAppEnvMessage, resolveManagedAppRuntime, } from '../../../lib/app-runtime.js';
|
|
11
|
-
import {
|
|
11
|
+
import { resolveDefaultConfigScope } from '../../../lib/cli-home.js';
|
|
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';
|
|
15
17
|
static examples = [
|
|
18
|
+
'<%= config.bin %> proxy nginx generate --host app1.example.com',
|
|
16
19
|
'<%= config.bin %> proxy nginx generate --env app1 --host app1.example.com',
|
|
17
20
|
'<%= config.bin %> proxy nginx generate --env app1 --host app1.example.com --port 8080',
|
|
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',
|
|
18
22
|
];
|
|
19
23
|
static flags = {
|
|
20
24
|
env: Flags.string({
|
|
21
25
|
char: 'e',
|
|
22
|
-
description: 'CLI env name to generate proxy files for',
|
|
23
|
-
|
|
26
|
+
description: 'CLI env name to generate proxy files for. Defaults to the current env when omitted',
|
|
27
|
+
}),
|
|
28
|
+
manual: Flags.boolean({
|
|
29
|
+
description: 'Generate proxy files from explicit runtime flags instead of a saved env',
|
|
30
|
+
default: false,
|
|
31
|
+
}),
|
|
32
|
+
name: Flags.string({
|
|
33
|
+
description: 'Output bundle name used under .nocobase/proxy/nginx in manual mode',
|
|
34
|
+
}),
|
|
35
|
+
'storage-path': Flags.string({
|
|
36
|
+
description: 'Path to the NocoBase storage directory in manual mode',
|
|
37
|
+
}),
|
|
38
|
+
'dist-root-path': Flags.string({
|
|
39
|
+
description: 'Path to the dist-client root directory used to generate index-v1.html and index-v2.html in manual mode',
|
|
40
|
+
}),
|
|
41
|
+
'runtime-version': Flags.string({
|
|
42
|
+
description: 'Frontend runtime version under dist-root-path in manual mode',
|
|
43
|
+
}),
|
|
44
|
+
'app-public-path': Flags.string({
|
|
45
|
+
description: 'Public base path served by the proxied app in manual mode. Defaults to /',
|
|
46
|
+
}),
|
|
47
|
+
'upstream-host': Flags.string({
|
|
48
|
+
description: 'Upstream host used by nginx proxy_pass in manual mode',
|
|
49
|
+
}),
|
|
50
|
+
'upstream-port': Flags.string({
|
|
51
|
+
description: 'Upstream port used by nginx proxy_pass in manual mode',
|
|
52
|
+
}),
|
|
53
|
+
'cdn-base-url': Flags.string({
|
|
54
|
+
description: 'Client asset CDN base URL used when generating runtime HTML',
|
|
55
|
+
}),
|
|
56
|
+
force: Flags.boolean({
|
|
57
|
+
description: 'Overwrite existing app.conf even when the managed block is missing',
|
|
58
|
+
default: false,
|
|
24
59
|
}),
|
|
25
60
|
host: Flags.string({
|
|
26
61
|
description: 'Host exposed by the nginx entry config, such as example.com or localhost',
|
|
@@ -34,9 +69,56 @@ export default class ProxyNginxGenerate extends Command {
|
|
|
34
69
|
const requestedEnv = flags.env?.trim() || undefined;
|
|
35
70
|
const requestedPort = flags.port?.trim() || undefined;
|
|
36
71
|
const normalizedPort = normalizeProxyListenPort(requestedPort);
|
|
72
|
+
const manual = Boolean(flags.manual);
|
|
37
73
|
if (requestedPort && !normalizedPort) {
|
|
38
74
|
this.error(`Invalid proxy entry port "${requestedPort}". Use an integer between 1 and 65535.`);
|
|
39
75
|
}
|
|
76
|
+
if (manual && requestedEnv) {
|
|
77
|
+
this.error('`--manual` cannot be combined with `--env`.');
|
|
78
|
+
}
|
|
79
|
+
if (manual) {
|
|
80
|
+
const name = flags.name?.trim() || undefined;
|
|
81
|
+
const requestedUpstreamPort = flags['upstream-port']?.trim() || undefined;
|
|
82
|
+
const upstreamPort = normalizeProxyListenPort(requestedUpstreamPort);
|
|
83
|
+
const storagePath = flags['storage-path']?.trim() || undefined;
|
|
84
|
+
const distRootPath = flags['dist-root-path']?.trim() || undefined;
|
|
85
|
+
const runtimeVersion = flags['runtime-version']?.trim() || undefined;
|
|
86
|
+
if (requestedUpstreamPort && !upstreamPort) {
|
|
87
|
+
this.error(`Invalid manual upstream port "${requestedUpstreamPort}". Use an integer between 1 and 65535.`);
|
|
88
|
+
}
|
|
89
|
+
if (!name || !upstreamPort || !storagePath || !distRootPath || !runtimeVersion) {
|
|
90
|
+
this.error('Manual mode requires `--name`, `--upstream-port`, `--storage-path`, `--dist-root-path`, and `--runtime-version`.');
|
|
91
|
+
}
|
|
92
|
+
const driver = await getNginxProxyDriver();
|
|
93
|
+
const runtimeContext = await resolveNginxProxyRuntimeContext();
|
|
94
|
+
announceTargetEnv(name);
|
|
95
|
+
startTask(`Generating nginx proxy config for env "${name}" with the ${driver} driver...`);
|
|
96
|
+
try {
|
|
97
|
+
const { bundle, status } = await writeManualNginxProxyBundle({
|
|
98
|
+
name,
|
|
99
|
+
storagePath,
|
|
100
|
+
distRootPath,
|
|
101
|
+
runtimeVersion,
|
|
102
|
+
appPublicPath: flags['app-public-path']?.trim() || undefined,
|
|
103
|
+
upstreamHost: flags['upstream-host']?.trim() || undefined,
|
|
104
|
+
upstreamPort,
|
|
105
|
+
cdnBaseUrl: flags['cdn-base-url']?.trim() || undefined,
|
|
106
|
+
}, {
|
|
107
|
+
host: flags.host?.trim() || undefined,
|
|
108
|
+
port: normalizedPort,
|
|
109
|
+
}, runtimeContext, {
|
|
110
|
+
force: flags.force,
|
|
111
|
+
});
|
|
112
|
+
succeedTask(status === 'created'
|
|
113
|
+
? `Saved nginx proxy files for env "${name}" under ${bundle.entryDir}, and created editable app entry config at ${bundle.appConfigPath}.`
|
|
114
|
+
: `Saved nginx proxy files for env "${name}" under ${bundle.entryDir}, and refreshed editable app entry config at ${bundle.appConfigPath}.`);
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
failTask(`Failed to generate nginx proxy config for env "${name}".`);
|
|
118
|
+
this.error(error instanceof Error ? error.message : String(error));
|
|
119
|
+
}
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
40
122
|
const runtime = await resolveManagedAppRuntime(requestedEnv);
|
|
41
123
|
if (!runtime) {
|
|
42
124
|
this.error(formatMissingManagedAppEnvMessage(requestedEnv));
|
|
@@ -52,10 +134,19 @@ export default class ProxyNginxGenerate extends Command {
|
|
|
52
134
|
announceTargetEnv(runtime.envName);
|
|
53
135
|
startTask(`Generating nginx proxy config for env "${runtime.envName}" with the ${driver} driver...`);
|
|
54
136
|
try {
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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, {
|
|
143
|
+
cdnBaseUrl: flags['cdn-base-url']?.trim() || undefined,
|
|
144
|
+
force: flags.force,
|
|
145
|
+
});
|
|
146
|
+
await setEnvProxyEntry(runtime.envName, 'nginx', {
|
|
147
|
+
host: appEntryOptions.host,
|
|
148
|
+
port: appEntryOptions.port ? Number(appEntryOptions.port) : undefined,
|
|
149
|
+
}, { scope: resolveDefaultConfigScope() });
|
|
59
150
|
succeedTask(status === 'created'
|
|
60
151
|
? `Saved nginx proxy files for env "${runtime.envName}" under ${bundle.entryDir}, and created editable app entry config at ${bundle.appConfigPath}.`
|
|
61
152
|
: `Saved nginx proxy files for env "${runtime.envName}" under ${bundle.entryDir}, and refreshed editable app entry config at ${bundle.appConfigPath}.`);
|