@nocobase/cli 3.0.0-alpha.5 → 3.0.0-alpha.7
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/env/add.js +6 -0
- package/dist/commands/env/update.js +13 -0
- package/dist/commands/init.js +49 -0
- package/dist/commands/install.js +18 -62
- package/dist/commands/portal/config.js +16 -5
- package/dist/commands/portal/create.js +13 -25
- package/dist/commands/portal/destroy.js +28 -6
- package/dist/commands/portal/list.js +5 -5
- package/dist/commands/portal/pull.js +28 -2
- package/dist/lib/api-client.js +35 -9
- package/dist/lib/app-client-entry-mode.js +28 -0
- package/dist/lib/app-managed-resources.js +2 -0
- package/dist/lib/auth-store.js +52 -1
- package/dist/lib/env-auth.js +2 -36
- package/dist/lib/env-command-config.js +1 -0
- package/dist/lib/env-config.js +10 -2
- package/dist/lib/env-portal-config.js +30 -0
- package/dist/lib/env-proxy.js +1 -1
- package/dist/lib/managed-env-file.js +57 -1
- package/dist/lib/managed-init-env.js +0 -2
- package/dist/lib/portal-config.js +0 -17
- package/dist/lib/portal-configure.js +46 -54
- package/dist/lib/portal-create.js +21 -5
- package/dist/lib/portal-deploy.js +5 -42
- package/dist/lib/portal-destroy.js +27 -9
- package/dist/lib/portal-dev.js +2 -3
- package/dist/lib/portal-info.js +2 -5
- package/dist/lib/portal-list.js +16 -23
- package/dist/lib/portal-path-safety.js +76 -0
- package/dist/lib/portal-source.js +60 -43
- package/dist/lib/prompt-catalog-core.js +2 -2
- package/dist/lib/prompt-catalog-terminal.js +4 -5
- package/dist/lib/prompt-web-ui.js +8 -1
- package/dist/locale/en-US.json +44 -16
- package/dist/locale/zh-CN.json +44 -16
- package/package.json +2 -2
package/dist/commands/env/add.js
CHANGED
|
@@ -11,6 +11,7 @@ import { setCurrentEnv, upsertEnv } from '../../lib/auth-store.js';
|
|
|
11
11
|
import { resolveDefaultConfigScope } from '../../lib/cli-home.js';
|
|
12
12
|
import { ENV_BOOLEAN_CONFIG_FLAG_MAP, ENV_STRING_CONFIG_FLAG_MAP } from '../../lib/env-command-config.js';
|
|
13
13
|
import { buildStoredEnvConfig } from '../../lib/env-config.js';
|
|
14
|
+
import { PUBLIC_APP_CLIENT_ENTRY_MODES } from '../../lib/app-client-entry-mode.js';
|
|
14
15
|
import { runPromptCatalog, } from '../../lib/prompt-catalog.js';
|
|
15
16
|
import { applyCliLocale, CLI_LOCALE_FLAG_DESCRIPTION, CLI_LOCALE_FLAG_OPTIONS, localeText, } from '../../lib/cli-locale.js';
|
|
16
17
|
import { validateApiBaseUrl } from '../../lib/prompt-validators.js';
|
|
@@ -172,6 +173,11 @@ export default class EnvAdd extends Command {
|
|
|
172
173
|
hidden: true,
|
|
173
174
|
description: 'Docker env file saved with this env',
|
|
174
175
|
}),
|
|
176
|
+
'app-client-entry-mode': Flags.string({
|
|
177
|
+
hidden: true,
|
|
178
|
+
description: 'UI entry mode saved with this env',
|
|
179
|
+
options: [...PUBLIC_APP_CLIENT_ENTRY_MODES],
|
|
180
|
+
}),
|
|
175
181
|
'app-port': Flags.string({
|
|
176
182
|
hidden: true,
|
|
177
183
|
description: 'Application HTTP port saved with this env',
|
|
@@ -16,6 +16,8 @@ import { appendDiagnosticLogPath } from '../../lib/cli-entry-error.js';
|
|
|
16
16
|
import { getActiveCommandLogFile } from '../../lib/command-log.js';
|
|
17
17
|
import { ENV_BOOLEAN_CONFIG_FLAG_MAP, ENV_STRING_CONFIG_FLAG_MAP } from '../../lib/env-command-config.js';
|
|
18
18
|
import { buildStoredEnvConfig } from '../../lib/env-config.js';
|
|
19
|
+
import { PUBLIC_APP_CLIENT_ENTRY_MODES } from '../../lib/app-client-entry-mode.js';
|
|
20
|
+
import { upsertManagedEnvFileValues } from '../../lib/managed-env-file.js';
|
|
19
21
|
import { validateApiBaseUrl } from '../../lib/prompt-validators.js';
|
|
20
22
|
import { failTask, printInfo, printVerbose, printWarningBlock, setVerboseMode, startTask, stopTask, succeedTask, } from '../../lib/ui.js';
|
|
21
23
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -35,6 +37,7 @@ const UPDATE_STRING_FLAGS = [
|
|
|
35
37
|
'app-public-path',
|
|
36
38
|
'cdn-base-url',
|
|
37
39
|
'env-file',
|
|
40
|
+
'app-client-entry-mode',
|
|
38
41
|
'app-port',
|
|
39
42
|
'app-key',
|
|
40
43
|
'timezone',
|
|
@@ -68,6 +71,7 @@ const APP_RESTART_FIELDS = new Set([
|
|
|
68
71
|
'storage-path',
|
|
69
72
|
'app-public-path',
|
|
70
73
|
'env-file',
|
|
74
|
+
'app-client-entry-mode',
|
|
71
75
|
'app-port',
|
|
72
76
|
'app-key',
|
|
73
77
|
'timezone',
|
|
@@ -255,6 +259,10 @@ export default class EnvUpdate extends Command {
|
|
|
255
259
|
hidden: true,
|
|
256
260
|
description: 'Saved Docker --env-file path for this env',
|
|
257
261
|
}),
|
|
262
|
+
'app-client-entry-mode': Flags.string({
|
|
263
|
+
description: 'Saved UI entry mode for this env',
|
|
264
|
+
options: [...PUBLIC_APP_CLIENT_ENTRY_MODES],
|
|
265
|
+
}),
|
|
258
266
|
'app-port': Flags.string({
|
|
259
267
|
description: 'Saved application HTTP port for this env',
|
|
260
268
|
}),
|
|
@@ -423,6 +431,11 @@ export default class EnvUpdate extends Command {
|
|
|
423
431
|
startTask(`Saving env config: ${envName}`);
|
|
424
432
|
try {
|
|
425
433
|
await replaceEnvConfig(envName, nextConfig, { scope: resolveDefaultConfigScope() });
|
|
434
|
+
if (providedFields.has('app-client-entry-mode') && nextConfig.appClientEntryMode) {
|
|
435
|
+
await upsertManagedEnvFileValues(envName, nextConfig, {
|
|
436
|
+
APP_CLIENT_ENTRY_MODE: nextConfig.appClientEntryMode,
|
|
437
|
+
});
|
|
438
|
+
}
|
|
426
439
|
succeedTask(`Saved env config for "${envName}".`);
|
|
427
440
|
}
|
|
428
441
|
catch (error) {
|
package/dist/commands/init.js
CHANGED
|
@@ -13,6 +13,7 @@ import { existsSync } from 'node:fs';
|
|
|
13
13
|
import path from 'node:path';
|
|
14
14
|
import { stdin as stdinStream, stdout as stdoutStream } from 'node:process';
|
|
15
15
|
import { getEnv, upsertEnv } from "../lib/auth-store.js";
|
|
16
|
+
import { defaultAppClientEntryModeForDownloadVersion, normalizePublicAppClientEntryMode, PUBLIC_APP_CLIENT_ENTRY_MODES, } from '../lib/app-client-entry-mode.js';
|
|
16
17
|
import { runPromptCatalog, } from "../lib/prompt-catalog.js";
|
|
17
18
|
import { applyCliLocale, localeText, translateCli } from "../lib/cli-locale.js";
|
|
18
19
|
import { resolveConfiguredEnvPath, resolveDefaultConfigScope, resolveEnvRelativePath } from '../lib/cli-home.js';
|
|
@@ -47,6 +48,23 @@ const INIT_ENV_ADD_FLAG_NAMES = [
|
|
|
47
48
|
'skip-auth',
|
|
48
49
|
];
|
|
49
50
|
const initText = (key, values) => localeText(`commands.init.${key}`, values);
|
|
51
|
+
const PUBLIC_APP_CLIENT_ENTRY_MODE_OPTIONS = [
|
|
52
|
+
{
|
|
53
|
+
value: 'modern-only',
|
|
54
|
+
label: initText('prompts.appClientEntryMode.modernOnlyLabel'),
|
|
55
|
+
hint: initText('prompts.appClientEntryMode.modernOnlyHint'),
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
value: 'modern-default',
|
|
59
|
+
label: initText('prompts.appClientEntryMode.modernDefaultLabel'),
|
|
60
|
+
hint: initText('prompts.appClientEntryMode.modernDefaultHint'),
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
value: 'legacy-default',
|
|
64
|
+
label: initText('prompts.appClientEntryMode.legacyDefaultLabel'),
|
|
65
|
+
hint: initText('prompts.appClientEntryMode.legacyDefaultHint'),
|
|
66
|
+
},
|
|
67
|
+
];
|
|
50
68
|
function withExtraHidden(def, extraHidden) {
|
|
51
69
|
if (def.type === 'run') {
|
|
52
70
|
return def;
|
|
@@ -106,6 +124,11 @@ function resolveInitDownloadVersion(results) {
|
|
|
106
124
|
}
|
|
107
125
|
return preset;
|
|
108
126
|
}
|
|
127
|
+
function resolveInitAppClientEntryMode(results, explicitValue) {
|
|
128
|
+
return (normalizePublicAppClientEntryMode(explicitValue) ??
|
|
129
|
+
normalizePublicAppClientEntryMode(results.appClientEntryMode) ??
|
|
130
|
+
defaultAppClientEntryModeForDownloadVersion(resolveInitDownloadVersion(results)));
|
|
131
|
+
}
|
|
109
132
|
function initVersionPromptValue(version) {
|
|
110
133
|
return version === 'latest' || version === 'beta' || version === 'alpha' ? version : 'other';
|
|
111
134
|
}
|
|
@@ -366,6 +389,15 @@ Prompt modes:
|
|
|
366
389
|
source: installLikeOnly(Download.prompts.source),
|
|
367
390
|
version: installLikeOnly(Download.prompts.version),
|
|
368
391
|
otherVersion: installLikeOnly(Download.prompts.otherVersion),
|
|
392
|
+
appClientEntryMode: installLikeOnly({
|
|
393
|
+
type: 'select',
|
|
394
|
+
message: initText('prompts.appClientEntryMode.message'),
|
|
395
|
+
options: [...PUBLIC_APP_CLIENT_ENTRY_MODE_OPTIONS],
|
|
396
|
+
variant: 'radio',
|
|
397
|
+
hidden: (values) => resolveInitDownloadVersion(values) === 'latest',
|
|
398
|
+
initialValue: (values) => defaultAppClientEntryModeForDownloadVersion(values.version),
|
|
399
|
+
required: true,
|
|
400
|
+
}),
|
|
369
401
|
dockerRegistry: installLikeOnly(Download.prompts.dockerRegistry),
|
|
370
402
|
dockerPlatform: installLikeOnly(Download.prompts.dockerPlatform),
|
|
371
403
|
dockerSave: installLikeDownloadExecutionOnly(Download.prompts.dockerSave),
|
|
@@ -490,6 +522,10 @@ Prompt modes:
|
|
|
490
522
|
description: 'Skip installing NocoBase AI coding skills during init',
|
|
491
523
|
default: false,
|
|
492
524
|
}),
|
|
525
|
+
'app-client-entry-mode': Flags.string({
|
|
526
|
+
description: 'UI entry mode for this app env: modern-only, modern-default, or legacy-default',
|
|
527
|
+
options: [...PUBLIC_APP_CLIENT_ENTRY_MODES],
|
|
528
|
+
}),
|
|
493
529
|
'ui-host': Flags.string({
|
|
494
530
|
description: 'Browser-accessible host for the --ui setup page URL (default: 127.0.0.1)',
|
|
495
531
|
}),
|
|
@@ -778,6 +814,7 @@ Prompt modes:
|
|
|
778
814
|
source: c.source,
|
|
779
815
|
version: c.version,
|
|
780
816
|
otherVersion: c.otherVersion,
|
|
817
|
+
appClientEntryMode: c.appClientEntryMode,
|
|
781
818
|
dockerRegistry: c.dockerRegistry,
|
|
782
819
|
dockerPlatform: c.dockerPlatform,
|
|
783
820
|
dockerSave: c.dockerSave,
|
|
@@ -890,6 +927,9 @@ Prompt modes:
|
|
|
890
927
|
if (flags['app-public-path'] !== undefined && String(flags['app-public-path']).trim() !== '') {
|
|
891
928
|
preset.appPublicPath = String(flags['app-public-path']).trim();
|
|
892
929
|
}
|
|
930
|
+
if (flags['app-client-entry-mode'] !== undefined && String(flags['app-client-entry-mode']).trim() !== '') {
|
|
931
|
+
preset.appClientEntryMode = String(flags['app-client-entry-mode']).trim();
|
|
932
|
+
}
|
|
893
933
|
if (flags['root-username'] !== undefined) {
|
|
894
934
|
preset.rootUsername = String(flags['root-username'] ?? '').trim();
|
|
895
935
|
}
|
|
@@ -1027,6 +1067,7 @@ Prompt modes:
|
|
|
1027
1067
|
const existingEnv = await getEnv(envName, { scope: resolveDefaultConfigScope() });
|
|
1028
1068
|
const appPort = String(results.appPort ?? '').trim();
|
|
1029
1069
|
const appPublicPath = String(results.appPublicPath ?? '').trim();
|
|
1070
|
+
const appClientEntryMode = resolveInitAppClientEntryMode(results, flags['app-client-entry-mode']);
|
|
1030
1071
|
const source = String(results.source ?? '').trim();
|
|
1031
1072
|
const version = resolveInitDownloadVersion(results);
|
|
1032
1073
|
const dockerRegistry = String(results.dockerRegistry ?? '').trim();
|
|
@@ -1100,6 +1141,7 @@ Prompt modes:
|
|
|
1100
1141
|
...(storagePath && !areConfiguredPathsEquivalent(storagePath, derivedStoragePath) ? { storagePath } : {}),
|
|
1101
1142
|
...(appPort ? { appPort } : {}),
|
|
1102
1143
|
...(appPublicPath ? { appPublicPath } : {}),
|
|
1144
|
+
...(appClientEntryMode ? { appClientEntryMode } : {}),
|
|
1103
1145
|
...(appKey ? { appKey } : {}),
|
|
1104
1146
|
...(timeZone ? { timezone: timeZone } : {}),
|
|
1105
1147
|
...(!skipDownload && results.devDependencies !== undefined
|
|
@@ -1228,6 +1270,13 @@ Prompt modes:
|
|
|
1228
1270
|
if (appPublicPath) {
|
|
1229
1271
|
argv.push('--app-public-path', appPublicPath);
|
|
1230
1272
|
}
|
|
1273
|
+
const appClientEntryMode = normalizePublicAppClientEntryMode(flags['app-client-entry-mode']) ||
|
|
1274
|
+
(results.setupMode === 'install-new' && results.hasNocobase === undefined
|
|
1275
|
+
? resolveInitAppClientEntryMode(results)
|
|
1276
|
+
: undefined);
|
|
1277
|
+
if (appClientEntryMode) {
|
|
1278
|
+
argv.push('--app-client-entry-mode', appClientEntryMode);
|
|
1279
|
+
}
|
|
1231
1280
|
if (flags.force) {
|
|
1232
1281
|
argv.push('--force');
|
|
1233
1282
|
}
|
package/dist/commands/install.js
CHANGED
|
@@ -25,6 +25,7 @@ import { commandOutput, commandSucceeds, ensureDockerDaemonRunning, run, runNoco
|
|
|
25
25
|
import { printInfo, printStage, printVerbose, printWarning, setVerboseMode } from '../lib/ui.js';
|
|
26
26
|
import { omitKeys, upperFirst } from "../lib/object-utils.js";
|
|
27
27
|
import { clearEnvRootSetup, getEnv, setCurrentEnv, upsertEnv } from '../lib/auth-store.js';
|
|
28
|
+
import { defaultAppClientEntryModeForDownloadVersion, normalizePublicAppClientEntryMode, PUBLIC_APP_CLIENT_ENTRY_MODES, } from '../lib/app-client-entry-mode.js';
|
|
28
29
|
import { buildStoredEnvConfig } from '../lib/env-config.js';
|
|
29
30
|
import { resolveDockerEnvFileArg } from "../lib/docker-env-file.js";
|
|
30
31
|
import { startDockerLogFollower } from '../lib/docker-log-stream.js';
|
|
@@ -54,10 +55,7 @@ const DEFAULT_INSTALL_ROOT_EMAIL = 'admin@nocobase.com';
|
|
|
54
55
|
const DEFAULT_INSTALL_ROOT_PASSWORD = 'admin123';
|
|
55
56
|
const DEFAULT_INSTALL_ROOT_NICKNAME = 'Super Admin';
|
|
56
57
|
const DEFAULT_INSTALL_API_HOST = '127.0.0.1';
|
|
57
|
-
const DEFAULT_INSTALL_PORTAL_TYPE = 'ai';
|
|
58
|
-
const DEFAULT_INSTALL_PORTAL_NAME = 'main';
|
|
59
58
|
const DEFAULT_INSTALL_PORTAL_TEMPLATE = '@nocobase/portal-template-default';
|
|
60
|
-
const INSTALL_PORTAL_TYPES = ['no-code', 'ai'];
|
|
61
59
|
function toOptionalPromptString(value) {
|
|
62
60
|
const text = String(value ?? '').trim();
|
|
63
61
|
return text || undefined;
|
|
@@ -185,9 +183,6 @@ function defaultBuiltinDbImageForDialect(value, options) {
|
|
|
185
183
|
function defaultDbDatabaseForDialect(value) {
|
|
186
184
|
return String(value ?? '').trim() === 'kingbase' ? 'kingbase' : DEFAULT_INSTALL_DB_DATABASE;
|
|
187
185
|
}
|
|
188
|
-
function isAiMode(values) {
|
|
189
|
-
return String(values.portalType ?? DEFAULT_INSTALL_PORTAL_TYPE).trim() === 'ai';
|
|
190
|
-
}
|
|
191
186
|
function supportsDbSchemaPrompt(value) {
|
|
192
187
|
const dialect = String(value ?? '').trim();
|
|
193
188
|
return dialect === 'postgres' || dialect === 'kingbase';
|
|
@@ -412,15 +407,12 @@ export default class Install extends Command {
|
|
|
412
407
|
'app-public-path': Flags.string({
|
|
413
408
|
description: 'Public path for the local app, for example / or /console/',
|
|
414
409
|
}),
|
|
415
|
-
'
|
|
416
|
-
description: '
|
|
417
|
-
options: [...
|
|
418
|
-
}),
|
|
419
|
-
'portal-name': Flags.string({
|
|
420
|
-
description: 'Initial portal name',
|
|
410
|
+
'app-client-entry-mode': Flags.string({
|
|
411
|
+
description: 'UI entry mode for this app env: modern-only, modern-default, or legacy-default',
|
|
412
|
+
options: [...PUBLIC_APP_CLIENT_ENTRY_MODES],
|
|
421
413
|
}),
|
|
422
414
|
'portal-template': Flags.string({
|
|
423
|
-
description: '
|
|
415
|
+
description: 'Template npm package or local path for the default AI Portal "main"',
|
|
424
416
|
}),
|
|
425
417
|
'root-username': Flags.string({
|
|
426
418
|
description: 'Initial admin username for the installed app',
|
|
@@ -531,39 +523,12 @@ export default class Install extends Command {
|
|
|
531
523
|
yesInitialValue: '/',
|
|
532
524
|
validate: validateAppPublicPath,
|
|
533
525
|
},
|
|
534
|
-
portalType: {
|
|
535
|
-
type: 'select',
|
|
536
|
-
message: installText('prompts.portalType.message'),
|
|
537
|
-
options: [
|
|
538
|
-
{
|
|
539
|
-
value: 'no-code',
|
|
540
|
-
label: installText('prompts.portalType.noCodeLabel'),
|
|
541
|
-
hint: installText('prompts.portalType.noCodeHint'),
|
|
542
|
-
},
|
|
543
|
-
{
|
|
544
|
-
value: 'ai',
|
|
545
|
-
label: installText('prompts.portalType.aiLabel'),
|
|
546
|
-
hint: installText('prompts.portalType.aiHint'),
|
|
547
|
-
},
|
|
548
|
-
],
|
|
549
|
-
initialValue: DEFAULT_INSTALL_PORTAL_TYPE,
|
|
550
|
-
yesInitialValue: DEFAULT_INSTALL_PORTAL_TYPE,
|
|
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
|
-
required: true,
|
|
560
|
-
},
|
|
561
526
|
portalTemplate: {
|
|
562
527
|
type: 'text',
|
|
563
528
|
message: installText('prompts.portalTemplate.message'),
|
|
564
529
|
placeholder: DEFAULT_INSTALL_PORTAL_TEMPLATE,
|
|
530
|
+
initialValue: DEFAULT_INSTALL_PORTAL_TEMPLATE,
|
|
565
531
|
yesInitialValue: DEFAULT_INSTALL_PORTAL_TEMPLATE,
|
|
566
|
-
hidden: (values) => !isAiMode(values),
|
|
567
532
|
required: true,
|
|
568
533
|
},
|
|
569
534
|
};
|
|
@@ -825,16 +790,10 @@ export default class Install extends Command {
|
|
|
825
790
|
preset.appPublicPath = v;
|
|
826
791
|
}
|
|
827
792
|
}
|
|
828
|
-
if (flags['
|
|
829
|
-
const v =
|
|
830
|
-
if (v) {
|
|
831
|
-
preset.portalType = v;
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
if (flags['portal-name'] !== undefined) {
|
|
835
|
-
const v = String(flags['portal-name'] ?? '').trim();
|
|
793
|
+
if (flags['app-client-entry-mode'] !== undefined) {
|
|
794
|
+
const v = normalizePublicAppClientEntryMode(flags['app-client-entry-mode']);
|
|
836
795
|
if (v) {
|
|
837
|
-
preset.
|
|
796
|
+
preset.appClientEntryMode = v;
|
|
838
797
|
}
|
|
839
798
|
}
|
|
840
799
|
if (flags['portal-template'] !== undefined) {
|
|
@@ -934,8 +893,7 @@ export default class Install extends Command {
|
|
|
934
893
|
'appPort',
|
|
935
894
|
'storagePath',
|
|
936
895
|
'appPublicPath',
|
|
937
|
-
'
|
|
938
|
-
'portalName',
|
|
896
|
+
'appClientEntryMode',
|
|
939
897
|
'portalTemplate',
|
|
940
898
|
]);
|
|
941
899
|
}
|
|
@@ -1186,8 +1144,6 @@ export default class Install extends Command {
|
|
|
1186
1144
|
const rootPassword = Install.toOptionalPromptString(config.rootPassword);
|
|
1187
1145
|
const rootNickname = Install.toOptionalPromptString(config.rootNickname);
|
|
1188
1146
|
const lang = Install.toOptionalPromptString(config.lang);
|
|
1189
|
-
const portalType = Install.toOptionalPromptString(config.portalType);
|
|
1190
|
-
const portalName = Install.toOptionalPromptString(config.portalName);
|
|
1191
1147
|
const portalTemplate = Install.toOptionalPromptString(config.portalTemplate);
|
|
1192
1148
|
const auth = config.auth;
|
|
1193
1149
|
const savedAuthType = Install.toOptionalPromptString(config.authType) ?? Install.toOptionalPromptString(auth?.type);
|
|
@@ -1198,8 +1154,6 @@ export default class Install extends Command {
|
|
|
1198
1154
|
...(appPort ? { appPort } : {}),
|
|
1199
1155
|
...(storagePath ? { storagePath } : {}),
|
|
1200
1156
|
...(appPublicPath ? { appPublicPath } : {}),
|
|
1201
|
-
...(portalType ? { portalType } : {}),
|
|
1202
|
-
...(portalName ? { portalName } : {}),
|
|
1203
1157
|
...(portalTemplate ? { portalTemplate } : {}),
|
|
1204
1158
|
...(hookScript ? { hookScript } : {}),
|
|
1205
1159
|
};
|
|
@@ -1523,8 +1477,6 @@ export default class Install extends Command {
|
|
|
1523
1477
|
rootEmail: String(params.rootResults.rootEmail ?? ''),
|
|
1524
1478
|
rootPassword: String(params.rootResults.rootPassword ?? ''),
|
|
1525
1479
|
rootNickname: String(params.rootResults.rootNickname ?? ''),
|
|
1526
|
-
portalType: String(params.appResults.portalType ?? ''),
|
|
1527
|
-
portalName: String(params.appResults.portalName ?? ''),
|
|
1528
1480
|
portalTemplate: String(params.appResults.portalTemplate ?? ''),
|
|
1529
1481
|
}, options);
|
|
1530
1482
|
}
|
|
@@ -1874,6 +1826,7 @@ export default class Install extends Command {
|
|
|
1874
1826
|
const extractClientAssets = resolveExtractClientAssetsDefaultEnabled(process.env.NOCOBASE_EXTRACT_CLIENT_ASSETS);
|
|
1875
1827
|
const appKey = Install.resolveManagedAppKey(params.appResults.appKey);
|
|
1876
1828
|
const appPublicPath = Install.toOptionalPromptString(params.appResults.appPublicPath);
|
|
1829
|
+
const appClientEntryMode = Install.toOptionalPromptString(params.appResults.appClientEntryMode);
|
|
1877
1830
|
const timeZone = Install.resolveManagedTimeZone(params.appResults.timeZone);
|
|
1878
1831
|
const containerName = Install.buildDockerAppContainerName(params.envName, params.dockerContainerPrefix ?? params.workspaceName);
|
|
1879
1832
|
const configuredEnvFile = String(params.appResults.envFile ?? '').trim();
|
|
@@ -1901,6 +1854,7 @@ export default class Install extends Command {
|
|
|
1901
1854
|
}
|
|
1902
1855
|
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`);
|
|
1903
1856
|
pushOptionalEnvArg(args, 'APP_PUBLIC_PATH', appPublicPath);
|
|
1857
|
+
pushOptionalEnvArg(args, 'APP_CLIENT_ENTRY_MODE', appClientEntryMode);
|
|
1904
1858
|
pushOptionalEnvArg(args, 'DB_SCHEMA', dbSchema);
|
|
1905
1859
|
pushOptionalEnvArg(args, 'DB_TABLE_PREFIX', dbTablePrefix);
|
|
1906
1860
|
pushOptionalEnvArg(args, 'DB_UNDERSCORED', dbUnderscored);
|
|
@@ -2161,6 +2115,7 @@ export default class Install extends Command {
|
|
|
2161
2115
|
}),
|
|
2162
2116
|
};
|
|
2163
2117
|
setOptionalEnvVar(env, 'APP_PUBLIC_PATH', Install.toOptionalPromptString(params.appResults.appPublicPath));
|
|
2118
|
+
setOptionalEnvVar(env, 'APP_CLIENT_ENTRY_MODE', Install.toOptionalPromptString(params.appResults.appClientEntryMode));
|
|
2164
2119
|
setOptionalEnvVar(env, 'DB_SCHEMA', optionalEnvString(params.dbResults.dbSchema));
|
|
2165
2120
|
setOptionalEnvVar(env, 'DB_TABLE_PREFIX', optionalEnvString(params.dbResults.dbTablePrefix));
|
|
2166
2121
|
setOptionalEnvVar(env, 'DB_UNDERSCORED', optionalEnvBoolean(params.dbResults.dbUnderscored));
|
|
@@ -2402,8 +2357,7 @@ export default class Install extends Command {
|
|
|
2402
2357
|
const appRootPath = Install.toOptionalPromptString(params.appResults.appRootPath);
|
|
2403
2358
|
const storagePath = Install.toOptionalPromptString(params.appResults.storagePath);
|
|
2404
2359
|
const appPublicPath = Install.toOptionalPromptString(params.appResults.appPublicPath);
|
|
2405
|
-
const
|
|
2406
|
-
const portalName = Install.toOptionalPromptString(params.appResults.portalName);
|
|
2360
|
+
const appClientEntryMode = Install.toOptionalPromptString(params.appResults.appClientEntryMode);
|
|
2407
2361
|
const portalTemplate = Install.toOptionalPromptString(params.appResults.portalTemplate);
|
|
2408
2362
|
const derivedAppRootPath = appPath ? deriveConfiguredSourcePath(appPath) : undefined;
|
|
2409
2363
|
const derivedStoragePath = appPath ? deriveConfiguredStoragePath(appPath) : undefined;
|
|
@@ -2437,10 +2391,9 @@ export default class Install extends Command {
|
|
|
2437
2391
|
appPort,
|
|
2438
2392
|
...(storagePath && !areConfiguredPathsEquivalent(storagePath, derivedStoragePath) ? { storagePath } : {}),
|
|
2439
2393
|
...(appPublicPath ? { appPublicPath } : {}),
|
|
2394
|
+
...(appClientEntryMode ? { appClientEntryMode } : {}),
|
|
2440
2395
|
...(envFile ? { envFile } : {}),
|
|
2441
2396
|
lang: params.appResults.lang,
|
|
2442
|
-
portalType,
|
|
2443
|
-
portalName,
|
|
2444
2397
|
portalTemplate,
|
|
2445
2398
|
appKey: params.appResults.appKey,
|
|
2446
2399
|
timezone: params.appResults.timeZone,
|
|
@@ -2512,6 +2465,9 @@ export default class Install extends Command {
|
|
|
2512
2465
|
};
|
|
2513
2466
|
downloadOpts.yes = yes;
|
|
2514
2467
|
const downloadResults = await runPromptCatalog(Download.prompts, downloadOpts);
|
|
2468
|
+
appResults.appClientEntryMode =
|
|
2469
|
+
normalizePublicAppClientEntryMode(appResults.appClientEntryMode) ??
|
|
2470
|
+
defaultAppClientEntryModeForDownloadVersion(downloadResultsValue(downloadResults, 'version'));
|
|
2515
2471
|
if (parsed['skip-download']) {
|
|
2516
2472
|
delete downloadResults.outputDir;
|
|
2517
2473
|
delete downloadResults.replace;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
9
|
import { Args, Command, Flags } from '@oclif/core';
|
|
10
|
-
import { getCurrentEnvName, getEnv } from '../../lib/auth-store.js';
|
|
10
|
+
import { getCurrentEnvName, getEnv, setEnvPortalPath } from '../../lib/auth-store.js';
|
|
11
11
|
import { resolveDefaultConfigScope } from '../../lib/cli-home.js';
|
|
12
12
|
import { translateCli } from '../../lib/cli-locale.js';
|
|
13
13
|
import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
|
|
@@ -17,6 +17,7 @@ const portalConfigureText = (key, values, fallback) => translateCli(`commands.po
|
|
|
17
17
|
export default class PortalConfig extends Command {
|
|
18
18
|
static summary = 'Update portal source configuration';
|
|
19
19
|
static examples = [
|
|
20
|
+
'<%= config.bin %> <%= command.id %> customer --path ./portals/customer',
|
|
20
21
|
'<%= config.bin %> <%= command.id %> customer --source-storage nocobase',
|
|
21
22
|
'<%= config.bin %> <%= command.id %> customer --source-storage git --git-repo git@github.com:nocobase/customer-portal.git',
|
|
22
23
|
'<%= config.bin %> <%= command.id %> customer --git-branch main --git-path portals/customer',
|
|
@@ -41,6 +42,9 @@ export default class PortalConfig extends Command {
|
|
|
41
42
|
description: 'Where portal source code is managed',
|
|
42
43
|
options: ['nocobase', 'git'],
|
|
43
44
|
}),
|
|
45
|
+
path: Flags.string({
|
|
46
|
+
description: 'Portal development workspace directory',
|
|
47
|
+
}),
|
|
44
48
|
'git-repo': Flags.string({
|
|
45
49
|
description: 'Git repository URL used when --source-storage=git',
|
|
46
50
|
}),
|
|
@@ -79,10 +83,17 @@ export default class PortalConfig extends Command {
|
|
|
79
83
|
gitRepo: flags['git-repo'],
|
|
80
84
|
gitBranch: flags['git-branch'],
|
|
81
85
|
gitPath: flags['git-path'],
|
|
86
|
+
sourcePath: flags.path,
|
|
82
87
|
});
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
88
|
+
if (flags.path) {
|
|
89
|
+
await setEnvPortalPath(envName, result.portal, result.portalDir, { scope });
|
|
90
|
+
}
|
|
91
|
+
printSuccess(portalConfigureText('messages.updated', { portal: result.portal }, `Portal "${result.portal}" configuration updated.`));
|
|
92
|
+
if (result.pathUpdated) {
|
|
93
|
+
printInfo(portalConfigureText('messages.pathUpdated', { portalDir: result.portalDir }, `Development path: ${result.portalDir}`));
|
|
94
|
+
}
|
|
95
|
+
if (result.config) {
|
|
96
|
+
printInfo(portalConfigureText('messages.remoteSynced', undefined, 'Remote portal record: synced'));
|
|
97
|
+
}
|
|
87
98
|
}
|
|
88
99
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
9
|
import { Args, Command, Flags } from '@oclif/core';
|
|
10
|
-
import { getEnv } from '../../lib/auth-store.js';
|
|
10
|
+
import { getCurrentEnvName, getEnv, setEnvPortalPath } from '../../lib/auth-store.js';
|
|
11
11
|
import { resolveDefaultConfigScope } from '../../lib/cli-home.js';
|
|
12
12
|
import { translateCli } from '../../lib/cli-locale.js';
|
|
13
13
|
import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
|
|
@@ -19,9 +19,9 @@ export default class PortalCreate extends Command {
|
|
|
19
19
|
static summary = 'Create a local AI portal from a template';
|
|
20
20
|
static examples = [
|
|
21
21
|
'<%= config.bin %> <%= command.id %> customer',
|
|
22
|
+
'<%= config.bin %> <%= command.id %> customer --path ./portals/customer',
|
|
22
23
|
'<%= config.bin %> <%= command.id %> customer --template @nocobase/portal-template-default',
|
|
23
24
|
'<%= config.bin %> <%= command.id %> customer --env dev --yes',
|
|
24
|
-
'<%= config.bin %> <%= command.id %> customer --source-storage git --git-repo git@github.com:nocobase/customer-portal.git',
|
|
25
25
|
];
|
|
26
26
|
static args = {
|
|
27
27
|
portal: Args.string({
|
|
@@ -46,24 +46,13 @@ export default class PortalCreate extends Command {
|
|
|
46
46
|
title: Flags.string({
|
|
47
47
|
description: 'Portal display title; defaults to a title generated from the portal slug',
|
|
48
48
|
}),
|
|
49
|
+
path: Flags.string({
|
|
50
|
+
description: 'Portal workspace directory; defaults to ./<portal>',
|
|
51
|
+
}),
|
|
49
52
|
force: Flags.boolean({
|
|
50
53
|
description: 'Delete the existing portal and recreate it',
|
|
51
54
|
default: false,
|
|
52
55
|
}),
|
|
53
|
-
'source-storage': Flags.string({
|
|
54
|
-
description: 'Where portal source code is managed',
|
|
55
|
-
options: ['nocobase', 'git'],
|
|
56
|
-
default: 'nocobase',
|
|
57
|
-
}),
|
|
58
|
-
'git-repo': Flags.string({
|
|
59
|
-
description: 'Git repository URL used when --source-storage=git',
|
|
60
|
-
}),
|
|
61
|
-
'git-branch': Flags.string({
|
|
62
|
-
description: 'Git branch used when --source-storage=git',
|
|
63
|
-
}),
|
|
64
|
-
'git-path': Flags.string({
|
|
65
|
-
description: 'Directory inside the Git repository for this portal; defaults to the repository root',
|
|
66
|
-
}),
|
|
67
56
|
};
|
|
68
57
|
async run() {
|
|
69
58
|
const { args, flags } = await this.parse(PortalCreate);
|
|
@@ -77,10 +66,11 @@ export default class PortalCreate extends Command {
|
|
|
77
66
|
return;
|
|
78
67
|
}
|
|
79
68
|
const scope = resolveDefaultConfigScope();
|
|
80
|
-
const
|
|
69
|
+
const envName = requestedEnv ?? (await getCurrentEnvName({ scope }));
|
|
70
|
+
const env = await getEnv(envName, { scope });
|
|
81
71
|
if (!env) {
|
|
82
|
-
this.error(
|
|
83
|
-
? portalCreateText('errors.envNotConfigured', { envName
|
|
72
|
+
this.error(requestedEnv
|
|
73
|
+
? portalCreateText('errors.envNotConfigured', { envName }, `Env "${envName}" is not configured. Run \`nb env add ${envName} --api-base-url <url>\` first.`)
|
|
84
74
|
: portalCreateText('errors.noEnvConfigured', undefined, 'No NocoBase env is configured yet. Run `nb init --ui` to create one first.'));
|
|
85
75
|
}
|
|
86
76
|
const result = await createPortalWorkspace({
|
|
@@ -88,16 +78,14 @@ export default class PortalCreate extends Command {
|
|
|
88
78
|
title: flags.title,
|
|
89
79
|
template: flags.template,
|
|
90
80
|
env,
|
|
91
|
-
envName
|
|
81
|
+
envName,
|
|
92
82
|
cliVersion: String(this.config.pjson.version ?? '').trim(),
|
|
93
83
|
force: flags.force,
|
|
94
|
-
|
|
95
|
-
gitRepo: flags['git-repo'],
|
|
96
|
-
gitBranch: flags['git-branch'],
|
|
97
|
-
gitPath: flags['git-path'],
|
|
84
|
+
sourcePath: flags.path,
|
|
98
85
|
onSkipInstall: (message) => printInfo(message),
|
|
99
86
|
});
|
|
100
|
-
|
|
87
|
+
await setEnvPortalPath(envName, result.portal, result.portalDir, { scope });
|
|
88
|
+
printSuccess(portalCreateText('messages.created', { portal: result.portal, portalDir: result.portalDir }, `Portal "${result.portal}" created at ${result.portalDir}`));
|
|
101
89
|
printInfo(portalCreateText('messages.app', { app: result.app }, `App: ${result.app}`));
|
|
102
90
|
printInfo(portalCreateText('messages.base', { base: result.portalBase }, `Base: ${result.portalBase}`));
|
|
103
91
|
printInfo(portalCreateText('messages.sourceStorage', { sourceStorage: result.sourceStorage }, `Source storage: ${result.sourceStorage}`));
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
9
|
import { Args, Command, Flags } from '@oclif/core';
|
|
10
|
-
import { getCurrentEnvName, getEnv } from '../../lib/auth-store.js';
|
|
10
|
+
import { getCurrentEnvName, getEnv, unsetEnvPortalPath } from '../../lib/auth-store.js';
|
|
11
11
|
import { resolveDefaultConfigScope } from '../../lib/cli-home.js';
|
|
12
12
|
import { translateCli } from '../../lib/cli-locale.js';
|
|
13
13
|
import { ensureCrossEnvConfirmed, hasExplicitEnvSelection } from '../../lib/env-guard.js';
|
|
@@ -15,6 +15,9 @@ import { confirm } from "../../lib/inquirer.js";
|
|
|
15
15
|
import { destroyPortalWorkspace } from '../../lib/portal-destroy.js';
|
|
16
16
|
import { isInteractiveTerminal, printInfo, printSuccess } from '../../lib/ui.js';
|
|
17
17
|
const portalDestroyText = (key, values, fallback) => translateCli(`commands.portalDestroy.${key}`, values, { fallback });
|
|
18
|
+
function portalDestroyStatus(status) {
|
|
19
|
+
return portalDestroyText(`statuses.${status}`, undefined, status);
|
|
20
|
+
}
|
|
18
21
|
async function ensureDestroyConfirmed(options) {
|
|
19
22
|
if (options.yes) {
|
|
20
23
|
return true;
|
|
@@ -24,7 +27,7 @@ async function ensureDestroyConfirmed(options) {
|
|
|
24
27
|
}
|
|
25
28
|
try {
|
|
26
29
|
return Boolean(await confirm({
|
|
27
|
-
message: portalDestroyText('prompts.confirm', { portal: options.portal }, `Destroy portal "${options.portal}" and delete its
|
|
30
|
+
message: portalDestroyText('prompts.confirm', { portal: options.portal }, `Destroy portal "${options.portal}" and delete its deployment directory?`),
|
|
28
31
|
default: false,
|
|
29
32
|
}));
|
|
30
33
|
}
|
|
@@ -33,9 +36,10 @@ async function ensureDestroyConfirmed(options) {
|
|
|
33
36
|
}
|
|
34
37
|
}
|
|
35
38
|
export default class PortalDestroy extends Command {
|
|
36
|
-
static summary = 'Destroy a portal record and
|
|
39
|
+
static summary = 'Destroy a portal record and deployed files';
|
|
37
40
|
static examples = [
|
|
38
41
|
'<%= config.bin %> <%= command.id %> customer --yes',
|
|
42
|
+
'<%= config.bin %> <%= command.id %> customer --delete-dev-path --yes',
|
|
39
43
|
'<%= config.bin %> <%= command.id %> customer --env dev --yes',
|
|
40
44
|
'<%= config.bin %> <%= command.id %> customer --force --yes',
|
|
41
45
|
];
|
|
@@ -56,7 +60,12 @@ export default class PortalDestroy extends Command {
|
|
|
56
60
|
default: false,
|
|
57
61
|
}),
|
|
58
62
|
force: Flags.boolean({
|
|
59
|
-
description: 'Ignore missing portal records or
|
|
63
|
+
description: 'Ignore missing portal records or deployment files',
|
|
64
|
+
default: false,
|
|
65
|
+
}),
|
|
66
|
+
'delete-dev-path': Flags.boolean({
|
|
67
|
+
char: 'D',
|
|
68
|
+
description: 'Delete the portal development directory in addition to the deployed portal',
|
|
60
69
|
default: false,
|
|
61
70
|
}),
|
|
62
71
|
};
|
|
@@ -93,12 +102,25 @@ export default class PortalDestroy extends Command {
|
|
|
93
102
|
envName,
|
|
94
103
|
cliVersion: String(this.config.pjson.version ?? '').trim(),
|
|
95
104
|
force: flags.force,
|
|
105
|
+
deleteDevPath: flags['delete-dev-path'],
|
|
96
106
|
});
|
|
107
|
+
await unsetEnvPortalPath(envName, result.portal, { scope });
|
|
97
108
|
printSuccess(portalDestroyText('messages.destroyed', { portal: result.portal }, `Portal "${result.portal}" destroyed.`));
|
|
98
109
|
printInfo(portalDestroyText('messages.mode', { mode: result.mode }, `Mode: ${result.mode}`));
|
|
99
110
|
printInfo(portalDestroyText('messages.app', { app: result.app }, `App: ${result.app}`));
|
|
100
111
|
printInfo(portalDestroyText('messages.base', { base: result.portalBase }, `Base: ${result.portalBase}`));
|
|
101
|
-
printInfo(portalDestroyText('messages.record', { status: result.recordDeleted ? 'deleted' : 'missing' }, `Record: ${result.recordDeleted ? 'deleted' : 'missing'}`));
|
|
102
|
-
|
|
112
|
+
printInfo(portalDestroyText('messages.record', { status: portalDestroyStatus(result.recordDeleted ? 'deleted' : 'missing') }, `Record: ${result.recordDeleted ? 'deleted' : 'missing'}`));
|
|
113
|
+
const deploymentPathStatus = result.deploymentPathDeleted ? 'deleted' : 'missing';
|
|
114
|
+
printInfo(portalDestroyText('messages.deploymentPath', { dir: result.deploymentPath, status: portalDestroyStatus(deploymentPathStatus) }, `Deployment path: ${deploymentPathStatus} (${result.deploymentPath})`));
|
|
115
|
+
let developmentPathStatus = 'missing';
|
|
116
|
+
if (result.developmentPath) {
|
|
117
|
+
developmentPathStatus = result.developmentPathDeleted ? 'deleted' : 'retained';
|
|
118
|
+
}
|
|
119
|
+
printInfo(portalDestroyText(result.developmentPath ? 'messages.developmentPath' : 'messages.developmentPathMissing', {
|
|
120
|
+
dir: result.developmentPath,
|
|
121
|
+
status: portalDestroyStatus(developmentPathStatus),
|
|
122
|
+
}, result.developmentPath
|
|
123
|
+
? `Development path: ${developmentPathStatus} (${result.developmentPath})`
|
|
124
|
+
: 'Development path: missing'));
|
|
103
125
|
}
|
|
104
126
|
}
|
|
@@ -21,7 +21,7 @@ function formatBoolean(value) {
|
|
|
21
21
|
return value ? 'yes' : 'no';
|
|
22
22
|
}
|
|
23
23
|
export default class PortalList extends Command {
|
|
24
|
-
static summary = 'List portal records and
|
|
24
|
+
static summary = 'List portal records and development paths';
|
|
25
25
|
static examples = [
|
|
26
26
|
'<%= config.bin %> <%= command.id %>',
|
|
27
27
|
'<%= config.bin %> <%= command.id %> --env dev --yes',
|
|
@@ -82,17 +82,17 @@ export default class PortalList extends Command {
|
|
|
82
82
|
portalListText('table.url', undefined, 'URL'),
|
|
83
83
|
portalListText('table.portalType', undefined, 'Portal type'),
|
|
84
84
|
portalListText('table.sourceStorage', undefined, 'Source storage'),
|
|
85
|
-
portalListText('table.path', undefined, '
|
|
85
|
+
portalListText('table.path', undefined, 'Development path'),
|
|
86
86
|
portalListText('table.enabled', undefined, 'Enabled'),
|
|
87
|
-
portalListText('table.
|
|
87
|
+
portalListText('table.default', undefined, 'Default'),
|
|
88
88
|
], outputItems.map((item) => [
|
|
89
89
|
item.name,
|
|
90
90
|
item.url,
|
|
91
91
|
item.portalType,
|
|
92
92
|
item.sourceStorage,
|
|
93
|
-
item.
|
|
93
|
+
item.developmentPath,
|
|
94
94
|
formatBoolean(item.enabled),
|
|
95
|
-
formatBoolean(item.
|
|
95
|
+
formatBoolean(item.isDefault),
|
|
96
96
|
])));
|
|
97
97
|
}
|
|
98
98
|
}
|