@nocobase/cli 2.2.0-beta.8 → 2.2.0-test.15
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 +24 -0
- package/dist/commands/app/start.js +25 -2
- package/dist/commands/config/set.js +1 -0
- package/dist/commands/env/info.js +11 -1
- package/dist/commands/init.js +131 -4
- package/dist/commands/install.js +179 -131
- package/dist/commands/portal/create.js +105 -0
- package/dist/commands/portal/deploy.js +81 -0
- package/dist/commands/portal/destroy.js +104 -0
- package/dist/commands/portal/dev.js +71 -0
- package/dist/commands/portal/index.js +20 -0
- package/dist/commands/portal/info.js +82 -0
- package/dist/commands/portal/list.js +98 -0
- package/dist/commands/portal/pull.js +77 -0
- package/dist/commands/portal/push.js +79 -0
- 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/dev.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 +71 -1
- package/dist/lib/cli-config.js +74 -2
- package/dist/lib/docker-image.js +94 -6
- package/dist/lib/env-config.js +8 -0
- package/dist/lib/env-proxy-config.js +48 -0
- package/dist/lib/env-proxy.js +266 -61
- package/dist/lib/managed-init-env.js +6 -1
- package/dist/lib/portal-command-env.js +31 -0
- package/dist/lib/portal-create.js +488 -0
- package/dist/lib/portal-deploy.js +275 -0
- package/dist/lib/portal-destroy.js +100 -0
- package/dist/lib/portal-dev.js +79 -0
- package/dist/lib/portal-env-files.js +53 -0
- package/dist/lib/portal-info.js +31 -0
- package/dist/lib/portal-list.js +197 -0
- package/dist/lib/portal-source.js +416 -0
- package/dist/lib/portal-template.js +190 -0
- 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/lib/run-npm.js +17 -16
- package/dist/lib/ui.js +28 -1
- package/dist/locale/en-US.json +192 -38
- package/dist/locale/zh-CN.json +192 -38
- package/package.json +6 -3
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';
|
|
@@ -29,9 +29,10 @@ import { buildStoredEnvConfig } from '../lib/env-config.js';
|
|
|
29
29
|
import { resolveDockerEnvFileArg } from "../lib/docker-env-file.js";
|
|
30
30
|
import { startDockerLogFollower } from '../lib/docker-log-stream.js';
|
|
31
31
|
import { buildInitAppEnvVarsFromConfig } from '../lib/managed-init-env.js';
|
|
32
|
+
import { prepareInitialPortalTemplate } from '../lib/portal-template.js';
|
|
32
33
|
import { buildHookContext, persistHookScript, resolveHookScriptPath, runHookScriptHook, } from '../lib/hook-script.js';
|
|
33
34
|
import { areConfiguredPathsEquivalent, deriveConfiguredSourcePath, deriveConfiguredStoragePath, inferConfiguredAppPathFromLegacyConfig, } from '../lib/env-paths.js';
|
|
34
|
-
import Download
|
|
35
|
+
import Download from './download.js';
|
|
35
36
|
import EnvAdd from "./env/add.js";
|
|
36
37
|
import { resolveAppUrlFromApiBaseUrl } from "./env/shared.js";
|
|
37
38
|
const DEFAULT_INSTALL_ENV_NAME = 'local';
|
|
@@ -45,18 +46,6 @@ const DEFAULT_INSTALL_DB_PORTS = {
|
|
|
45
46
|
mariadb: '3306',
|
|
46
47
|
kingbase: '54321',
|
|
47
48
|
};
|
|
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
49
|
const DEFAULT_INSTALL_DB_DATABASE = 'nocobase';
|
|
61
50
|
const DEFAULT_INSTALL_DB_USER = 'nocobase';
|
|
62
51
|
const DEFAULT_INSTALL_DB_PASSWORD = 'nocobase';
|
|
@@ -65,6 +54,10 @@ const DEFAULT_INSTALL_ROOT_EMAIL = 'admin@nocobase.com';
|
|
|
65
54
|
const DEFAULT_INSTALL_ROOT_PASSWORD = 'admin123';
|
|
66
55
|
const DEFAULT_INSTALL_ROOT_NICKNAME = 'Super Admin';
|
|
67
56
|
const DEFAULT_INSTALL_API_HOST = '127.0.0.1';
|
|
57
|
+
const DEFAULT_INSTALL_DEVELOPMENT_MODE = 'no-code';
|
|
58
|
+
const DEFAULT_INSTALL_PORTAL_NAME = 'admin';
|
|
59
|
+
const DEFAULT_INSTALL_PORTAL_TEMPLATE = 'git@github.com:nocobase/admin-starter.git';
|
|
60
|
+
const INSTALL_DEVELOPMENT_MODES = ['no-code', 'vibe-coding'];
|
|
68
61
|
function toOptionalPromptString(value) {
|
|
69
62
|
const text = String(value ?? '').trim();
|
|
70
63
|
return text || undefined;
|
|
@@ -179,22 +172,26 @@ function downloadVersionPromptValue(version) {
|
|
|
179
172
|
}
|
|
180
173
|
function supportsBuiltinDbDialect(value) {
|
|
181
174
|
const dialect = String(value ?? '').trim();
|
|
182
|
-
return
|
|
175
|
+
return INSTALL_DB_DIALECTS.includes(dialect);
|
|
183
176
|
}
|
|
184
177
|
export function defaultDbPortForDialect(value) {
|
|
185
178
|
const dialect = String(value ?? 'postgres').trim();
|
|
186
179
|
return DEFAULT_INSTALL_DB_PORTS[isInstallDbDialect(dialect) ? dialect : 'postgres'];
|
|
187
180
|
}
|
|
188
|
-
function defaultBuiltinDbImageForDialect(value) {
|
|
181
|
+
function defaultBuiltinDbImageForDialect(value, options) {
|
|
189
182
|
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;
|
|
183
|
+
return resolveBuiltinDbImage(dialect, { registry: options?.registry });
|
|
194
184
|
}
|
|
195
185
|
function defaultDbDatabaseForDialect(value) {
|
|
196
186
|
return String(value ?? '').trim() === 'kingbase' ? 'kingbase' : DEFAULT_INSTALL_DB_DATABASE;
|
|
197
187
|
}
|
|
188
|
+
function isVibeCodingMode(values) {
|
|
189
|
+
return String(values.developmentMode ?? DEFAULT_INSTALL_DEVELOPMENT_MODE).trim() === 'vibe-coding';
|
|
190
|
+
}
|
|
191
|
+
function supportsDbSchemaPrompt(value) {
|
|
192
|
+
const dialect = String(value ?? '').trim();
|
|
193
|
+
return dialect === 'postgres' || dialect === 'kingbase';
|
|
194
|
+
}
|
|
198
195
|
function defaultDbHostForBuiltinDb(values) {
|
|
199
196
|
return values.builtinDb ? DEFAULT_INSTALL_BUILTIN_DB_HOST : DEFAULT_INSTALL_DB_HOST;
|
|
200
197
|
}
|
|
@@ -415,6 +412,16 @@ export default class Install extends Command {
|
|
|
415
412
|
'app-public-path': Flags.string({
|
|
416
413
|
description: 'Public path for the local app, for example / or /console/',
|
|
417
414
|
}),
|
|
415
|
+
'development-mode': Flags.string({
|
|
416
|
+
description: 'Initial development mode for the installed app',
|
|
417
|
+
options: [...INSTALL_DEVELOPMENT_MODES],
|
|
418
|
+
}),
|
|
419
|
+
'portal-name': Flags.string({
|
|
420
|
+
description: 'Initial portal name when --development-mode vibe-coding is used',
|
|
421
|
+
}),
|
|
422
|
+
'portal-template': Flags.string({
|
|
423
|
+
description: 'Initial portal template Git URL or local path when --development-mode vibe-coding is used',
|
|
424
|
+
}),
|
|
418
425
|
'root-username': Flags.string({
|
|
419
426
|
description: 'Initial admin username for the installed app',
|
|
420
427
|
required: false,
|
|
@@ -524,6 +531,42 @@ export default class Install extends Command {
|
|
|
524
531
|
yesInitialValue: '/',
|
|
525
532
|
validate: validateAppPublicPath,
|
|
526
533
|
},
|
|
534
|
+
developmentMode: {
|
|
535
|
+
type: 'select',
|
|
536
|
+
message: installText('prompts.developmentMode.message'),
|
|
537
|
+
options: [
|
|
538
|
+
{
|
|
539
|
+
value: 'no-code',
|
|
540
|
+
label: installText('prompts.developmentMode.noCodeLabel'),
|
|
541
|
+
hint: installText('prompts.developmentMode.noCodeHint'),
|
|
542
|
+
},
|
|
543
|
+
{
|
|
544
|
+
value: 'vibe-coding',
|
|
545
|
+
label: installText('prompts.developmentMode.vibeCodingLabel'),
|
|
546
|
+
hint: installText('prompts.developmentMode.vibeCodingHint'),
|
|
547
|
+
},
|
|
548
|
+
],
|
|
549
|
+
initialValue: DEFAULT_INSTALL_DEVELOPMENT_MODE,
|
|
550
|
+
yesInitialValue: DEFAULT_INSTALL_DEVELOPMENT_MODE,
|
|
551
|
+
required: true,
|
|
552
|
+
},
|
|
553
|
+
portalName: {
|
|
554
|
+
type: 'text',
|
|
555
|
+
message: installText('prompts.portalName.message'),
|
|
556
|
+
placeholder: DEFAULT_INSTALL_PORTAL_NAME,
|
|
557
|
+
initialValue: DEFAULT_INSTALL_PORTAL_NAME,
|
|
558
|
+
yesInitialValue: DEFAULT_INSTALL_PORTAL_NAME,
|
|
559
|
+
hidden: (values) => !isVibeCodingMode(values),
|
|
560
|
+
required: true,
|
|
561
|
+
},
|
|
562
|
+
portalTemplate: {
|
|
563
|
+
type: 'text',
|
|
564
|
+
message: installText('prompts.portalTemplate.message'),
|
|
565
|
+
placeholder: DEFAULT_INSTALL_PORTAL_TEMPLATE,
|
|
566
|
+
yesInitialValue: DEFAULT_INSTALL_PORTAL_TEMPLATE,
|
|
567
|
+
hidden: (values) => !isVibeCodingMode(values),
|
|
568
|
+
required: true,
|
|
569
|
+
},
|
|
527
570
|
};
|
|
528
571
|
}
|
|
529
572
|
static dbPrompts = {
|
|
@@ -551,7 +594,9 @@ export default class Install extends Command {
|
|
|
551
594
|
type: 'text',
|
|
552
595
|
message: installText('prompts.builtinDbImage.message'),
|
|
553
596
|
placeholder: installText('prompts.builtinDbImage.placeholder'),
|
|
554
|
-
initialValue: (values) => defaultBuiltinDbImageForDialect(values.dbDialect
|
|
597
|
+
initialValue: (values) => defaultBuiltinDbImageForDialect(values.dbDialect, {
|
|
598
|
+
registry: String(values.builtinDbImageRegistry ?? '').trim() || undefined,
|
|
599
|
+
}),
|
|
555
600
|
hidden: (values) => !values.builtinDb || !supportsBuiltinDbDialect(values.dbDialect),
|
|
556
601
|
required: true,
|
|
557
602
|
},
|
|
@@ -601,7 +646,7 @@ export default class Install extends Command {
|
|
|
601
646
|
type: 'text',
|
|
602
647
|
message: installText('prompts.dbSchema.message'),
|
|
603
648
|
placeholder: installText('prompts.dbSchema.placeholder'),
|
|
604
|
-
hidden: (values) =>
|
|
649
|
+
hidden: (values) => !supportsDbSchemaPrompt(values.dbDialect),
|
|
605
650
|
},
|
|
606
651
|
dbTablePrefix: {
|
|
607
652
|
type: 'text',
|
|
@@ -781,6 +826,24 @@ export default class Install extends Command {
|
|
|
781
826
|
preset.appPublicPath = v;
|
|
782
827
|
}
|
|
783
828
|
}
|
|
829
|
+
if (flags['development-mode'] !== undefined) {
|
|
830
|
+
const v = String(flags['development-mode'] ?? '').trim();
|
|
831
|
+
if (v) {
|
|
832
|
+
preset.developmentMode = v;
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
if (flags['portal-name'] !== undefined) {
|
|
836
|
+
const v = String(flags['portal-name'] ?? '').trim();
|
|
837
|
+
if (v) {
|
|
838
|
+
preset.portalName = v;
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
if (flags['portal-template'] !== undefined) {
|
|
842
|
+
const v = String(flags['portal-template'] ?? '').trim();
|
|
843
|
+
if (v) {
|
|
844
|
+
preset.portalTemplate = v;
|
|
845
|
+
}
|
|
846
|
+
}
|
|
784
847
|
if (flags['root-username'] !== undefined) {
|
|
785
848
|
preset.rootUsername = String(flags['root-username'] ?? '').trim();
|
|
786
849
|
}
|
|
@@ -872,6 +935,9 @@ export default class Install extends Command {
|
|
|
872
935
|
'appPort',
|
|
873
936
|
'storagePath',
|
|
874
937
|
'appPublicPath',
|
|
938
|
+
'developmentMode',
|
|
939
|
+
'portalName',
|
|
940
|
+
'portalTemplate',
|
|
875
941
|
]);
|
|
876
942
|
}
|
|
877
943
|
static buildDbPresetValuesFromFlags(flags, argv = process.argv.slice(2)) {
|
|
@@ -1121,6 +1187,9 @@ export default class Install extends Command {
|
|
|
1121
1187
|
const rootPassword = Install.toOptionalPromptString(config.rootPassword);
|
|
1122
1188
|
const rootNickname = Install.toOptionalPromptString(config.rootNickname);
|
|
1123
1189
|
const lang = Install.toOptionalPromptString(config.lang);
|
|
1190
|
+
const developmentMode = Install.toOptionalPromptString(config.developmentMode);
|
|
1191
|
+
const portalName = Install.toOptionalPromptString(config.portalName);
|
|
1192
|
+
const portalTemplate = Install.toOptionalPromptString(config.portalTemplate);
|
|
1124
1193
|
const auth = config.auth;
|
|
1125
1194
|
const savedAuthType = Install.toOptionalPromptString(config.authType) ?? Install.toOptionalPromptString(auth?.type);
|
|
1126
1195
|
const appPreset = {
|
|
@@ -1130,6 +1199,9 @@ export default class Install extends Command {
|
|
|
1130
1199
|
...(appPort ? { appPort } : {}),
|
|
1131
1200
|
...(storagePath ? { storagePath } : {}),
|
|
1132
1201
|
...(appPublicPath ? { appPublicPath } : {}),
|
|
1202
|
+
...(developmentMode ? { developmentMode } : {}),
|
|
1203
|
+
...(portalName ? { portalName } : {}),
|
|
1204
|
+
...(portalTemplate ? { portalTemplate } : {}),
|
|
1133
1205
|
...(hookScript ? { hookScript } : {}),
|
|
1134
1206
|
};
|
|
1135
1207
|
const downloadPreset = {
|
|
@@ -1272,6 +1344,12 @@ export default class Install extends Command {
|
|
|
1272
1344
|
warn: params.warnOnPortFallback ?? true,
|
|
1273
1345
|
});
|
|
1274
1346
|
}
|
|
1347
|
+
if (params.flags['portal-template'] === undefined) {
|
|
1348
|
+
const defaultPortalTemplate = Install.toOptionalPromptString(await getCliConfigValue('default-portal-template'));
|
|
1349
|
+
if (defaultPortalTemplate) {
|
|
1350
|
+
initialValues.portalTemplate = defaultPortalTemplate;
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1275
1353
|
return initialValues;
|
|
1276
1354
|
}
|
|
1277
1355
|
static shouldPublishBuiltinDbPortForValues(values) {
|
|
@@ -1279,19 +1357,25 @@ export default class Install extends Command {
|
|
|
1279
1357
|
return builtinDb && Install.shouldPublishBuiltinDbPort(values.source);
|
|
1280
1358
|
}
|
|
1281
1359
|
static async buildDbPromptInitialValues(params) {
|
|
1282
|
-
|
|
1283
|
-
return {};
|
|
1284
|
-
}
|
|
1360
|
+
const configuredRegistry = await getCliConfigValue('nb-image-registry');
|
|
1285
1361
|
const values = {
|
|
1286
1362
|
...params.downloadResults,
|
|
1287
1363
|
...params.dbPreset,
|
|
1288
1364
|
};
|
|
1365
|
+
const dockerRegistry = String(values.dockerRegistry ?? '').trim() || resolveOfficialDockerRegistry(configuredRegistry);
|
|
1366
|
+
const dialect = String(values.dbDialect ?? 'postgres').trim() || 'postgres';
|
|
1367
|
+
const initialValues = values.builtinDb !== false && params.dbPreset.builtinDbImage === undefined
|
|
1368
|
+
? { builtinDbImage: defaultBuiltinDbImageForDialect(dialect, { registry: dockerRegistry }) }
|
|
1369
|
+
: {};
|
|
1370
|
+
if (params.flags['db-port'] !== undefined) {
|
|
1371
|
+
return initialValues;
|
|
1372
|
+
}
|
|
1289
1373
|
if (!Install.shouldPublishBuiltinDbPortForValues(values)) {
|
|
1290
|
-
return
|
|
1374
|
+
return initialValues;
|
|
1291
1375
|
}
|
|
1292
|
-
const dialect = String(values.dbDialect ?? 'postgres').trim() || 'postgres';
|
|
1293
1376
|
const defaultPort = defaultDbPortForDialect(dialect);
|
|
1294
1377
|
return {
|
|
1378
|
+
...initialValues,
|
|
1295
1379
|
dbPort: await Install.resolveAvailableDefaultPort(defaultPort, {
|
|
1296
1380
|
label: `Default ${dialect} port`,
|
|
1297
1381
|
warn: params.warnOnPortFallback ?? true,
|
|
@@ -1302,12 +1386,13 @@ export default class Install extends Command {
|
|
|
1302
1386
|
* When install runs {@link Download.prompts} after app prompts, align the download
|
|
1303
1387
|
* output directory with app settings, while Docker registry defaults follow the CLI locale.
|
|
1304
1388
|
*/
|
|
1305
|
-
static buildDownloadPromptOptionsForInstall(appResults, envName) {
|
|
1389
|
+
static async buildDownloadPromptOptionsForInstall(appResults, envName) {
|
|
1306
1390
|
const appRoot = resolveConfiguredSourcePathValue(appResults, envName);
|
|
1307
1391
|
const lang = String(appResults.lang ?? DEFAULT_INSTALL_LANG).trim() || DEFAULT_INSTALL_LANG;
|
|
1392
|
+
const dockerRegistry = resolveOfficialDockerRegistry(await getCliConfigValue('nb-image-registry'));
|
|
1308
1393
|
const initialValues = {
|
|
1309
1394
|
lang,
|
|
1310
|
-
dockerRegistry
|
|
1395
|
+
dockerRegistry,
|
|
1311
1396
|
outputDir: appRoot,
|
|
1312
1397
|
};
|
|
1313
1398
|
const values = {
|
|
@@ -1432,14 +1517,17 @@ export default class Install extends Command {
|
|
|
1432
1517
|
static buildDockerAppContainerName(envName, containerPrefix) {
|
|
1433
1518
|
return Install.sanitizeDockerResourceName(`${Install.buildBuiltinDbContainerPrefix(containerPrefix)}-${envName}-app`);
|
|
1434
1519
|
}
|
|
1435
|
-
static buildInitAppEnvVars(params) {
|
|
1520
|
+
static buildInitAppEnvVars(params, options = {}) {
|
|
1436
1521
|
return buildInitAppEnvVarsFromConfig({
|
|
1437
1522
|
lang: String(params.appResults.lang ?? ''),
|
|
1438
1523
|
rootUsername: String(params.rootResults.rootUsername ?? ''),
|
|
1439
1524
|
rootEmail: String(params.rootResults.rootEmail ?? ''),
|
|
1440
1525
|
rootPassword: String(params.rootResults.rootPassword ?? ''),
|
|
1441
1526
|
rootNickname: String(params.rootResults.rootNickname ?? ''),
|
|
1442
|
-
|
|
1527
|
+
developmentMode: String(params.appResults.developmentMode ?? ''),
|
|
1528
|
+
portalName: String(params.appResults.portalName ?? ''),
|
|
1529
|
+
portalTemplate: String(params.appResults.portalTemplate ?? ''),
|
|
1530
|
+
}, options);
|
|
1443
1531
|
}
|
|
1444
1532
|
static shouldPublishBuiltinDbPort(source) {
|
|
1445
1533
|
return String(source ?? '').trim() !== 'docker';
|
|
@@ -1760,12 +1848,15 @@ export default class Install extends Command {
|
|
|
1760
1848
|
return plan;
|
|
1761
1849
|
}
|
|
1762
1850
|
static async buildDockerAppPlan(params) {
|
|
1851
|
+
const configuredRegistry = await getCliConfigValue('nb-image-registry');
|
|
1852
|
+
const configuredVariant = normalizeNbImageVariant(await getCliConfigValue('nb-image-variant')) ?? DEFAULT_NB_IMAGE_VARIANT;
|
|
1763
1853
|
const dockerRegistry = String(downloadResultsValue(params.downloadResults, 'dockerRegistry') ?? '').trim() ||
|
|
1764
|
-
|
|
1854
|
+
resolveOfficialDockerRegistry(configuredRegistry);
|
|
1765
1855
|
const version = String(downloadResultsValue(params.downloadResults, 'version') ?? '').trim() || DEFAULT_DOCKER_VERSION;
|
|
1766
1856
|
const imageRef = resolveDockerImageRef(dockerRegistry, version, {
|
|
1767
|
-
defaultRegistry:
|
|
1857
|
+
defaultRegistry: resolveOfficialDockerRegistry(configuredRegistry),
|
|
1768
1858
|
defaultVersion: DEFAULT_DOCKER_VERSION,
|
|
1859
|
+
variant: inferNbImageRegistryFromRepository(dockerRegistry) ? configuredVariant : undefined,
|
|
1769
1860
|
});
|
|
1770
1861
|
const appPort = String(params.appResults.appPort ?? DEFAULT_INSTALL_APP_PORT).trim() || DEFAULT_INSTALL_APP_PORT;
|
|
1771
1862
|
const configuredStoragePath = resolveConfiguredStoragePathValue(params.appResults, params.envName);
|
|
@@ -1791,7 +1882,8 @@ export default class Install extends Command {
|
|
|
1791
1882
|
const initEnvVars = Install.buildInitAppEnvVars({
|
|
1792
1883
|
appResults: params.appResults,
|
|
1793
1884
|
rootResults: params.rootResults,
|
|
1794
|
-
});
|
|
1885
|
+
}, { includePortal: false });
|
|
1886
|
+
const containerPort = resolveDockerImageContainerPort(imageRef);
|
|
1795
1887
|
const args = [
|
|
1796
1888
|
'run',
|
|
1797
1889
|
'-d',
|
|
@@ -1800,7 +1892,7 @@ export default class Install extends Command {
|
|
|
1800
1892
|
'--network',
|
|
1801
1893
|
params.networkName,
|
|
1802
1894
|
'-p',
|
|
1803
|
-
`${appPort}
|
|
1895
|
+
`${appPort}:${containerPort}`,
|
|
1804
1896
|
];
|
|
1805
1897
|
if (envFile) {
|
|
1806
1898
|
args.push('--env-file', envFile);
|
|
@@ -1990,12 +2082,15 @@ export default class Install extends Command {
|
|
|
1990
2082
|
}
|
|
1991
2083
|
async ensureSkippedDownloadInputsReady(params) {
|
|
1992
2084
|
if (params.source === 'docker') {
|
|
2085
|
+
const configuredRegistry = await getCliConfigValue('nb-image-registry');
|
|
2086
|
+
const configuredVariant = normalizeNbImageVariant(await getCliConfigValue('nb-image-variant')) ?? DEFAULT_NB_IMAGE_VARIANT;
|
|
1993
2087
|
const dockerRegistry = String(downloadResultsValue(params.downloadResults, 'dockerRegistry') ?? '').trim() ||
|
|
1994
|
-
|
|
2088
|
+
resolveOfficialDockerRegistry(configuredRegistry);
|
|
1995
2089
|
const version = String(downloadResultsValue(params.downloadResults, 'version') ?? '').trim() || DEFAULT_DOCKER_VERSION;
|
|
1996
2090
|
const imageRef = resolveDockerImageRef(dockerRegistry, version, {
|
|
1997
|
-
defaultRegistry:
|
|
2091
|
+
defaultRegistry: resolveOfficialDockerRegistry(configuredRegistry),
|
|
1998
2092
|
defaultVersion: DEFAULT_DOCKER_VERSION,
|
|
2093
|
+
variant: inferNbImageRegistryFromRepository(dockerRegistry) ? configuredVariant : undefined,
|
|
1999
2094
|
});
|
|
2000
2095
|
const imageExists = await commandSucceeds('docker', ['image', 'inspect', imageRef]);
|
|
2001
2096
|
if (!imageExists) {
|
|
@@ -2064,7 +2159,7 @@ export default class Install extends Command {
|
|
|
2064
2159
|
...Install.buildInitAppEnvVars({
|
|
2065
2160
|
appResults: params.appResults,
|
|
2066
2161
|
rootResults: params.rootResults,
|
|
2067
|
-
}),
|
|
2162
|
+
}, { includePortal: false }),
|
|
2068
2163
|
};
|
|
2069
2164
|
setOptionalEnvVar(env, 'APP_PUBLIC_PATH', Install.toOptionalPromptString(params.appResults.appPublicPath));
|
|
2070
2165
|
setOptionalEnvVar(env, 'DB_SCHEMA', optionalEnvString(params.dbResults.dbSchema));
|
|
@@ -2254,6 +2349,26 @@ export default class Install extends Command {
|
|
|
2254
2349
|
}
|
|
2255
2350
|
await this.config.runCommand('env:update', [params.envName]);
|
|
2256
2351
|
}
|
|
2352
|
+
buildAppStartArgv(params) {
|
|
2353
|
+
const argv = ['--env', params.envName, '--yes', '--no-sync-licensed-plugins', '--hook-command', 'init'];
|
|
2354
|
+
if (params.verbose) {
|
|
2355
|
+
argv.push('--verbose');
|
|
2356
|
+
}
|
|
2357
|
+
return argv;
|
|
2358
|
+
}
|
|
2359
|
+
async prepareInitialPortalIfNeeded(params) {
|
|
2360
|
+
await prepareInitialPortalTemplate({
|
|
2361
|
+
developmentMode: Install.toOptionalPromptString(params.appResults.developmentMode),
|
|
2362
|
+
portalName: Install.toOptionalPromptString(params.appResults.portalName),
|
|
2363
|
+
portalTemplate: Install.toOptionalPromptString(params.appResults.portalTemplate),
|
|
2364
|
+
npmRegistry: Install.toOptionalPromptString(params.appResults.npmRegistry),
|
|
2365
|
+
storagePath: Install.resolveAbsoluteStoragePath(params.envName, params.appResults),
|
|
2366
|
+
verbose: params.verbose,
|
|
2367
|
+
onStartTask: (message) => this.logStage(message),
|
|
2368
|
+
onSucceedTask: (message) => printInfo(message),
|
|
2369
|
+
onFailTask: (message) => printWarning(message),
|
|
2370
|
+
});
|
|
2371
|
+
}
|
|
2257
2372
|
async runInstallHookIfNeeded(params) {
|
|
2258
2373
|
const appPath = Install.resolveAbsoluteAppPath(params.envName, params.appResults);
|
|
2259
2374
|
const savedHookScript = Install.toOptionalPromptString(params.appResults.hookScript);
|
|
@@ -2297,6 +2412,9 @@ export default class Install extends Command {
|
|
|
2297
2412
|
const appRootPath = Install.toOptionalPromptString(params.appResults.appRootPath);
|
|
2298
2413
|
const storagePath = Install.toOptionalPromptString(params.appResults.storagePath);
|
|
2299
2414
|
const appPublicPath = Install.toOptionalPromptString(params.appResults.appPublicPath);
|
|
2415
|
+
const developmentMode = Install.toOptionalPromptString(params.appResults.developmentMode);
|
|
2416
|
+
const portalName = Install.toOptionalPromptString(params.appResults.portalName);
|
|
2417
|
+
const portalTemplate = Install.toOptionalPromptString(params.appResults.portalTemplate);
|
|
2300
2418
|
const derivedAppRootPath = appPath ? deriveConfiguredSourcePath(appPath) : undefined;
|
|
2301
2419
|
const derivedStoragePath = appPath ? deriveConfiguredStoragePath(appPath) : undefined;
|
|
2302
2420
|
const appPort = String(params.appResults.appPort ?? DEFAULT_INSTALL_APP_PORT).trim() || DEFAULT_INSTALL_APP_PORT;
|
|
@@ -2331,6 +2449,9 @@ export default class Install extends Command {
|
|
|
2331
2449
|
...(appPublicPath ? { appPublicPath } : {}),
|
|
2332
2450
|
...(envFile ? { envFile } : {}),
|
|
2333
2451
|
lang: params.appResults.lang,
|
|
2452
|
+
developmentMode,
|
|
2453
|
+
portalName,
|
|
2454
|
+
portalTemplate,
|
|
2334
2455
|
appKey: params.appResults.appKey,
|
|
2335
2456
|
timezone: params.appResults.timeZone,
|
|
2336
2457
|
builtinDb: params.dbResults.builtinDb,
|
|
@@ -2382,6 +2503,7 @@ export default class Install extends Command {
|
|
|
2382
2503
|
'app-root-path': parsed['app-root-path'] ?? Install.toOptionalPromptString(appPreset.appRootPath),
|
|
2383
2504
|
'app-port': parsed['app-port'] ?? Install.toOptionalPromptString(appPreset.appPort),
|
|
2384
2505
|
'storage-path': parsed['storage-path'] ?? Install.toOptionalPromptString(appPreset.storagePath),
|
|
2506
|
+
'portal-template': parsed['portal-template'] ?? Install.toOptionalPromptString(appPreset.portalTemplate),
|
|
2385
2507
|
},
|
|
2386
2508
|
}),
|
|
2387
2509
|
values: appPreset,
|
|
@@ -2391,7 +2513,7 @@ export default class Install extends Command {
|
|
|
2391
2513
|
if (resumePreset?.appPreset?.hookScript !== undefined && appResults.hookScript === undefined) {
|
|
2392
2514
|
appResults.hookScript = resumePreset.appPreset.hookScript;
|
|
2393
2515
|
}
|
|
2394
|
-
const downloadOpts = Install.buildDownloadPromptOptionsForInstall(appResults, envName);
|
|
2516
|
+
const downloadOpts = await Install.buildDownloadPromptOptionsForInstall(appResults, envName);
|
|
2395
2517
|
downloadOpts.values = {
|
|
2396
2518
|
...(resumePreset?.downloadPreset ?? {}),
|
|
2397
2519
|
...downloadOpts.values,
|
|
@@ -2498,7 +2620,6 @@ export default class Install extends Command {
|
|
|
2498
2620
|
const parsed = {
|
|
2499
2621
|
...flags,
|
|
2500
2622
|
};
|
|
2501
|
-
const defaultApiHost = await resolveDefaultApiHost();
|
|
2502
2623
|
if (parsed['skip-auth'] && (parsed['access-token'] !== undefined || parsed.token !== undefined)) {
|
|
2503
2624
|
this.error('--skip-auth cannot be used with --access-token or --token.');
|
|
2504
2625
|
}
|
|
@@ -2578,8 +2699,7 @@ export default class Install extends Command {
|
|
|
2578
2699
|
dbResults.dbUser = builtinDbPlan.dbUser;
|
|
2579
2700
|
dbResults.dbPassword = builtinDbPlan.dbPassword;
|
|
2580
2701
|
}
|
|
2581
|
-
let
|
|
2582
|
-
let localAppPlan;
|
|
2702
|
+
let shouldStartApp = false;
|
|
2583
2703
|
if (source === 'docker' || source === 'npm' || source === 'git') {
|
|
2584
2704
|
this.logStage('Preparing application');
|
|
2585
2705
|
if (source === 'docker') {
|
|
@@ -2591,124 +2711,52 @@ export default class Install extends Command {
|
|
|
2591
2711
|
printInfo('Application image ready.');
|
|
2592
2712
|
}
|
|
2593
2713
|
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;
|
|
2714
|
+
shouldStartApp = true;
|
|
2619
2715
|
}
|
|
2620
2716
|
}
|
|
2621
2717
|
else if (source === 'npm' || source === 'git') {
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
? Install.resolveLocalProjectRoot({
|
|
2625
|
-
envName,
|
|
2626
|
-
appResults,
|
|
2627
|
-
downloadResults,
|
|
2628
|
-
})
|
|
2629
|
-
: await this.downloadLocalApp({
|
|
2718
|
+
if (!parsed['skip-download'] && !parsed['prepare-only']) {
|
|
2719
|
+
await this.downloadLocalApp({
|
|
2630
2720
|
envName,
|
|
2631
2721
|
appResults,
|
|
2632
2722
|
downloadResults,
|
|
2633
2723
|
verbose: parsed.verbose,
|
|
2634
2724
|
});
|
|
2635
|
-
if (!parsed['skip-download'] && !parsed['prepare-only']) {
|
|
2636
2725
|
printInfo('Application files ready.');
|
|
2637
2726
|
}
|
|
2638
2727
|
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;
|
|
2728
|
+
shouldStartApp = true;
|
|
2662
2729
|
}
|
|
2663
2730
|
}
|
|
2664
2731
|
}
|
|
2665
2732
|
else {
|
|
2666
2733
|
this.logDetail('Skipped app download and install.');
|
|
2667
2734
|
}
|
|
2668
|
-
if (
|
|
2669
|
-
this.
|
|
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',
|
|
2735
|
+
if (shouldStartApp || builtinDbPlan) {
|
|
2736
|
+
await this.saveInstalledEnv({
|
|
2687
2737
|
envName,
|
|
2688
|
-
source,
|
|
2689
2738
|
appResults,
|
|
2690
2739
|
downloadResults,
|
|
2691
2740
|
dbResults,
|
|
2692
2741
|
rootResults,
|
|
2693
2742
|
envAddResults,
|
|
2694
|
-
projectRoot: localAppPlan?.projectRoot,
|
|
2695
|
-
defaultApiHost,
|
|
2696
2743
|
});
|
|
2697
2744
|
}
|
|
2698
|
-
if (
|
|
2699
|
-
await this.
|
|
2745
|
+
if (shouldStartApp) {
|
|
2746
|
+
await this.prepareInitialPortalIfNeeded({
|
|
2700
2747
|
envName,
|
|
2701
2748
|
appResults,
|
|
2702
|
-
|
|
2703
|
-
dbResults,
|
|
2704
|
-
rootResults,
|
|
2705
|
-
envAddResults,
|
|
2749
|
+
verbose: parsed.verbose,
|
|
2706
2750
|
});
|
|
2707
2751
|
}
|
|
2752
|
+
if (shouldStartApp) {
|
|
2753
|
+
this.logStage('Starting NocoBase');
|
|
2754
|
+
await this.config.runCommand('app:start', this.buildAppStartArgv({ envName, verbose: parsed.verbose }));
|
|
2755
|
+
}
|
|
2708
2756
|
await this.syncInstalledEnvConnection({
|
|
2709
2757
|
envName,
|
|
2710
2758
|
envAddResults,
|
|
2711
|
-
appReady:
|
|
2759
|
+
appReady: shouldStartApp,
|
|
2712
2760
|
skipAuth: Boolean(parsed['skip-auth']),
|
|
2713
2761
|
});
|
|
2714
2762
|
if (!parsed['prepare-only']) {
|
|
@@ -2717,7 +2765,7 @@ export default class Install extends Command {
|
|
|
2717
2765
|
if (parsed['prepare-only']) {
|
|
2718
2766
|
printInfo(`Preparation complete for "${envName}". Activate the license, then run \`nb app start --env ${envName}\`.`);
|
|
2719
2767
|
}
|
|
2720
|
-
else if (!
|
|
2768
|
+
else if (!shouldStartApp) {
|
|
2721
2769
|
printInfo(`Install config for "${envName}" has been saved.`);
|
|
2722
2770
|
}
|
|
2723
2771
|
}
|