@nocobase/cli 2.1.0-beta.35 → 2.1.0-beta.36
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/dist/commands/app/upgrade.js +38 -16
- package/dist/commands/backup/create.js +147 -0
- package/dist/commands/backup/index.js +20 -0
- package/dist/commands/backup/restore.js +105 -0
- package/dist/commands/env/add.js +63 -9
- package/dist/commands/env/auth.js +85 -11
- package/dist/commands/init.js +71 -13
- package/dist/commands/install.js +140 -11
- package/dist/commands/license/activate.js +6 -4
- package/dist/commands/source/publish.js +17 -0
- package/dist/commands/v1.js +210 -0
- package/dist/lib/app-managed-resources.js +20 -1
- package/dist/lib/app-runtime.js +13 -4
- package/dist/lib/auth-store.js +28 -5
- package/dist/lib/backup.js +171 -0
- package/dist/lib/bootstrap.js +23 -13
- package/dist/lib/env-config.js +6 -0
- package/dist/lib/run-npm.js +35 -9
- package/dist/lib/source-publish.js +20 -1
- package/dist/lib/source-registry.js +2 -2
- package/package.json +6 -3
package/dist/commands/init.js
CHANGED
|
@@ -33,6 +33,7 @@ const INIT_ENV_ADD_FLAG_NAMES = [
|
|
|
33
33
|
'auth-type',
|
|
34
34
|
'access-token',
|
|
35
35
|
'token',
|
|
36
|
+
'skip-auth',
|
|
36
37
|
];
|
|
37
38
|
const initText = (key, values) => localeText(`commands.init.${key}`, values);
|
|
38
39
|
function withExtraHidden(def, extraHidden) {
|
|
@@ -285,6 +286,19 @@ Prompt modes:
|
|
|
285
286
|
rootPassword: newInstallOnly(Install.rootUserPrompts.rootPassword),
|
|
286
287
|
rootNickname: newInstallOnly(Install.rootUserPrompts.rootNickname),
|
|
287
288
|
};
|
|
289
|
+
buildPromptCatalog(flags) {
|
|
290
|
+
if (!flags['skip-auth']) {
|
|
291
|
+
return Init.prompts;
|
|
292
|
+
}
|
|
293
|
+
const accessTokenPrompt = {
|
|
294
|
+
...EnvAdd.prompts.accessToken,
|
|
295
|
+
hidden: () => true,
|
|
296
|
+
};
|
|
297
|
+
return {
|
|
298
|
+
...Init.prompts,
|
|
299
|
+
accessToken: existingAppOnly(accessTokenPrompt),
|
|
300
|
+
};
|
|
301
|
+
}
|
|
288
302
|
parsedFlagsForPromptSeeds;
|
|
289
303
|
static flags = {
|
|
290
304
|
yes: Flags.boolean({
|
|
@@ -330,6 +344,10 @@ Prompt modes:
|
|
|
330
344
|
if (normalizedFlags.ui && normalizedFlags.yes) {
|
|
331
345
|
this.error('--ui cannot be used with --yes.');
|
|
332
346
|
}
|
|
347
|
+
if (normalizedFlags['skip-auth']
|
|
348
|
+
&& (normalizedFlags['access-token'] !== undefined || normalizedFlags.token !== undefined)) {
|
|
349
|
+
this.error('--skip-auth cannot be used with --access-token or --token.');
|
|
350
|
+
}
|
|
333
351
|
if (normalizedFlags.ui && normalizedFlags.resume) {
|
|
334
352
|
this.error('--ui cannot be used with --resume.');
|
|
335
353
|
}
|
|
@@ -397,9 +415,10 @@ Prompt modes:
|
|
|
397
415
|
}
|
|
398
416
|
}
|
|
399
417
|
const dynamicInitialValues = await Init.buildDynamicInitialValuesForInstall(normalizedFlags, presetValues);
|
|
418
|
+
const promptCatalog = this.buildPromptCatalog(normalizedFlags);
|
|
400
419
|
if (useBrowserUi) {
|
|
401
420
|
presetValues = await runPromptCatalogWebUI({
|
|
402
|
-
stages: Init.buildWebUiStages(),
|
|
421
|
+
stages: Init.buildWebUiStages(promptCatalog),
|
|
403
422
|
values: {
|
|
404
423
|
...dynamicInitialValues,
|
|
405
424
|
...presetValues,
|
|
@@ -418,7 +437,7 @@ Prompt modes:
|
|
|
418
437
|
},
|
|
419
438
|
});
|
|
420
439
|
}
|
|
421
|
-
const results = await runPromptCatalog(
|
|
440
|
+
const results = await runPromptCatalog(promptCatalog, {
|
|
422
441
|
initialValues: dynamicInitialValues,
|
|
423
442
|
values: presetValues,
|
|
424
443
|
yes: normalizedFlags.yes || useBrowserUi || !interactive,
|
|
@@ -434,9 +453,13 @@ Prompt modes:
|
|
|
434
453
|
},
|
|
435
454
|
command: this,
|
|
436
455
|
});
|
|
437
|
-
const
|
|
456
|
+
const normalizedResults = {
|
|
457
|
+
...results,
|
|
458
|
+
...pickKeys(presetValues, ['dbSchema', 'dbTablePrefix', 'dbUnderscored', 'skipAuth']),
|
|
459
|
+
};
|
|
460
|
+
const hasNocobase = normalizedResults.hasNocobase === 'yes';
|
|
438
461
|
const existingEnv = !hasNocobase
|
|
439
|
-
? await getEnv(String(
|
|
462
|
+
? await getEnv(String(normalizedResults.appName ?? '').trim(), { scope: resolveDefaultConfigScope() })
|
|
440
463
|
: undefined;
|
|
441
464
|
if (existingEnv && Boolean(normalizedFlags.force)) {
|
|
442
465
|
printWarning(`Reconfiguring existing env ${pc.cyan(pc.bold(`"${existingEnv.name}"`))} from the global config because ${pc.bold('--force')} was set. The env config will be updated before install starts, then refreshed again after install succeeds.`);
|
|
@@ -450,15 +473,15 @@ Prompt modes:
|
|
|
450
473
|
if (hasNocobase) {
|
|
451
474
|
logInitStage('Connecting to the env');
|
|
452
475
|
printVerbose('Running nb env add');
|
|
453
|
-
await this.config.runCommand('env:add', this.buildEnvAddArgv(
|
|
476
|
+
await this.config.runCommand('env:add', this.buildEnvAddArgv(normalizedResults));
|
|
454
477
|
}
|
|
455
478
|
else {
|
|
456
479
|
logInitStage('Saving env config');
|
|
457
|
-
await this.persistManagedEnvConfig(
|
|
458
|
-
managedInstallResults =
|
|
459
|
-
printInfo(`Saved env config for "${String(
|
|
480
|
+
await this.persistManagedEnvConfig(normalizedResults, normalizedFlags);
|
|
481
|
+
managedInstallResults = normalizedResults;
|
|
482
|
+
printInfo(`Saved env config for "${String(normalizedResults.appName ?? DEFAULT_INIT_APP_NAME).trim() || DEFAULT_INIT_APP_NAME}".`);
|
|
460
483
|
printVerbose('Running nb init');
|
|
461
|
-
await this.config.runCommand('install', this.buildInstallArgv(
|
|
484
|
+
await this.config.runCommand('install', this.buildInstallArgv(normalizedResults, normalizedFlags));
|
|
462
485
|
}
|
|
463
486
|
}
|
|
464
487
|
catch (error) {
|
|
@@ -505,8 +528,7 @@ Prompt modes:
|
|
|
505
528
|
}
|
|
506
529
|
return out;
|
|
507
530
|
}
|
|
508
|
-
static buildWebUiStages() {
|
|
509
|
-
const c = Init.prompts;
|
|
531
|
+
static buildWebUiStages(c = Init.prompts) {
|
|
510
532
|
return [
|
|
511
533
|
{
|
|
512
534
|
sectionTitle: initText('webUi.gettingStarted.title'),
|
|
@@ -599,6 +621,9 @@ Prompt modes:
|
|
|
599
621
|
if (flags['auth-type'] !== undefined && String(flags['auth-type']).trim() !== '') {
|
|
600
622
|
preset.authType = String(flags['auth-type']).trim();
|
|
601
623
|
}
|
|
624
|
+
if (flags['skip-auth']) {
|
|
625
|
+
preset.skipAuth = true;
|
|
626
|
+
}
|
|
602
627
|
const accessToken = String(flags['access-token'] ?? flags.token ?? '');
|
|
603
628
|
if (flags['access-token'] !== undefined || flags.token !== undefined) {
|
|
604
629
|
preset.accessToken = accessToken;
|
|
@@ -649,6 +674,15 @@ Prompt modes:
|
|
|
649
674
|
if (flags['db-password'] !== undefined) {
|
|
650
675
|
preset.dbPassword = String(flags['db-password'] ?? '');
|
|
651
676
|
}
|
|
677
|
+
if (flags['db-schema'] !== undefined && String(flags['db-schema']).trim() !== '') {
|
|
678
|
+
preset.dbSchema = String(flags['db-schema']).trim();
|
|
679
|
+
}
|
|
680
|
+
if (flags['db-table-prefix'] !== undefined && String(flags['db-table-prefix']).trim() !== '') {
|
|
681
|
+
preset.dbTablePrefix = String(flags['db-table-prefix']).trim();
|
|
682
|
+
}
|
|
683
|
+
if (argvHasToken(argv, ['--db-underscored', '--no-db-underscored'])) {
|
|
684
|
+
preset.dbUnderscored = Boolean(flags['db-underscored']);
|
|
685
|
+
}
|
|
652
686
|
if (argvHasToken(argv, ['--fetch-source'])) {
|
|
653
687
|
preset.fetchSource = Boolean(flags['fetch-source']);
|
|
654
688
|
}
|
|
@@ -748,6 +782,8 @@ Prompt modes:
|
|
|
748
782
|
const dbDatabase = String(results.dbDatabase ?? '').trim();
|
|
749
783
|
const dbUser = String(results.dbUser ?? '').trim();
|
|
750
784
|
const dbPassword = String(results.dbPassword ?? '');
|
|
785
|
+
const dbSchema = String(results.dbSchema ?? '').trim();
|
|
786
|
+
const dbTablePrefix = String(results.dbTablePrefix ?? '').trim();
|
|
751
787
|
const apiBaseUrl = String(results.apiBaseUrl ?? '').trim();
|
|
752
788
|
const authType = String(results.authType ?? '').trim() || 'oauth';
|
|
753
789
|
const accessToken = String(results.accessToken ?? '');
|
|
@@ -765,6 +801,7 @@ Prompt modes:
|
|
|
765
801
|
? { kind: 'http' }
|
|
766
802
|
: {}),
|
|
767
803
|
...(apiBaseUrl ? { apiBaseUrl } : appPort ? { apiBaseUrl: `http://127.0.0.1:${appPort}/api` } : {}),
|
|
804
|
+
...(authType ? { authType } : {}),
|
|
768
805
|
...(authType === 'token' && accessToken ? { accessToken } : {}),
|
|
769
806
|
...(source ? { source } : {}),
|
|
770
807
|
...(version ? { downloadVersion: version } : {}),
|
|
@@ -786,6 +823,9 @@ Prompt modes:
|
|
|
786
823
|
...(dbDatabase ? { dbDatabase } : {}),
|
|
787
824
|
...(dbUser ? { dbUser } : {}),
|
|
788
825
|
...(dbPassword ? { dbPassword } : {}),
|
|
826
|
+
...(dbSchema ? { dbSchema } : {}),
|
|
827
|
+
...(dbTablePrefix ? { dbTablePrefix } : {}),
|
|
828
|
+
...(results.dbUnderscored !== undefined ? { dbUnderscored: Boolean(results.dbUnderscored) } : {}),
|
|
789
829
|
}, { scope: resolveDefaultConfigScope() });
|
|
790
830
|
}
|
|
791
831
|
buildEnvAddArgv(results) {
|
|
@@ -793,8 +833,12 @@ Prompt modes:
|
|
|
793
833
|
argv.push('--no-intro');
|
|
794
834
|
argv.push('--api-base-url', String(results.apiBaseUrl ?? DEFAULT_INIT_API_BASE_URL));
|
|
795
835
|
argv.push('--auth-type', String(results.authType ?? 'oauth'));
|
|
796
|
-
|
|
797
|
-
|
|
836
|
+
const accessToken = String(results.accessToken ?? '');
|
|
837
|
+
if (results.skipAuth === true) {
|
|
838
|
+
argv.push('--skip-auth');
|
|
839
|
+
}
|
|
840
|
+
else if (results.authType === 'token' && accessToken) {
|
|
841
|
+
argv.push('--access-token', accessToken);
|
|
798
842
|
}
|
|
799
843
|
return argv;
|
|
800
844
|
}
|
|
@@ -824,6 +868,9 @@ Prompt modes:
|
|
|
824
868
|
if (authType) {
|
|
825
869
|
argv.push('--auth-type', authType);
|
|
826
870
|
}
|
|
871
|
+
if (Boolean(flags['skip-auth']) || results.skipAuth === true) {
|
|
872
|
+
argv.push('--skip-auth');
|
|
873
|
+
}
|
|
827
874
|
if (authType === 'token' && accessToken) {
|
|
828
875
|
argv.push('--access-token', accessToken);
|
|
829
876
|
}
|
|
@@ -935,6 +982,17 @@ Prompt modes:
|
|
|
935
982
|
if (dbPassword) {
|
|
936
983
|
argv.push('--db-password', dbPassword);
|
|
937
984
|
}
|
|
985
|
+
const dbSchema = String(results.dbSchema ?? '').trim();
|
|
986
|
+
if (dbSchema) {
|
|
987
|
+
argv.push('--db-schema', dbSchema);
|
|
988
|
+
}
|
|
989
|
+
const dbTablePrefix = String(results.dbTablePrefix ?? '').trim();
|
|
990
|
+
if (dbTablePrefix) {
|
|
991
|
+
argv.push('--db-table-prefix', dbTablePrefix);
|
|
992
|
+
}
|
|
993
|
+
if (results.dbUnderscored !== undefined) {
|
|
994
|
+
argv.push(Boolean(results.dbUnderscored) ? '--db-underscored' : '--no-db-underscored');
|
|
995
|
+
}
|
|
938
996
|
const rootUsername = String(results.rootUsername ?? '').trim();
|
|
939
997
|
if (rootUsername) {
|
|
940
998
|
argv.push('--root-username', rootUsername);
|
package/dist/commands/install.js
CHANGED
|
@@ -136,6 +136,17 @@ const INSTALL_LANGUAGE_OPTIONS = Object.entries(INSTALL_LANGUAGE_CODES).map(([va
|
|
|
136
136
|
label: `${label} (${value})`,
|
|
137
137
|
}));
|
|
138
138
|
const installText = (key, values) => localeText(`commands.install.${key}`, values);
|
|
139
|
+
function formatDeferredAuthMessage(envName, authType) {
|
|
140
|
+
const normalizedAuthType = String(authType ?? '').trim();
|
|
141
|
+
const nextStep = `Authentication was skipped for env "${envName}". Run \`nb env auth ${envName}\` to finish setup.`;
|
|
142
|
+
if (normalizedAuthType === 'token') {
|
|
143
|
+
return `${nextStep} You will be prompted for an access token.`;
|
|
144
|
+
}
|
|
145
|
+
if (normalizedAuthType === 'oauth') {
|
|
146
|
+
return `${nextStep} A browser sign-in flow will be started.`;
|
|
147
|
+
}
|
|
148
|
+
return nextStep;
|
|
149
|
+
}
|
|
139
150
|
function argvHasToken(argv, tokens) {
|
|
140
151
|
return tokens.some((t) => argv.includes(t));
|
|
141
152
|
}
|
|
@@ -259,6 +270,40 @@ async function commandOutput(command, args, options) {
|
|
|
259
270
|
});
|
|
260
271
|
});
|
|
261
272
|
}
|
|
273
|
+
function optionalEnvString(value) {
|
|
274
|
+
const text = String(value ?? '').trim();
|
|
275
|
+
return text || undefined;
|
|
276
|
+
}
|
|
277
|
+
function optionalEnvBoolean(value) {
|
|
278
|
+
if (value === undefined || value === null) {
|
|
279
|
+
return undefined;
|
|
280
|
+
}
|
|
281
|
+
return Boolean(value);
|
|
282
|
+
}
|
|
283
|
+
function pushOptionalEnvArg(args, key, value) {
|
|
284
|
+
if (typeof value === 'string') {
|
|
285
|
+
if (!value) {
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
args.push('-e', `${key}=${value}`);
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
if (typeof value === 'boolean') {
|
|
292
|
+
args.push('-e', `${key}=${String(value)}`);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
function setOptionalEnvVar(out, key, value) {
|
|
296
|
+
if (typeof value === 'string') {
|
|
297
|
+
if (!value) {
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
out[key] = value;
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
if (typeof value === 'boolean') {
|
|
304
|
+
out[key] = String(value);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
262
307
|
export default class Install extends Command {
|
|
263
308
|
ensuredDockerNetworks = new Set();
|
|
264
309
|
logStage(title) {
|
|
@@ -322,6 +367,10 @@ export default class Install extends Command {
|
|
|
322
367
|
aliases: ['token'],
|
|
323
368
|
description: 'API key or access token when using --auth-type token',
|
|
324
369
|
}),
|
|
370
|
+
'skip-auth': Flags.boolean({
|
|
371
|
+
description: 'Save the env auth mode now and finish authentication later with `nb env auth`',
|
|
372
|
+
default: false,
|
|
373
|
+
}),
|
|
325
374
|
lang: Flags.string({ description: 'Language for the installed NocoBase app', char: 'l', required: false }),
|
|
326
375
|
force: Flags.boolean({
|
|
327
376
|
description: 'Reconfigure an existing env and replace conflicting runtime resources when needed',
|
|
@@ -380,6 +429,17 @@ export default class Install extends Command {
|
|
|
380
429
|
'db-password': Flags.string({
|
|
381
430
|
description: 'Database password for the app',
|
|
382
431
|
}),
|
|
432
|
+
'db-schema': Flags.string({
|
|
433
|
+
description: 'Database schema for the app',
|
|
434
|
+
}),
|
|
435
|
+
'db-table-prefix': Flags.string({
|
|
436
|
+
description: 'Database table prefix for the app',
|
|
437
|
+
}),
|
|
438
|
+
'db-underscored': Flags.boolean({
|
|
439
|
+
allowNo: true,
|
|
440
|
+
description: 'Use underscored database naming for the app',
|
|
441
|
+
default: false,
|
|
442
|
+
}),
|
|
383
443
|
'fetch-source': Flags.boolean({
|
|
384
444
|
description: 'Download NocoBase app files or pull a Docker image before installing',
|
|
385
445
|
default: false,
|
|
@@ -621,6 +681,9 @@ export default class Install extends Command {
|
|
|
621
681
|
preset.authType = authType;
|
|
622
682
|
}
|
|
623
683
|
}
|
|
684
|
+
if (flags['skip-auth']) {
|
|
685
|
+
preset.skipAuth = true;
|
|
686
|
+
}
|
|
624
687
|
if (flags['access-token'] !== undefined || flags.token !== undefined) {
|
|
625
688
|
preset.accessToken = String(flags['access-token'] ?? flags.token ?? '');
|
|
626
689
|
}
|
|
@@ -709,6 +772,21 @@ export default class Install extends Command {
|
|
|
709
772
|
if (flags['db-password'] !== undefined) {
|
|
710
773
|
preset.dbPassword = String(flags['db-password'] ?? '');
|
|
711
774
|
}
|
|
775
|
+
if (flags['db-schema'] !== undefined) {
|
|
776
|
+
const v = String(flags['db-schema'] ?? '').trim();
|
|
777
|
+
if (v) {
|
|
778
|
+
preset.dbSchema = v;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
if (flags['db-table-prefix'] !== undefined) {
|
|
782
|
+
const v = String(flags['db-table-prefix'] ?? '').trim();
|
|
783
|
+
if (v) {
|
|
784
|
+
preset.dbTablePrefix = v;
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
if (argvHasToken(argv, ['--db-underscored', '--no-db-underscored'])) {
|
|
788
|
+
preset.dbUnderscored = flags['db-underscored'];
|
|
789
|
+
}
|
|
712
790
|
return preset;
|
|
713
791
|
}
|
|
714
792
|
static buildAppPresetValuesFromFlags(flags) {
|
|
@@ -731,6 +809,9 @@ export default class Install extends Command {
|
|
|
731
809
|
'dbDatabase',
|
|
732
810
|
'dbUser',
|
|
733
811
|
'dbPassword',
|
|
812
|
+
'dbSchema',
|
|
813
|
+
'dbTablePrefix',
|
|
814
|
+
'dbUnderscored',
|
|
734
815
|
]);
|
|
735
816
|
}
|
|
736
817
|
static buildRootPresetValuesFromFlags(flags) {
|
|
@@ -748,6 +829,27 @@ export default class Install extends Command {
|
|
|
748
829
|
'accessToken',
|
|
749
830
|
]);
|
|
750
831
|
}
|
|
832
|
+
buildEnvAddPromptsForInstall(parsed) {
|
|
833
|
+
const apiBaseUrlPrompt = {
|
|
834
|
+
...EnvAdd.prompts.apiBaseUrl,
|
|
835
|
+
validate: undefined,
|
|
836
|
+
};
|
|
837
|
+
const prompts = {
|
|
838
|
+
...EnvAdd.prompts,
|
|
839
|
+
apiBaseUrl: apiBaseUrlPrompt,
|
|
840
|
+
};
|
|
841
|
+
if (!parsed['skip-auth']) {
|
|
842
|
+
return prompts;
|
|
843
|
+
}
|
|
844
|
+
const accessTokenPrompt = {
|
|
845
|
+
...EnvAdd.prompts.accessToken,
|
|
846
|
+
hidden: () => true,
|
|
847
|
+
};
|
|
848
|
+
return {
|
|
849
|
+
...prompts,
|
|
850
|
+
accessToken: accessTokenPrompt,
|
|
851
|
+
};
|
|
852
|
+
}
|
|
751
853
|
static toOptionalPromptString(value) {
|
|
752
854
|
if (value === undefined || value === null) {
|
|
753
855
|
return undefined;
|
|
@@ -917,12 +1019,16 @@ export default class Install extends Command {
|
|
|
917
1019
|
const dbDatabase = Install.toOptionalPromptString(config.dbDatabase);
|
|
918
1020
|
const dbUser = Install.toOptionalPromptString(config.dbUser);
|
|
919
1021
|
const dbPassword = Install.toOptionalPromptString(config.dbPassword);
|
|
1022
|
+
const dbSchema = Install.toOptionalPromptString(config.dbSchema);
|
|
1023
|
+
const dbTablePrefix = Install.toOptionalPromptString(config.dbTablePrefix);
|
|
1024
|
+
const dbUnderscored = typeof config.dbUnderscored === 'boolean' ? config.dbUnderscored : undefined;
|
|
920
1025
|
const builtinDbImage = Install.toOptionalPromptString(config.builtinDbImage);
|
|
921
1026
|
const rootUsername = Install.toOptionalPromptString(config.rootUsername);
|
|
922
1027
|
const rootEmail = Install.toOptionalPromptString(config.rootEmail);
|
|
923
1028
|
const rootPassword = Install.toOptionalPromptString(config.rootPassword);
|
|
924
1029
|
const rootNickname = Install.toOptionalPromptString(config.rootNickname);
|
|
925
1030
|
const auth = config.auth;
|
|
1031
|
+
const savedAuthType = Install.toOptionalPromptString(config.authType) ?? Install.toOptionalPromptString(auth?.type);
|
|
926
1032
|
const appPreset = {
|
|
927
1033
|
...(appRootPath ? { appRootPath } : {}),
|
|
928
1034
|
...(appPort ? { appPort } : {}),
|
|
@@ -962,6 +1068,9 @@ export default class Install extends Command {
|
|
|
962
1068
|
...(dbDatabase ? { dbDatabase } : {}),
|
|
963
1069
|
...(dbUser ? { dbUser } : {}),
|
|
964
1070
|
...(dbPassword ? { dbPassword } : {}),
|
|
1071
|
+
...(dbSchema ? { dbSchema } : {}),
|
|
1072
|
+
...(dbTablePrefix ? { dbTablePrefix } : {}),
|
|
1073
|
+
...(dbUnderscored !== undefined ? { dbUnderscored } : {}),
|
|
965
1074
|
};
|
|
966
1075
|
const rootPreset = {
|
|
967
1076
|
...(rootUsername ? { rootUsername } : {}),
|
|
@@ -970,13 +1079,13 @@ export default class Install extends Command {
|
|
|
970
1079
|
...(rootNickname ? { rootNickname } : {}),
|
|
971
1080
|
};
|
|
972
1081
|
const envAddPreset = {};
|
|
973
|
-
if (
|
|
1082
|
+
if (savedAuthType === 'token') {
|
|
974
1083
|
envAddPreset.authType = 'token';
|
|
975
1084
|
if (Install.toOptionalPromptString(auth.accessToken)) {
|
|
976
1085
|
envAddPreset.accessToken = String(auth.accessToken);
|
|
977
1086
|
}
|
|
978
1087
|
}
|
|
979
|
-
else if (
|
|
1088
|
+
else if (savedAuthType === 'oauth') {
|
|
980
1089
|
envAddPreset.authType = 'oauth';
|
|
981
1090
|
}
|
|
982
1091
|
return {
|
|
@@ -1596,6 +1705,9 @@ export default class Install extends Command {
|
|
|
1596
1705
|
|| DEFAULT_INSTALL_DB_DATABASE;
|
|
1597
1706
|
const dbUser = String(params.dbResults.dbUser ?? DEFAULT_INSTALL_DB_USER).trim() || DEFAULT_INSTALL_DB_USER;
|
|
1598
1707
|
const dbPassword = String(params.dbResults.dbPassword ?? DEFAULT_INSTALL_DB_PASSWORD) || DEFAULT_INSTALL_DB_PASSWORD;
|
|
1708
|
+
const dbSchema = optionalEnvString(params.dbResults.dbSchema);
|
|
1709
|
+
const dbTablePrefix = optionalEnvString(params.dbResults.dbTablePrefix);
|
|
1710
|
+
const dbUnderscored = optionalEnvBoolean(params.dbResults.dbUnderscored);
|
|
1599
1711
|
const appKey = crypto.randomBytes(32).toString('hex');
|
|
1600
1712
|
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC';
|
|
1601
1713
|
const containerName = Install.buildDockerAppContainerName(params.envName, params.dockerContainerPrefix ?? params.workspaceName);
|
|
@@ -1623,7 +1735,11 @@ export default class Install extends Command {
|
|
|
1623
1735
|
for (const [key, value] of Object.entries(initEnvVars)) {
|
|
1624
1736
|
args.push('-e', `${key}=${value}`);
|
|
1625
1737
|
}
|
|
1626
|
-
args.push('-e', `APP_KEY=${appKey}`, '-e', `DB_DIALECT=${dbDialect}`, '-e', `DB_HOST=${dbHost}`, '-e', `DB_PORT=${dbPort}`, '-e', `DB_DATABASE=${dbDatabase}`, '-e', `DB_USER=${dbUser}`, '-e', `DB_PASSWORD=${dbPassword}`, '-e', `TZ=${timeZone}`, '-v', `${storagePath}:/app/nocobase/storage
|
|
1738
|
+
args.push('-e', `APP_KEY=${appKey}`, '-e', `DB_DIALECT=${dbDialect}`, '-e', `DB_HOST=${dbHost}`, '-e', `DB_PORT=${dbPort}`, '-e', `DB_DATABASE=${dbDatabase}`, '-e', `DB_USER=${dbUser}`, '-e', `DB_PASSWORD=${dbPassword}`, '-e', `TZ=${timeZone}`, '-v', `${storagePath}:/app/nocobase/storage`);
|
|
1739
|
+
pushOptionalEnvArg(args, 'DB_SCHEMA', dbSchema);
|
|
1740
|
+
pushOptionalEnvArg(args, 'DB_TABLE_PREFIX', dbTablePrefix);
|
|
1741
|
+
pushOptionalEnvArg(args, 'DB_UNDERSCORED', dbUnderscored);
|
|
1742
|
+
args.push(imageRef);
|
|
1627
1743
|
return {
|
|
1628
1744
|
source: 'docker',
|
|
1629
1745
|
networkName: params.networkName,
|
|
@@ -1797,6 +1913,9 @@ export default class Install extends Command {
|
|
|
1797
1913
|
rootResults: params.rootResults,
|
|
1798
1914
|
}),
|
|
1799
1915
|
};
|
|
1916
|
+
setOptionalEnvVar(env, 'DB_SCHEMA', optionalEnvString(params.dbResults.dbSchema));
|
|
1917
|
+
setOptionalEnvVar(env, 'DB_TABLE_PREFIX', optionalEnvString(params.dbResults.dbTablePrefix));
|
|
1918
|
+
setOptionalEnvVar(env, 'DB_UNDERSCORED', optionalEnvBoolean(params.dbResults.dbUnderscored));
|
|
1800
1919
|
return env;
|
|
1801
1920
|
}
|
|
1802
1921
|
async startLocalApp(params) {
|
|
@@ -1938,6 +2057,10 @@ export default class Install extends Command {
|
|
|
1938
2057
|
}
|
|
1939
2058
|
const authType = String(params.envAddResults.authType ?? 'oauth').trim()
|
|
1940
2059
|
|| 'oauth';
|
|
2060
|
+
if (params.skipAuth) {
|
|
2061
|
+
printInfo(formatDeferredAuthMessage(params.envName, authType));
|
|
2062
|
+
return;
|
|
2063
|
+
}
|
|
1941
2064
|
if (authType === 'oauth') {
|
|
1942
2065
|
await this.config.runCommand('env:auth', [params.envName]);
|
|
1943
2066
|
}
|
|
@@ -1982,6 +2105,9 @@ export default class Install extends Command {
|
|
|
1982
2105
|
dbDatabase: params.dbResults.dbDatabase,
|
|
1983
2106
|
dbUser: params.dbResults.dbUser,
|
|
1984
2107
|
dbPassword: params.dbResults.dbPassword,
|
|
2108
|
+
dbSchema: params.dbResults.dbSchema,
|
|
2109
|
+
dbTablePrefix: params.dbResults.dbTablePrefix,
|
|
2110
|
+
dbUnderscored: params.dbResults.dbUnderscored,
|
|
1985
2111
|
rootUsername: params.rootResults.rootUsername,
|
|
1986
2112
|
rootEmail: params.rootResults.rootEmail,
|
|
1987
2113
|
rootPassword: params.rootResults.rootPassword,
|
|
@@ -2041,7 +2167,7 @@ export default class Install extends Command {
|
|
|
2041
2167
|
...(resumePreset?.dbPreset ?? {}),
|
|
2042
2168
|
...Install.buildDbPresetValuesFromFlags(parsed),
|
|
2043
2169
|
};
|
|
2044
|
-
const
|
|
2170
|
+
const promptedDbResults = await runPromptCatalog(Install.buildDbPromptsCatalog(envName, downloadResults, {
|
|
2045
2171
|
resume: parsed.resume,
|
|
2046
2172
|
}), {
|
|
2047
2173
|
initialValues: {
|
|
@@ -2059,6 +2185,10 @@ export default class Install extends Command {
|
|
|
2059
2185
|
values: dbPreset,
|
|
2060
2186
|
yes,
|
|
2061
2187
|
});
|
|
2188
|
+
const dbResults = {
|
|
2189
|
+
...promptedDbResults,
|
|
2190
|
+
...pickPresetKeys(dbPreset, ['dbSchema', 'dbTablePrefix', 'dbUnderscored']),
|
|
2191
|
+
};
|
|
2062
2192
|
const rootPreset = Install.buildRootPresetValuesFromFlags(parsed);
|
|
2063
2193
|
const rootResults = await runPromptCatalog(Install.rootUserPrompts, {
|
|
2064
2194
|
initialValues: {},
|
|
@@ -2068,19 +2198,14 @@ export default class Install extends Command {
|
|
|
2068
2198
|
},
|
|
2069
2199
|
yes,
|
|
2070
2200
|
});
|
|
2071
|
-
const envAddPromptsForInstall =
|
|
2072
|
-
...EnvAdd.prompts,
|
|
2073
|
-
apiBaseUrl: {
|
|
2074
|
-
...EnvAdd.prompts.apiBaseUrl,
|
|
2075
|
-
validate: undefined,
|
|
2076
|
-
},
|
|
2077
|
-
};
|
|
2201
|
+
const envAddPromptsForInstall = this.buildEnvAddPromptsForInstall(parsed);
|
|
2078
2202
|
const envAddResults = await runPromptCatalog(envAddPromptsForInstall, {
|
|
2079
2203
|
initialValues: {
|
|
2080
2204
|
apiBaseUrl: `http://127.0.0.1:${appResults.appPort ?? DEFAULT_INSTALL_APP_PORT}/api`,
|
|
2081
2205
|
},
|
|
2082
2206
|
values: {
|
|
2083
2207
|
name: envName,
|
|
2208
|
+
...(parsed['skip-auth'] ? { skipAuth: true } : {}),
|
|
2084
2209
|
...(resumePreset?.envAddPreset ?? {}),
|
|
2085
2210
|
...Install.buildEnvAddPresetValuesFromFlags(parsed),
|
|
2086
2211
|
},
|
|
@@ -2103,6 +2228,9 @@ export default class Install extends Command {
|
|
|
2103
2228
|
const parsed = {
|
|
2104
2229
|
...flags,
|
|
2105
2230
|
};
|
|
2231
|
+
if (parsed['skip-auth'] && (parsed['access-token'] !== undefined || parsed.token !== undefined)) {
|
|
2232
|
+
this.error('--skip-auth cannot be used with --access-token or --token.');
|
|
2233
|
+
}
|
|
2106
2234
|
setVerboseMode(Boolean(parsed.verbose));
|
|
2107
2235
|
const commandStdio = this.commandStdio(parsed.verbose);
|
|
2108
2236
|
if (!parsed['no-intro']) {
|
|
@@ -2235,6 +2363,7 @@ export default class Install extends Command {
|
|
|
2235
2363
|
envName,
|
|
2236
2364
|
envAddResults,
|
|
2237
2365
|
appReady: Boolean(dockerAppPlan || localAppPlan),
|
|
2366
|
+
skipAuth: Boolean(parsed['skip-auth']),
|
|
2238
2367
|
});
|
|
2239
2368
|
if (!dockerAppPlan && !localAppPlan) {
|
|
2240
2369
|
printInfo(`Install config for "${envName}" has been saved.`);
|
|
@@ -21,11 +21,11 @@ async function promptActivationMode() {
|
|
|
21
21
|
return await select({
|
|
22
22
|
message: 'How do you want to activate the license?',
|
|
23
23
|
choices: [
|
|
24
|
-
{ value: 'key', name: 'Use an existing license key' },
|
|
25
24
|
{ value: 'online', name: 'Request and activate a license online' },
|
|
25
|
+
{ value: 'key', name: 'Use an existing license key' },
|
|
26
26
|
{ value: 'cancel', name: 'Cancel' },
|
|
27
27
|
],
|
|
28
|
-
default: '
|
|
28
|
+
default: 'online',
|
|
29
29
|
});
|
|
30
30
|
}
|
|
31
31
|
catch {
|
|
@@ -70,7 +70,7 @@ async function promptLicenseKeyInput() {
|
|
|
70
70
|
return {};
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
|
-
async function promptOnlineActivationInput(initial) {
|
|
73
|
+
async function promptOnlineActivationInput(initial, defaultAppName) {
|
|
74
74
|
let account = String(initial.account ?? '').trim();
|
|
75
75
|
if (!account) {
|
|
76
76
|
try {
|
|
@@ -107,8 +107,10 @@ async function promptOnlineActivationInput(initial) {
|
|
|
107
107
|
let appName = String(initial.appName ?? '').trim();
|
|
108
108
|
if (!appName) {
|
|
109
109
|
try {
|
|
110
|
+
const resolvedDefaultAppName = String(defaultAppName ?? '').trim();
|
|
110
111
|
const answer = await input({
|
|
111
112
|
message: 'Application name',
|
|
113
|
+
default: resolvedDefaultAppName || undefined,
|
|
112
114
|
validate: (value) => String(value ?? '').trim() ? true : 'Application name is required.',
|
|
113
115
|
});
|
|
114
116
|
appName = String(answer ?? '').trim();
|
|
@@ -259,7 +261,7 @@ export default class LicenseActivate extends Command {
|
|
|
259
261
|
if (!isInteractiveTerminal()) {
|
|
260
262
|
this.error('Online activation requires --account, --password, and --desc when not using a TTY.');
|
|
261
263
|
}
|
|
262
|
-
const prompted = await promptOnlineActivationInput(initialOnline);
|
|
264
|
+
const prompted = await promptOnlineActivationInput(initialOnline, runtime.envName);
|
|
263
265
|
if (!prompted) {
|
|
264
266
|
return;
|
|
265
267
|
}
|
|
@@ -10,6 +10,11 @@ import { Command, Flags } from '@oclif/core';
|
|
|
10
10
|
import { buildSuggestedInitCommand, publishSourceSnapshot } from '../../lib/source-publish.js';
|
|
11
11
|
import { failTask, printInfo, startTask, succeedTask } from '../../lib/ui.js';
|
|
12
12
|
function formatPublishFailure(message) {
|
|
13
|
+
if (message.includes('The specified --cwd does not exist:')
|
|
14
|
+
|| message.includes('The specified --cwd is not a directory:')
|
|
15
|
+
|| message.includes('Couldn\'t find a NocoBase source project from --cwd:')) {
|
|
16
|
+
return message;
|
|
17
|
+
}
|
|
13
18
|
return [
|
|
14
19
|
'Couldn\'t publish a source snapshot.',
|
|
15
20
|
'Check that Docker is running, the target npm registry is reachable, and the current directory is a NocoBase source repo.',
|
|
@@ -20,6 +25,8 @@ export default class SourcePublish extends Command {
|
|
|
20
25
|
static description = 'Publish the current NocoBase source repo as a snapshot version to an npm registry for install testing.';
|
|
21
26
|
static examples = [
|
|
22
27
|
'<%= config.bin %> <%= command.id %> --snapshot',
|
|
28
|
+
'<%= config.bin %> <%= command.id %> --snapshot --no-build',
|
|
29
|
+
'<%= config.bin %> <%= command.id %> --snapshot --build-dts',
|
|
23
30
|
'<%= config.bin %> <%= command.id %> --snapshot --cwd /path/to/nocobase/source',
|
|
24
31
|
'<%= config.bin %> <%= command.id %> --snapshot --npm-registry=http://127.0.0.1:4873',
|
|
25
32
|
'<%= config.bin %> <%= command.id %> --snapshot --json',
|
|
@@ -38,6 +45,14 @@ export default class SourcePublish extends Command {
|
|
|
38
45
|
description: 'Source repository path. Defaults to the nearest detected NocoBase source root from the current working directory',
|
|
39
46
|
required: false,
|
|
40
47
|
}),
|
|
48
|
+
'no-build': Flags.boolean({
|
|
49
|
+
description: 'Skip building the source repo before snapshot versioning and publish',
|
|
50
|
+
default: false,
|
|
51
|
+
}),
|
|
52
|
+
'build-dts': Flags.boolean({
|
|
53
|
+
description: 'Generate TypeScript declaration files during the source build',
|
|
54
|
+
default: false,
|
|
55
|
+
}),
|
|
41
56
|
json: Flags.boolean({
|
|
42
57
|
description: 'Print the publish result as JSON',
|
|
43
58
|
default: false,
|
|
@@ -59,6 +74,8 @@ export default class SourcePublish extends Command {
|
|
|
59
74
|
const result = await publishSourceSnapshot({
|
|
60
75
|
cwd: flags.cwd,
|
|
61
76
|
npmRegistry: flags['npm-registry'],
|
|
77
|
+
build: !flags['no-build'],
|
|
78
|
+
buildDts: flags['build-dts'],
|
|
62
79
|
verbose: flags.verbose,
|
|
63
80
|
});
|
|
64
81
|
if (flags.json) {
|