@nocobase/cli 2.2.0-beta.9 → 2.2.0-test.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/env-proxy/nginx/snippets/uploads-location.conf +4 -1
- package/dist/commands/app/start.js +21 -1
- 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 +129 -6
- package/dist/commands/portal/create.js +104 -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/source/dev.js +1 -1
- package/dist/commands/source/download.js +2 -2
- package/dist/lib/auth-store.js +3 -1
- package/dist/lib/cli-config.js +23 -2
- package/dist/lib/env-config.js +3 -0
- package/dist/lib/env-proxy.js +102 -3
- 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/run-npm.js +17 -16
- package/dist/lib/ui.js +28 -1
- package/dist/locale/en-US.json +191 -37
- package/dist/locale/zh-CN.json +191 -37
- package/package.json +5 -3
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
add_header Cache-Control "public" always;
|
|
2
|
+
add_header Content-Security-Policy "sandbox" always;
|
|
2
3
|
add_header X-Content-Type-Options "nosniff" always;
|
|
3
4
|
|
|
4
5
|
access_log off;
|
|
5
6
|
autoindex off;
|
|
6
7
|
|
|
7
8
|
# Force potentially renderable uploaded files to download.
|
|
8
|
-
location ~* \.(?:htm|html|svg|svgz|xhtml)$ {
|
|
9
|
+
location ~* \.(?:htm|html|pdf|svg|svgz|xht|xhtml|xml|xsl|xslt)$ {
|
|
9
10
|
add_header Cache-Control "public" always;
|
|
11
|
+
add_header Content-Security-Policy "sandbox" always;
|
|
10
12
|
add_header X-Content-Type-Options "nosniff" always;
|
|
11
13
|
add_header Content-Disposition "attachment" always;
|
|
12
14
|
}
|
|
@@ -16,6 +18,7 @@ location ~* \.md$ {
|
|
|
16
18
|
default_type text/markdown;
|
|
17
19
|
|
|
18
20
|
add_header Cache-Control "public" always;
|
|
21
|
+
add_header Content-Security-Policy "sandbox" always;
|
|
19
22
|
add_header X-Content-Type-Options "nosniff" always;
|
|
20
23
|
add_header Content-Disposition "inline" always;
|
|
21
24
|
}
|
|
@@ -14,6 +14,7 @@ import { AppHealthCheckError, formatAppUrl, isAppReady, resolveManagedAppApiBase
|
|
|
14
14
|
import { ensureBuiltinDbReady, ensureLocalPostinstall, ensureSavedLocalSource, recreateSavedDockerApp, } from '../../lib/app-managed-resources.js';
|
|
15
15
|
import { clearEnvRootSetup, getEnv, upsertEnv } from '../../lib/auth-store.js';
|
|
16
16
|
import { buildInitAppEnvVarsFromConfig, isPreparedSetupState } from '../../lib/managed-init-env.js';
|
|
17
|
+
import { prepareInitialPortalTemplate } from '../../lib/portal-template.js';
|
|
17
18
|
import { resolveAppUrlFromApiBaseUrl } from '../env/shared.js';
|
|
18
19
|
import { readManagedRuntimeEnvValues } from '../../lib/managed-env-file.js';
|
|
19
20
|
import { run } from '../../lib/run-npm.js';
|
|
@@ -181,6 +182,19 @@ async function finalizePreparedEnv(envName) {
|
|
|
181
182
|
setupState: 'installed',
|
|
182
183
|
});
|
|
183
184
|
}
|
|
185
|
+
async function prepareInitialPortalForRuntime(runtime, verbose) {
|
|
186
|
+
await prepareInitialPortalTemplate({
|
|
187
|
+
developmentMode: runtime.env.config?.developmentMode,
|
|
188
|
+
portalName: runtime.env.config?.portalName,
|
|
189
|
+
portalTemplate: runtime.env.config?.portalTemplate,
|
|
190
|
+
npmRegistry: runtime.env.config?.npmRegistry,
|
|
191
|
+
storagePath: runtime.env.storagePath || runtime.env.config?.storagePath,
|
|
192
|
+
verbose,
|
|
193
|
+
onStartTask: startTask,
|
|
194
|
+
onSucceedTask: succeedTask,
|
|
195
|
+
onFailTask: failTask,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
184
198
|
export default class AppStart extends Command {
|
|
185
199
|
static hidden = false;
|
|
186
200
|
static description = 'Start NocoBase for the selected env. When applicable, the CLI synchronizes licensed commercial plugins first, then prepares and starts the app or recreates the saved Docker container.';
|
|
@@ -249,7 +263,7 @@ export default class AppStart extends Command {
|
|
|
249
263
|
}
|
|
250
264
|
const daemonFlagWasProvided = argvHasToken(this.argv, ['--daemon', '--no-daemon']);
|
|
251
265
|
const runtime = await resolveManagedAppRuntime(requestedEnv);
|
|
252
|
-
const preparedInitEnvVars = buildInitAppEnvVarsFromConfig(runtime?.env.config);
|
|
266
|
+
const preparedInitEnvVars = buildInitAppEnvVarsFromConfig(runtime?.env.config, { includePortal: false });
|
|
253
267
|
const isPreparedEnv = isPreparedSetupState(runtime?.env.config?.setupState);
|
|
254
268
|
const hookCommand = resolveHookCommand(flags['hook-command']);
|
|
255
269
|
const hookPhase = resolveAppStartHookPhase({
|
|
@@ -322,6 +336,9 @@ export default class AppStart extends Command {
|
|
|
322
336
|
command: hookCommand,
|
|
323
337
|
});
|
|
324
338
|
}
|
|
339
|
+
if (isPreparedEnv) {
|
|
340
|
+
await prepareInitialPortalForRuntime(runtime, flags.verbose);
|
|
341
|
+
}
|
|
325
342
|
await recreateSavedDockerApp(runtime, {
|
|
326
343
|
initEnvVars: isPreparedEnv ? preparedInitEnvVars : undefined,
|
|
327
344
|
verbose: flags.verbose,
|
|
@@ -470,6 +487,9 @@ export default class AppStart extends Command {
|
|
|
470
487
|
command: hookCommand,
|
|
471
488
|
});
|
|
472
489
|
}
|
|
490
|
+
if (isPreparedEnv) {
|
|
491
|
+
await prepareInitialPortalForRuntime(runtime, flags.verbose);
|
|
492
|
+
}
|
|
473
493
|
await runLocalNocoBaseCommand(runtime, npmArgs, {
|
|
474
494
|
env: startEnv,
|
|
475
495
|
stdio: commandStdio,
|
|
@@ -14,6 +14,7 @@ export default class ConfigSet extends Command {
|
|
|
14
14
|
static description = 'Set a CLI configuration value for a supported configuration key.';
|
|
15
15
|
static examples = [
|
|
16
16
|
'<%= config.bin %> <%= command.id %> locale zh-CN',
|
|
17
|
+
'<%= config.bin %> <%= command.id %> default-portal-template git@github.com:nocobase/admin-starter.git',
|
|
17
18
|
'<%= config.bin %> <%= command.id %> update.policy prompt',
|
|
18
19
|
'<%= config.bin %> <%= command.id %> docker.network nocobase',
|
|
19
20
|
'<%= config.bin %> <%= command.id %> docker.container-prefix nb',
|
|
@@ -170,8 +170,13 @@ export default class EnvInfo extends Command {
|
|
|
170
170
|
'auth.accessToken': authGroup.accessToken,
|
|
171
171
|
'auth.refreshToken': authGroup.refreshToken,
|
|
172
172
|
};
|
|
173
|
+
const envGroup = {
|
|
174
|
+
name: runtime.envName,
|
|
175
|
+
kind: runtime.kind,
|
|
176
|
+
};
|
|
173
177
|
const output = {
|
|
174
178
|
ok: true,
|
|
179
|
+
name: runtime.envName,
|
|
175
180
|
env: runtime.envName,
|
|
176
181
|
kind: runtime.kind,
|
|
177
182
|
app: serializeGroup(appGroup),
|
|
@@ -197,6 +202,11 @@ export default class EnvInfo extends Command {
|
|
|
197
202
|
this.log(JSON.stringify(output, null, 2));
|
|
198
203
|
return;
|
|
199
204
|
}
|
|
200
|
-
this.log([
|
|
205
|
+
this.log([
|
|
206
|
+
createGroupTable('Env', envGroup),
|
|
207
|
+
createGroupTable('App', appGroup),
|
|
208
|
+
createGroupTable('DB', dbGroup),
|
|
209
|
+
createGroupTable('API', apiGroup),
|
|
210
|
+
].join('\n\n'));
|
|
201
211
|
}
|
|
202
212
|
}
|
package/dist/commands/init.js
CHANGED
|
@@ -16,7 +16,8 @@ import { getEnv, upsertEnv } from "../lib/auth-store.js";
|
|
|
16
16
|
import { runPromptCatalog, } from "../lib/prompt-catalog.js";
|
|
17
17
|
import { applyCliLocale, localeText, translateCli } from "../lib/cli-locale.js";
|
|
18
18
|
import { resolveConfiguredEnvPath, resolveDefaultConfigScope, resolveEnvRelativePath } from '../lib/cli-home.js';
|
|
19
|
-
import { resolveDefaultApiHost, resolveDefaultUiHost } from '../lib/cli-config.js';
|
|
19
|
+
import { getCliConfigValue, resolveDefaultApiHost, resolveDefaultUiHost } from '../lib/cli-config.js';
|
|
20
|
+
import { resolveOfficialDockerRegistry } from '../lib/docker-image.js';
|
|
20
21
|
import { areConfiguredPathsEquivalent, deriveConfiguredSourcePath, deriveConfiguredStoragePath, inferConfiguredAppPathFromLegacyConfig, } from '../lib/env-paths.js';
|
|
21
22
|
import { formatMissingManagedAppEnvMessage } from '../lib/app-runtime.js';
|
|
22
23
|
import { runPromptCatalogWebUI } from "../lib/prompt-web-ui.js";
|
|
@@ -31,8 +32,12 @@ import EnvAdd from "./env/add.js";
|
|
|
31
32
|
import Install, { defaultDbPortForDialect } from "./install.js";
|
|
32
33
|
const DEFAULT_INIT_API_BASE_URL = 'http://localhost:13000/api';
|
|
33
34
|
const DEFAULT_INIT_APP_NAME = 'local';
|
|
35
|
+
const DEFAULT_INIT_DEVELOPMENT_MODE = 'no-code';
|
|
36
|
+
const DEFAULT_INIT_PORTAL_NAME = 'admin';
|
|
37
|
+
const DEFAULT_INIT_PORTAL_TEMPLATE = 'git@github.com:nocobase/admin-starter.git';
|
|
34
38
|
const DOWNLOAD_OUTPUT_DIR_PROMPT = Download.prompts.outputDir;
|
|
35
39
|
const INIT_SETUP_MODES = ['install-new', 'manage-local', 'connect-remote'];
|
|
40
|
+
const INIT_DEVELOPMENT_MODES = ['no-code', 'vibe-coding'];
|
|
36
41
|
const INIT_ENV_ADD_FLAG_NAMES = [
|
|
37
42
|
'locale',
|
|
38
43
|
'default-api-base-url',
|
|
@@ -79,6 +84,9 @@ function isInstallNewSetupMode(values) {
|
|
|
79
84
|
function isInstallLikeSetupMode(values) {
|
|
80
85
|
return !isRemoteSetupMode(values);
|
|
81
86
|
}
|
|
87
|
+
function isVibeCodingMode(values) {
|
|
88
|
+
return String(values.developmentMode ?? DEFAULT_INIT_DEVELOPMENT_MODE).trim() === 'vibe-coding';
|
|
89
|
+
}
|
|
82
90
|
function remoteConnectionOnly(def) {
|
|
83
91
|
return withExtraHidden(def, (values) => !isRemoteSetupMode(values));
|
|
84
92
|
}
|
|
@@ -386,6 +394,43 @@ Prompt modes:
|
|
|
386
394
|
devDependencies: installLikeDownloadExecutionOnly(Download.prompts.devDependencies),
|
|
387
395
|
build: installLikeDownloadExecutionOnly(Download.prompts.build),
|
|
388
396
|
buildDts: installLikeDownloadExecutionOnly(Download.prompts.buildDts),
|
|
397
|
+
developmentMode: installNewOnly({
|
|
398
|
+
type: 'select',
|
|
399
|
+
variant: 'radio',
|
|
400
|
+
message: initText('prompts.developmentMode.message'),
|
|
401
|
+
options: [
|
|
402
|
+
{
|
|
403
|
+
value: 'no-code',
|
|
404
|
+
label: initText('prompts.developmentMode.noCodeLabel'),
|
|
405
|
+
hint: initText('prompts.developmentMode.noCodeHint'),
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
value: 'vibe-coding',
|
|
409
|
+
label: initText('prompts.developmentMode.vibeCodingLabel'),
|
|
410
|
+
hint: initText('prompts.developmentMode.vibeCodingHint'),
|
|
411
|
+
},
|
|
412
|
+
],
|
|
413
|
+
initialValue: DEFAULT_INIT_DEVELOPMENT_MODE,
|
|
414
|
+
yesInitialValue: DEFAULT_INIT_DEVELOPMENT_MODE,
|
|
415
|
+
required: true,
|
|
416
|
+
}),
|
|
417
|
+
portalName: installNewOnly({
|
|
418
|
+
type: 'text',
|
|
419
|
+
message: initText('prompts.portalName.message'),
|
|
420
|
+
placeholder: DEFAULT_INIT_PORTAL_NAME,
|
|
421
|
+
initialValue: DEFAULT_INIT_PORTAL_NAME,
|
|
422
|
+
yesInitialValue: DEFAULT_INIT_PORTAL_NAME,
|
|
423
|
+
hidden: (values) => !isVibeCodingMode(values),
|
|
424
|
+
required: true,
|
|
425
|
+
}),
|
|
426
|
+
portalTemplate: installNewOnly({
|
|
427
|
+
type: 'text',
|
|
428
|
+
message: initText('prompts.portalTemplate.message'),
|
|
429
|
+
placeholder: DEFAULT_INIT_PORTAL_TEMPLATE,
|
|
430
|
+
yesInitialValue: DEFAULT_INIT_PORTAL_TEMPLATE,
|
|
431
|
+
hidden: (values) => !isVibeCodingMode(values),
|
|
432
|
+
required: true,
|
|
433
|
+
}),
|
|
389
434
|
dbDialect: installLikeOnly(Install.dbPrompts.dbDialect),
|
|
390
435
|
builtinDb: installLikeOnly(Install.dbPrompts.builtinDb),
|
|
391
436
|
builtinDbImage: installLikeOnly(Install.dbPrompts.builtinDbImage),
|
|
@@ -411,6 +456,13 @@ Prompt modes:
|
|
|
411
456
|
...Init.prompts,
|
|
412
457
|
installApiBaseUrl: createInstallConnectionApiBaseUrlPrompt(options.defaultApiHost),
|
|
413
458
|
};
|
|
459
|
+
const defaultPortalTemplate = String(options.defaultPortalTemplate ?? '').trim();
|
|
460
|
+
if (defaultPortalTemplate) {
|
|
461
|
+
prompts.portalTemplate = {
|
|
462
|
+
...prompts.portalTemplate,
|
|
463
|
+
yesInitialValue: defaultPortalTemplate,
|
|
464
|
+
};
|
|
465
|
+
}
|
|
414
466
|
if (flags['skip-auth']) {
|
|
415
467
|
const accessTokenPrompt = {
|
|
416
468
|
...EnvAdd.prompts.accessToken,
|
|
@@ -460,6 +512,16 @@ Prompt modes:
|
|
|
460
512
|
description: 'Setup mode: install a new app, manage a local app by reusing its database, or connect a remote app',
|
|
461
513
|
options: [...INIT_SETUP_MODES],
|
|
462
514
|
}),
|
|
515
|
+
'development-mode': Flags.string({
|
|
516
|
+
description: 'Initial development mode: no-code or vibe-coding',
|
|
517
|
+
options: [...INIT_DEVELOPMENT_MODES],
|
|
518
|
+
}),
|
|
519
|
+
'portal-name': Flags.string({
|
|
520
|
+
description: 'Initial portal name when --development-mode vibe-coding is used',
|
|
521
|
+
}),
|
|
522
|
+
'portal-template': Flags.string({
|
|
523
|
+
description: 'Initial portal template Git URL or local path when --development-mode vibe-coding is used',
|
|
524
|
+
}),
|
|
463
525
|
ui: Flags.boolean({
|
|
464
526
|
description: 'Open the guided setup flow in a local browser form (not valid with --yes)',
|
|
465
527
|
default: false,
|
|
@@ -577,7 +639,8 @@ Prompt modes:
|
|
|
577
639
|
const dynamicInitialValues = await Init.buildDynamicInitialValuesForInstall(normalizedFlags, presetValues);
|
|
578
640
|
const defaultUiHost = await resolveDefaultUiHost();
|
|
579
641
|
const defaultApiHost = await resolveDefaultApiHost();
|
|
580
|
-
const
|
|
642
|
+
const defaultPortalTemplate = String(dynamicInitialValues.portalTemplate ?? '').trim();
|
|
643
|
+
const promptCatalog = this.buildPromptCatalog(normalizedFlags, { defaultApiHost, defaultPortalTemplate });
|
|
581
644
|
if (useBrowserUi) {
|
|
582
645
|
presetValues = await runPromptCatalogWebUI({
|
|
583
646
|
stages: Init.buildWebUiStages(promptCatalog),
|
|
@@ -606,6 +669,7 @@ Prompt modes:
|
|
|
606
669
|
? { setupMode: normalizeInitSetupMode(presetValues.hasNocobase) }
|
|
607
670
|
: {}),
|
|
608
671
|
},
|
|
672
|
+
yesInitialValues: pickKeys(dynamicInitialValues, ['portalTemplate']),
|
|
609
673
|
values: presetValues,
|
|
610
674
|
yes: normalizedFlags.yes || useBrowserUi || !interactive,
|
|
611
675
|
hooks: {
|
|
@@ -678,7 +742,9 @@ Prompt modes:
|
|
|
678
742
|
}
|
|
679
743
|
static async buildDynamicInitialValuesForInstall(flags, presetValues) {
|
|
680
744
|
const out = {};
|
|
681
|
-
|
|
745
|
+
const shouldResolveAppInitialValues = !Object.prototype.hasOwnProperty.call(presetValues, 'appPort') ||
|
|
746
|
+
!Object.prototype.hasOwnProperty.call(presetValues, 'portalTemplate');
|
|
747
|
+
if (shouldResolveAppInitialValues) {
|
|
682
748
|
const appInitialValues = await Install.buildAppPromptInitialValues({
|
|
683
749
|
envName: String(presetValues.appName ?? '').trim(),
|
|
684
750
|
flags: {
|
|
@@ -686,17 +752,31 @@ Prompt modes:
|
|
|
686
752
|
'app-path': flags['app-path'] ?? '',
|
|
687
753
|
'app-root-path': flags['app-root-path'] ?? '',
|
|
688
754
|
'storage-path': flags['storage-path'] ?? '',
|
|
755
|
+
'portal-template': flags['portal-template'] ??
|
|
756
|
+
(Object.prototype.hasOwnProperty.call(presetValues, 'portalTemplate')
|
|
757
|
+
? String(presetValues.portalTemplate ?? '')
|
|
758
|
+
: undefined),
|
|
689
759
|
},
|
|
690
760
|
warnOnPortFallback: false,
|
|
691
761
|
});
|
|
692
|
-
if (appInitialValues.appPort !== undefined) {
|
|
762
|
+
if (appInitialValues.appPort !== undefined && !Object.prototype.hasOwnProperty.call(presetValues, 'appPort')) {
|
|
693
763
|
out.appPort = appInitialValues.appPort;
|
|
694
764
|
}
|
|
765
|
+
if (appInitialValues.portalTemplate !== undefined &&
|
|
766
|
+
!Object.prototype.hasOwnProperty.call(presetValues, 'portalTemplate')) {
|
|
767
|
+
out.portalTemplate = appInitialValues.portalTemplate;
|
|
768
|
+
}
|
|
695
769
|
}
|
|
696
770
|
const downloadSeed = { ...presetValues };
|
|
697
771
|
if (flags.yes && !Object.prototype.hasOwnProperty.call(downloadSeed, 'source')) {
|
|
698
772
|
downloadSeed.source = 'docker';
|
|
699
773
|
}
|
|
774
|
+
const builtinDbImageRegistry = String(presetValues.dockerRegistry ?? '').trim() ||
|
|
775
|
+
resolveOfficialDockerRegistry(await getCliConfigValue('nb-image-registry'));
|
|
776
|
+
if (!Object.prototype.hasOwnProperty.call(presetValues, 'dockerRegistry')) {
|
|
777
|
+
out.dockerRegistry = builtinDbImageRegistry;
|
|
778
|
+
}
|
|
779
|
+
out.builtinDbImageRegistry = builtinDbImageRegistry;
|
|
700
780
|
const dbInitial = await Install.buildDbPromptInitialValues({
|
|
701
781
|
flags,
|
|
702
782
|
downloadResults: downloadSeed,
|
|
@@ -704,6 +784,9 @@ Prompt modes:
|
|
|
704
784
|
warnOnPortFallback: false,
|
|
705
785
|
});
|
|
706
786
|
for (const [key, value] of Object.entries(dbInitial)) {
|
|
787
|
+
if (key === 'builtinDbImage') {
|
|
788
|
+
continue;
|
|
789
|
+
}
|
|
707
790
|
if (!Object.prototype.hasOwnProperty.call(presetValues, key)) {
|
|
708
791
|
out[key] = value;
|
|
709
792
|
}
|
|
@@ -761,6 +844,13 @@ Prompt modes:
|
|
|
761
844
|
buildDts: c.buildDts,
|
|
762
845
|
},
|
|
763
846
|
},
|
|
847
|
+
{
|
|
848
|
+
sectionTitle: initText('webUi.developmentMode.title'),
|
|
849
|
+
sectionDescription: initText('webUi.developmentMode.description'),
|
|
850
|
+
catalog: {
|
|
851
|
+
developmentMode: c.developmentMode,
|
|
852
|
+
},
|
|
853
|
+
},
|
|
764
854
|
{
|
|
765
855
|
sectionTitle: initText('webUi.configureDatabase.title'),
|
|
766
856
|
sectionDescription: initText('webUi.configureDatabase.description'),
|
|
@@ -861,6 +951,15 @@ Prompt modes:
|
|
|
861
951
|
if (flags['app-public-path'] !== undefined && String(flags['app-public-path']).trim() !== '') {
|
|
862
952
|
preset.appPublicPath = String(flags['app-public-path']).trim();
|
|
863
953
|
}
|
|
954
|
+
if (flags['development-mode'] !== undefined && String(flags['development-mode']).trim() !== '') {
|
|
955
|
+
preset.developmentMode = String(flags['development-mode']).trim();
|
|
956
|
+
}
|
|
957
|
+
if (flags['portal-name'] !== undefined && String(flags['portal-name']).trim() !== '') {
|
|
958
|
+
preset.portalName = String(flags['portal-name']).trim();
|
|
959
|
+
}
|
|
960
|
+
if (flags['portal-template'] !== undefined && String(flags['portal-template']).trim() !== '') {
|
|
961
|
+
preset.portalTemplate = String(flags['portal-template']).trim();
|
|
962
|
+
}
|
|
864
963
|
if (flags['root-username'] !== undefined) {
|
|
865
964
|
preset.rootUsername = String(flags['root-username'] ?? '').trim();
|
|
866
965
|
}
|
|
@@ -998,6 +1097,9 @@ Prompt modes:
|
|
|
998
1097
|
const existingEnv = await getEnv(envName, { scope: resolveDefaultConfigScope() });
|
|
999
1098
|
const appPort = String(results.appPort ?? '').trim();
|
|
1000
1099
|
const appPublicPath = String(results.appPublicPath ?? '').trim();
|
|
1100
|
+
const developmentMode = String(results.developmentMode ?? '').trim();
|
|
1101
|
+
const portalName = String(results.portalName ?? '').trim();
|
|
1102
|
+
const portalTemplate = String(results.portalTemplate ?? '').trim();
|
|
1001
1103
|
const source = String(results.source ?? '').trim();
|
|
1002
1104
|
const version = resolveInitDownloadVersion(results);
|
|
1003
1105
|
const dockerRegistry = String(results.dockerRegistry ?? '').trim();
|
|
@@ -1070,6 +1172,9 @@ Prompt modes:
|
|
|
1070
1172
|
...(storagePath && !areConfiguredPathsEquivalent(storagePath, derivedStoragePath) ? { storagePath } : {}),
|
|
1071
1173
|
...(appPort ? { appPort } : {}),
|
|
1072
1174
|
...(appPublicPath ? { appPublicPath } : {}),
|
|
1175
|
+
...(developmentMode && developmentMode !== DEFAULT_INIT_DEVELOPMENT_MODE ? { developmentMode } : {}),
|
|
1176
|
+
...(portalName ? { portalName } : {}),
|
|
1177
|
+
...(portalTemplate ? { portalTemplate } : {}),
|
|
1073
1178
|
...(appKey ? { appKey } : {}),
|
|
1074
1179
|
...(timeZone ? { timezone: timeZone } : {}),
|
|
1075
1180
|
...(!skipDownload && results.devDependencies !== undefined
|
|
@@ -1194,6 +1299,18 @@ Prompt modes:
|
|
|
1194
1299
|
if (appPublicPath) {
|
|
1195
1300
|
argv.push('--app-public-path', appPublicPath);
|
|
1196
1301
|
}
|
|
1302
|
+
const developmentMode = String(results.developmentMode ?? '').trim();
|
|
1303
|
+
if (developmentMode && developmentMode !== DEFAULT_INIT_DEVELOPMENT_MODE) {
|
|
1304
|
+
argv.push('--development-mode', developmentMode);
|
|
1305
|
+
}
|
|
1306
|
+
const portalName = String(results.portalName ?? '').trim();
|
|
1307
|
+
if (portalName) {
|
|
1308
|
+
argv.push('--portal-name', portalName);
|
|
1309
|
+
}
|
|
1310
|
+
const portalTemplate = String(results.portalTemplate ?? '').trim();
|
|
1311
|
+
if (portalTemplate) {
|
|
1312
|
+
argv.push('--portal-template', portalTemplate);
|
|
1313
|
+
}
|
|
1197
1314
|
if (flags.force) {
|
|
1198
1315
|
argv.push('--force');
|
|
1199
1316
|
}
|
|
@@ -1391,6 +1508,16 @@ Prompt modes:
|
|
|
1391
1508
|
delete normalized.rootPassword;
|
|
1392
1509
|
delete normalized.rootNickname;
|
|
1393
1510
|
}
|
|
1511
|
+
const developmentMode = normalizeConnectionString(normalized.developmentMode) || DEFAULT_INIT_DEVELOPMENT_MODE;
|
|
1512
|
+
normalized.developmentMode = developmentMode;
|
|
1513
|
+
if (developmentMode === 'vibe-coding') {
|
|
1514
|
+
normalized.portalName = normalizeConnectionString(normalized.portalName) || DEFAULT_INIT_PORTAL_NAME;
|
|
1515
|
+
normalized.portalTemplate = normalizeConnectionString(normalized.portalTemplate);
|
|
1516
|
+
}
|
|
1517
|
+
else {
|
|
1518
|
+
delete normalized.portalName;
|
|
1519
|
+
delete normalized.portalTemplate;
|
|
1520
|
+
}
|
|
1394
1521
|
delete normalized.installApiBaseUrl;
|
|
1395
1522
|
delete normalized.installAuthType;
|
|
1396
1523
|
delete normalized.installUsername;
|
package/dist/commands/install.js
CHANGED
|
@@ -29,6 +29,7 @@ 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
35
|
import Download from './download.js';
|
|
@@ -53,6 +54,10 @@ const DEFAULT_INSTALL_ROOT_EMAIL = 'admin@nocobase.com';
|
|
|
53
54
|
const DEFAULT_INSTALL_ROOT_PASSWORD = 'admin123';
|
|
54
55
|
const DEFAULT_INSTALL_ROOT_NICKNAME = 'Super Admin';
|
|
55
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'];
|
|
56
61
|
function toOptionalPromptString(value) {
|
|
57
62
|
const text = String(value ?? '').trim();
|
|
58
63
|
return text || undefined;
|
|
@@ -180,6 +185,13 @@ function defaultBuiltinDbImageForDialect(value, options) {
|
|
|
180
185
|
function defaultDbDatabaseForDialect(value) {
|
|
181
186
|
return String(value ?? '').trim() === 'kingbase' ? 'kingbase' : DEFAULT_INSTALL_DB_DATABASE;
|
|
182
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
|
+
}
|
|
183
195
|
function defaultDbHostForBuiltinDb(values) {
|
|
184
196
|
return values.builtinDb ? DEFAULT_INSTALL_BUILTIN_DB_HOST : DEFAULT_INSTALL_DB_HOST;
|
|
185
197
|
}
|
|
@@ -400,6 +412,16 @@ export default class Install extends Command {
|
|
|
400
412
|
'app-public-path': Flags.string({
|
|
401
413
|
description: 'Public path for the local app, for example / or /console/',
|
|
402
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
|
+
}),
|
|
403
425
|
'root-username': Flags.string({
|
|
404
426
|
description: 'Initial admin username for the installed app',
|
|
405
427
|
required: false,
|
|
@@ -509,6 +531,42 @@ export default class Install extends Command {
|
|
|
509
531
|
yesInitialValue: '/',
|
|
510
532
|
validate: validateAppPublicPath,
|
|
511
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
|
+
},
|
|
512
570
|
};
|
|
513
571
|
}
|
|
514
572
|
static dbPrompts = {
|
|
@@ -536,7 +594,9 @@ export default class Install extends Command {
|
|
|
536
594
|
type: 'text',
|
|
537
595
|
message: installText('prompts.builtinDbImage.message'),
|
|
538
596
|
placeholder: installText('prompts.builtinDbImage.placeholder'),
|
|
539
|
-
initialValue: (values) => defaultBuiltinDbImageForDialect(values.dbDialect
|
|
597
|
+
initialValue: (values) => defaultBuiltinDbImageForDialect(values.dbDialect, {
|
|
598
|
+
registry: String(values.builtinDbImageRegistry ?? '').trim() || undefined,
|
|
599
|
+
}),
|
|
540
600
|
hidden: (values) => !values.builtinDb || !supportsBuiltinDbDialect(values.dbDialect),
|
|
541
601
|
required: true,
|
|
542
602
|
},
|
|
@@ -586,7 +646,7 @@ export default class Install extends Command {
|
|
|
586
646
|
type: 'text',
|
|
587
647
|
message: installText('prompts.dbSchema.message'),
|
|
588
648
|
placeholder: installText('prompts.dbSchema.placeholder'),
|
|
589
|
-
hidden: (values) =>
|
|
649
|
+
hidden: (values) => !supportsDbSchemaPrompt(values.dbDialect),
|
|
590
650
|
},
|
|
591
651
|
dbTablePrefix: {
|
|
592
652
|
type: 'text',
|
|
@@ -766,6 +826,24 @@ export default class Install extends Command {
|
|
|
766
826
|
preset.appPublicPath = v;
|
|
767
827
|
}
|
|
768
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
|
+
}
|
|
769
847
|
if (flags['root-username'] !== undefined) {
|
|
770
848
|
preset.rootUsername = String(flags['root-username'] ?? '').trim();
|
|
771
849
|
}
|
|
@@ -857,6 +935,9 @@ export default class Install extends Command {
|
|
|
857
935
|
'appPort',
|
|
858
936
|
'storagePath',
|
|
859
937
|
'appPublicPath',
|
|
938
|
+
'developmentMode',
|
|
939
|
+
'portalName',
|
|
940
|
+
'portalTemplate',
|
|
860
941
|
]);
|
|
861
942
|
}
|
|
862
943
|
static buildDbPresetValuesFromFlags(flags, argv = process.argv.slice(2)) {
|
|
@@ -1106,6 +1187,9 @@ export default class Install extends Command {
|
|
|
1106
1187
|
const rootPassword = Install.toOptionalPromptString(config.rootPassword);
|
|
1107
1188
|
const rootNickname = Install.toOptionalPromptString(config.rootNickname);
|
|
1108
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);
|
|
1109
1193
|
const auth = config.auth;
|
|
1110
1194
|
const savedAuthType = Install.toOptionalPromptString(config.authType) ?? Install.toOptionalPromptString(auth?.type);
|
|
1111
1195
|
const appPreset = {
|
|
@@ -1115,6 +1199,9 @@ export default class Install extends Command {
|
|
|
1115
1199
|
...(appPort ? { appPort } : {}),
|
|
1116
1200
|
...(storagePath ? { storagePath } : {}),
|
|
1117
1201
|
...(appPublicPath ? { appPublicPath } : {}),
|
|
1202
|
+
...(developmentMode ? { developmentMode } : {}),
|
|
1203
|
+
...(portalName ? { portalName } : {}),
|
|
1204
|
+
...(portalTemplate ? { portalTemplate } : {}),
|
|
1118
1205
|
...(hookScript ? { hookScript } : {}),
|
|
1119
1206
|
};
|
|
1120
1207
|
const downloadPreset = {
|
|
@@ -1257,6 +1344,12 @@ export default class Install extends Command {
|
|
|
1257
1344
|
warn: params.warnOnPortFallback ?? true,
|
|
1258
1345
|
});
|
|
1259
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
|
+
}
|
|
1260
1353
|
return initialValues;
|
|
1261
1354
|
}
|
|
1262
1355
|
static shouldPublishBuiltinDbPortForValues(values) {
|
|
@@ -1424,14 +1517,17 @@ export default class Install extends Command {
|
|
|
1424
1517
|
static buildDockerAppContainerName(envName, containerPrefix) {
|
|
1425
1518
|
return Install.sanitizeDockerResourceName(`${Install.buildBuiltinDbContainerPrefix(containerPrefix)}-${envName}-app`);
|
|
1426
1519
|
}
|
|
1427
|
-
static buildInitAppEnvVars(params) {
|
|
1520
|
+
static buildInitAppEnvVars(params, options = {}) {
|
|
1428
1521
|
return buildInitAppEnvVarsFromConfig({
|
|
1429
1522
|
lang: String(params.appResults.lang ?? ''),
|
|
1430
1523
|
rootUsername: String(params.rootResults.rootUsername ?? ''),
|
|
1431
1524
|
rootEmail: String(params.rootResults.rootEmail ?? ''),
|
|
1432
1525
|
rootPassword: String(params.rootResults.rootPassword ?? ''),
|
|
1433
1526
|
rootNickname: String(params.rootResults.rootNickname ?? ''),
|
|
1434
|
-
|
|
1527
|
+
developmentMode: String(params.appResults.developmentMode ?? ''),
|
|
1528
|
+
portalName: String(params.appResults.portalName ?? ''),
|
|
1529
|
+
portalTemplate: String(params.appResults.portalTemplate ?? ''),
|
|
1530
|
+
}, options);
|
|
1435
1531
|
}
|
|
1436
1532
|
static shouldPublishBuiltinDbPort(source) {
|
|
1437
1533
|
return String(source ?? '').trim() !== 'docker';
|
|
@@ -1786,7 +1882,7 @@ export default class Install extends Command {
|
|
|
1786
1882
|
const initEnvVars = Install.buildInitAppEnvVars({
|
|
1787
1883
|
appResults: params.appResults,
|
|
1788
1884
|
rootResults: params.rootResults,
|
|
1789
|
-
});
|
|
1885
|
+
}, { includePortal: false });
|
|
1790
1886
|
const containerPort = resolveDockerImageContainerPort(imageRef);
|
|
1791
1887
|
const args = [
|
|
1792
1888
|
'run',
|
|
@@ -2063,7 +2159,7 @@ export default class Install extends Command {
|
|
|
2063
2159
|
...Install.buildInitAppEnvVars({
|
|
2064
2160
|
appResults: params.appResults,
|
|
2065
2161
|
rootResults: params.rootResults,
|
|
2066
|
-
}),
|
|
2162
|
+
}, { includePortal: false }),
|
|
2067
2163
|
};
|
|
2068
2164
|
setOptionalEnvVar(env, 'APP_PUBLIC_PATH', Install.toOptionalPromptString(params.appResults.appPublicPath));
|
|
2069
2165
|
setOptionalEnvVar(env, 'DB_SCHEMA', optionalEnvString(params.dbResults.dbSchema));
|
|
@@ -2260,6 +2356,19 @@ export default class Install extends Command {
|
|
|
2260
2356
|
}
|
|
2261
2357
|
return argv;
|
|
2262
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
|
+
}
|
|
2263
2372
|
async runInstallHookIfNeeded(params) {
|
|
2264
2373
|
const appPath = Install.resolveAbsoluteAppPath(params.envName, params.appResults);
|
|
2265
2374
|
const savedHookScript = Install.toOptionalPromptString(params.appResults.hookScript);
|
|
@@ -2303,6 +2412,9 @@ export default class Install extends Command {
|
|
|
2303
2412
|
const appRootPath = Install.toOptionalPromptString(params.appResults.appRootPath);
|
|
2304
2413
|
const storagePath = Install.toOptionalPromptString(params.appResults.storagePath);
|
|
2305
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);
|
|
2306
2418
|
const derivedAppRootPath = appPath ? deriveConfiguredSourcePath(appPath) : undefined;
|
|
2307
2419
|
const derivedStoragePath = appPath ? deriveConfiguredStoragePath(appPath) : undefined;
|
|
2308
2420
|
const appPort = String(params.appResults.appPort ?? DEFAULT_INSTALL_APP_PORT).trim() || DEFAULT_INSTALL_APP_PORT;
|
|
@@ -2337,6 +2449,9 @@ export default class Install extends Command {
|
|
|
2337
2449
|
...(appPublicPath ? { appPublicPath } : {}),
|
|
2338
2450
|
...(envFile ? { envFile } : {}),
|
|
2339
2451
|
lang: params.appResults.lang,
|
|
2452
|
+
developmentMode,
|
|
2453
|
+
portalName,
|
|
2454
|
+
portalTemplate,
|
|
2340
2455
|
appKey: params.appResults.appKey,
|
|
2341
2456
|
timezone: params.appResults.timeZone,
|
|
2342
2457
|
builtinDb: params.dbResults.builtinDb,
|
|
@@ -2388,6 +2503,7 @@ export default class Install extends Command {
|
|
|
2388
2503
|
'app-root-path': parsed['app-root-path'] ?? Install.toOptionalPromptString(appPreset.appRootPath),
|
|
2389
2504
|
'app-port': parsed['app-port'] ?? Install.toOptionalPromptString(appPreset.appPort),
|
|
2390
2505
|
'storage-path': parsed['storage-path'] ?? Install.toOptionalPromptString(appPreset.storagePath),
|
|
2506
|
+
'portal-template': parsed['portal-template'] ?? Install.toOptionalPromptString(appPreset.portalTemplate),
|
|
2391
2507
|
},
|
|
2392
2508
|
}),
|
|
2393
2509
|
values: appPreset,
|
|
@@ -2626,6 +2742,13 @@ export default class Install extends Command {
|
|
|
2626
2742
|
envAddResults,
|
|
2627
2743
|
});
|
|
2628
2744
|
}
|
|
2745
|
+
if (shouldStartApp) {
|
|
2746
|
+
await this.prepareInitialPortalIfNeeded({
|
|
2747
|
+
envName,
|
|
2748
|
+
appResults,
|
|
2749
|
+
verbose: parsed.verbose,
|
|
2750
|
+
});
|
|
2751
|
+
}
|
|
2629
2752
|
if (shouldStartApp) {
|
|
2630
2753
|
this.logStage('Starting NocoBase');
|
|
2631
2754
|
await this.config.runCommand('app:start', this.buildAppStartArgv({ envName, verbose: parsed.verbose }));
|