@nocobase/cli 2.1.0-alpha.21 → 2.1.0-alpha.22
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/download.js +33 -22
- package/dist/commands/env/add.js +39 -12
- package/dist/commands/init.js +72 -45
- package/dist/commands/install.js +166 -51
- package/dist/commands/prompts-stages.js +6 -0
- package/dist/commands/prompts-test.js +6 -0
- package/dist/lib/api-client.js +49 -5
- package/dist/lib/cli-locale.js +115 -0
- package/dist/lib/env-auth.js +2 -2
- package/dist/lib/prompt-catalog.js +76 -54
- package/dist/lib/prompt-validators.js +9 -8
- package/dist/lib/prompt-web-ui.js +98 -64
- package/dist/lib/runtime-generator.js +12 -1
- package/dist/locale/en-US.json +282 -0
- package/dist/locale/zh-CN.json +282 -0
- package/package.json +4 -3
package/dist/commands/install.js
CHANGED
|
@@ -15,6 +15,7 @@ import { mkdir } from 'node:fs/promises';
|
|
|
15
15
|
import path from 'node:path';
|
|
16
16
|
import { exit } from 'node:process';
|
|
17
17
|
import { runPromptCatalog, } from "../lib/prompt-catalog.js";
|
|
18
|
+
import { applyCliLocale, localeText, translateCli, } from "../lib/cli-locale.js";
|
|
18
19
|
import { findAvailableTcpPort, validateAvailableTcpPort, validateTcpPort, validateEnvKey, } from "../lib/prompt-validators.js";
|
|
19
20
|
import { formatMissingManagedAppEnvMessage } from '../lib/app-runtime.js';
|
|
20
21
|
import { run, runNocoBaseCommand } from '../lib/run-npm.js';
|
|
@@ -33,11 +34,17 @@ const DEFAULT_INSTALL_DB_PORTS = {
|
|
|
33
34
|
mariadb: '3306',
|
|
34
35
|
kingbase: '54321',
|
|
35
36
|
};
|
|
37
|
+
const DEFAULT_INSTALL_BUILTIN_DB_IMAGES = {
|
|
38
|
+
postgres: 'postgres:16',
|
|
39
|
+
mysql: 'mysql:8',
|
|
40
|
+
mariadb: 'mariadb:11',
|
|
41
|
+
kingbase: 'registry.cn-shanghai.aliyuncs.com/nocobase/kingbase:v009r001c001b0030_single_x86',
|
|
42
|
+
};
|
|
36
43
|
const DEFAULT_INSTALL_DB_DATABASE = 'nocobase';
|
|
37
44
|
const DEFAULT_INSTALL_DB_USER = 'nocobase';
|
|
38
45
|
const DEFAULT_INSTALL_DB_PASSWORD = 'nocobase';
|
|
39
46
|
const DEFAULT_INSTALL_ROOT_USERNAME = 'nocobase';
|
|
40
|
-
const DEFAULT_INSTALL_ROOT_EMAIL = 'admin@
|
|
47
|
+
const DEFAULT_INSTALL_ROOT_EMAIL = 'admin@nocobase.com';
|
|
41
48
|
const DEFAULT_INSTALL_ROOT_PASSWORD = 'admin123';
|
|
42
49
|
const DEFAULT_INSTALL_ROOT_NICKNAME = 'Super Admin';
|
|
43
50
|
const CONFIG_SCOPE = 'project';
|
|
@@ -117,21 +124,47 @@ const INSTALL_LANGUAGE_OPTIONS = Object.entries(INSTALL_LANGUAGE_CODES).map(([va
|
|
|
117
124
|
value,
|
|
118
125
|
label: `${label} (${value})`,
|
|
119
126
|
}));
|
|
127
|
+
const installText = (key, values) => localeText(`commands.install.${key}`, values);
|
|
120
128
|
function argvHasToken(argv, tokens) {
|
|
121
129
|
return tokens.some((t) => argv.includes(t));
|
|
122
130
|
}
|
|
123
131
|
function isInstallDbDialect(value) {
|
|
124
132
|
return INSTALL_DB_DIALECTS.includes(value);
|
|
125
133
|
}
|
|
134
|
+
function supportsBuiltinDbDialect(value) {
|
|
135
|
+
const dialect = String(value ?? '').trim();
|
|
136
|
+
return Object.prototype.hasOwnProperty.call(DEFAULT_INSTALL_BUILTIN_DB_IMAGES, dialect);
|
|
137
|
+
}
|
|
126
138
|
export function defaultDbPortForDialect(value) {
|
|
127
139
|
const dialect = String(value ?? 'postgres').trim();
|
|
128
140
|
return DEFAULT_INSTALL_DB_PORTS[isInstallDbDialect(dialect) ? dialect : 'postgres'];
|
|
129
141
|
}
|
|
142
|
+
function defaultBuiltinDbImageForDialect(value) {
|
|
143
|
+
const dialect = String(value ?? 'postgres').trim();
|
|
144
|
+
return supportsBuiltinDbDialect(dialect)
|
|
145
|
+
? DEFAULT_INSTALL_BUILTIN_DB_IMAGES[dialect]
|
|
146
|
+
: DEFAULT_INSTALL_BUILTIN_DB_IMAGES.postgres;
|
|
147
|
+
}
|
|
148
|
+
function defaultDbDatabaseForDialect(value) {
|
|
149
|
+
return String(value ?? '').trim() === 'kingbase'
|
|
150
|
+
? 'kingbase'
|
|
151
|
+
: DEFAULT_INSTALL_DB_DATABASE;
|
|
152
|
+
}
|
|
130
153
|
function defaultDbHostForBuiltinDb(values) {
|
|
131
154
|
return Boolean(values.builtinDb)
|
|
132
155
|
? DEFAULT_INSTALL_BUILTIN_DB_HOST
|
|
133
156
|
: DEFAULT_INSTALL_DB_HOST;
|
|
134
157
|
}
|
|
158
|
+
function validateBuiltinDbEnabled(value, values) {
|
|
159
|
+
if (!Boolean(value)) {
|
|
160
|
+
return undefined;
|
|
161
|
+
}
|
|
162
|
+
const dialect = String(values.dbDialect ?? 'postgres').trim() || 'postgres';
|
|
163
|
+
if (supportsBuiltinDbDialect(dialect)) {
|
|
164
|
+
return undefined;
|
|
165
|
+
}
|
|
166
|
+
return translateCli('commands.install.validation.builtinDbUnsupported', { dialect });
|
|
167
|
+
}
|
|
135
168
|
function defaultInstallAppRootPath(envName) {
|
|
136
169
|
const name = String(envName ?? DEFAULT_INSTALL_ENV_NAME).trim() || DEFAULT_INSTALL_ENV_NAME;
|
|
137
170
|
return `./${name}/source/`;
|
|
@@ -264,6 +297,9 @@ export default class Install extends Command {
|
|
|
264
297
|
description: 'Database dialect for the app',
|
|
265
298
|
options: ['postgres', 'mysql', 'mariadb', 'kingbase'],
|
|
266
299
|
}),
|
|
300
|
+
'builtin-db-image': Flags.string({
|
|
301
|
+
description: 'Docker image for the built-in database container (default follows the selected database)',
|
|
302
|
+
}),
|
|
267
303
|
'db-host': Flags.string({
|
|
268
304
|
description: 'Database host for the app',
|
|
269
305
|
}),
|
|
@@ -289,8 +325,8 @@ export default class Install extends Command {
|
|
|
289
325
|
static envPrompts = {
|
|
290
326
|
env: {
|
|
291
327
|
type: 'text',
|
|
292
|
-
message: '
|
|
293
|
-
placeholder:
|
|
328
|
+
message: installText('prompts.env.message'),
|
|
329
|
+
placeholder: installText('prompts.env.placeholder'),
|
|
294
330
|
required: true,
|
|
295
331
|
validate: validateEnvKey,
|
|
296
332
|
},
|
|
@@ -298,7 +334,7 @@ export default class Install extends Command {
|
|
|
298
334
|
static appPrompts = {
|
|
299
335
|
lang: {
|
|
300
336
|
type: 'select',
|
|
301
|
-
message: '
|
|
337
|
+
message: installText('prompts.lang.message'),
|
|
302
338
|
options: INSTALL_LANGUAGE_OPTIONS,
|
|
303
339
|
initialValue: DEFAULT_INSTALL_LANG,
|
|
304
340
|
yesInitialValue: DEFAULT_INSTALL_LANG,
|
|
@@ -311,39 +347,33 @@ export default class Install extends Command {
|
|
|
311
347
|
// },
|
|
312
348
|
appRootPath: {
|
|
313
349
|
type: 'text',
|
|
314
|
-
message: '
|
|
315
|
-
placeholder: '
|
|
350
|
+
message: installText('prompts.appRootPath.message'),
|
|
351
|
+
placeholder: installText('prompts.appRootPath.placeholder'),
|
|
316
352
|
initialValue: (values) => defaultInstallAppRootPath(values.env ?? values.appName),
|
|
317
353
|
},
|
|
318
354
|
appPort: {
|
|
319
355
|
type: 'text',
|
|
320
|
-
message: '
|
|
321
|
-
placeholder:
|
|
356
|
+
message: installText('prompts.appPort.message'),
|
|
357
|
+
placeholder: installText('prompts.appPort.placeholder'),
|
|
322
358
|
validate: validateAvailableTcpPort,
|
|
323
359
|
},
|
|
324
360
|
storagePath: {
|
|
325
361
|
type: 'text',
|
|
326
|
-
message: '
|
|
327
|
-
placeholder: '
|
|
362
|
+
message: installText('prompts.storagePath.message'),
|
|
363
|
+
placeholder: installText('prompts.storagePath.placeholder'),
|
|
328
364
|
initialValue: (values) => defaultInstallStoragePath(values.env ?? values.appName),
|
|
329
365
|
},
|
|
330
366
|
fetchSource: {
|
|
331
367
|
type: 'boolean',
|
|
332
|
-
message: '
|
|
368
|
+
message: installText('prompts.fetchSource.message'),
|
|
333
369
|
initialValue: true,
|
|
334
370
|
yesInitialValue: true,
|
|
335
371
|
},
|
|
336
372
|
};
|
|
337
373
|
static dbPrompts = {
|
|
338
|
-
builtinDb: {
|
|
339
|
-
type: 'boolean',
|
|
340
|
-
message: "Would you like to use the built-in database?",
|
|
341
|
-
initialValue: true,
|
|
342
|
-
yesInitialValue: true,
|
|
343
|
-
},
|
|
344
374
|
dbDialect: {
|
|
345
375
|
type: 'select',
|
|
346
|
-
message: '
|
|
376
|
+
message: installText('prompts.dbDialect.message'),
|
|
347
377
|
options: [
|
|
348
378
|
{ value: 'postgres', label: 'PostgreSQL' },
|
|
349
379
|
{ value: 'mysql', label: 'MySQL' },
|
|
@@ -354,10 +384,26 @@ export default class Install extends Command {
|
|
|
354
384
|
yesInitialValue: 'postgres',
|
|
355
385
|
required: true,
|
|
356
386
|
},
|
|
387
|
+
builtinDb: {
|
|
388
|
+
type: 'boolean',
|
|
389
|
+
message: installText('prompts.builtinDb.message'),
|
|
390
|
+
initialValue: true,
|
|
391
|
+
yesInitialValue: true,
|
|
392
|
+
validate: validateBuiltinDbEnabled,
|
|
393
|
+
},
|
|
394
|
+
builtinDbImage: {
|
|
395
|
+
type: 'text',
|
|
396
|
+
message: installText('prompts.builtinDbImage.message'),
|
|
397
|
+
placeholder: installText('prompts.builtinDbImage.placeholder'),
|
|
398
|
+
initialValue: (values) => defaultBuiltinDbImageForDialect(values.dbDialect),
|
|
399
|
+
hidden: (values) => !Boolean(values.builtinDb)
|
|
400
|
+
|| !supportsBuiltinDbDialect(values.dbDialect),
|
|
401
|
+
required: true,
|
|
402
|
+
},
|
|
357
403
|
dbHost: {
|
|
358
404
|
type: 'text',
|
|
359
|
-
message: '
|
|
360
|
-
placeholder:
|
|
405
|
+
message: installText('prompts.dbHost.message'),
|
|
406
|
+
placeholder: installText('prompts.dbHost.placeholder'),
|
|
361
407
|
initialValue: (values) => defaultDbHostForBuiltinDb(values),
|
|
362
408
|
yesInitialValue: DEFAULT_INSTALL_BUILTIN_DB_HOST,
|
|
363
409
|
required: true,
|
|
@@ -365,8 +411,8 @@ export default class Install extends Command {
|
|
|
365
411
|
},
|
|
366
412
|
dbPort: {
|
|
367
413
|
type: 'text',
|
|
368
|
-
message: '
|
|
369
|
-
placeholder:
|
|
414
|
+
message: installText('prompts.dbPort.message'),
|
|
415
|
+
placeholder: installText('prompts.dbPort.placeholder'),
|
|
370
416
|
initialValue: (values) => defaultDbPortForDialect(values.dbDialect),
|
|
371
417
|
required: true,
|
|
372
418
|
validate: validateTcpPort,
|
|
@@ -375,21 +421,20 @@ export default class Install extends Command {
|
|
|
375
421
|
},
|
|
376
422
|
dbDatabase: {
|
|
377
423
|
type: 'text',
|
|
378
|
-
message: '
|
|
379
|
-
initialValue:
|
|
380
|
-
yesInitialValue: DEFAULT_INSTALL_DB_DATABASE,
|
|
424
|
+
message: installText('prompts.dbDatabase.message'),
|
|
425
|
+
initialValue: (values) => defaultDbDatabaseForDialect(values.dbDialect),
|
|
381
426
|
required: true,
|
|
382
427
|
},
|
|
383
428
|
dbUser: {
|
|
384
429
|
type: 'text',
|
|
385
|
-
message: '
|
|
430
|
+
message: installText('prompts.dbUser.message'),
|
|
386
431
|
initialValue: DEFAULT_INSTALL_DB_USER,
|
|
387
432
|
yesInitialValue: DEFAULT_INSTALL_DB_USER,
|
|
388
433
|
required: true,
|
|
389
434
|
},
|
|
390
435
|
dbPassword: {
|
|
391
436
|
type: 'password',
|
|
392
|
-
message: '
|
|
437
|
+
message: installText('prompts.dbPassword.message'),
|
|
393
438
|
initialValue: DEFAULT_INSTALL_DB_PASSWORD,
|
|
394
439
|
yesInitialValue: DEFAULT_INSTALL_DB_PASSWORD,
|
|
395
440
|
required: true,
|
|
@@ -398,30 +443,30 @@ export default class Install extends Command {
|
|
|
398
443
|
static rootUserPrompts = {
|
|
399
444
|
rootUsername: {
|
|
400
445
|
type: 'text',
|
|
401
|
-
message: '
|
|
402
|
-
placeholder:
|
|
403
|
-
initialValue: DEFAULT_INSTALL_ROOT_USERNAME,
|
|
446
|
+
message: installText('prompts.rootUsername.message'),
|
|
447
|
+
placeholder: installText('prompts.rootUsername.placeholder'),
|
|
404
448
|
yesInitialValue: DEFAULT_INSTALL_ROOT_USERNAME,
|
|
449
|
+
required: true,
|
|
405
450
|
},
|
|
406
451
|
rootEmail: {
|
|
407
452
|
type: 'text',
|
|
408
|
-
message: '
|
|
409
|
-
placeholder:
|
|
410
|
-
initialValue: DEFAULT_INSTALL_ROOT_EMAIL,
|
|
453
|
+
message: installText('prompts.rootEmail.message'),
|
|
454
|
+
placeholder: installText('prompts.rootEmail.placeholder'),
|
|
411
455
|
yesInitialValue: DEFAULT_INSTALL_ROOT_EMAIL,
|
|
456
|
+
required: true,
|
|
412
457
|
},
|
|
413
458
|
rootPassword: {
|
|
414
459
|
type: 'password',
|
|
415
|
-
message: '
|
|
416
|
-
initialValue: DEFAULT_INSTALL_ROOT_PASSWORD,
|
|
460
|
+
message: installText('prompts.rootPassword.message'),
|
|
417
461
|
yesInitialValue: DEFAULT_INSTALL_ROOT_PASSWORD,
|
|
462
|
+
required: true,
|
|
418
463
|
},
|
|
419
464
|
rootNickname: {
|
|
420
465
|
type: 'text',
|
|
421
|
-
message: '
|
|
422
|
-
placeholder:
|
|
423
|
-
initialValue: DEFAULT_INSTALL_ROOT_NICKNAME,
|
|
466
|
+
message: installText('prompts.rootNickname.message'),
|
|
467
|
+
placeholder: installText('prompts.rootNickname.placeholder'),
|
|
424
468
|
yesInitialValue: DEFAULT_INSTALL_ROOT_NICKNAME,
|
|
469
|
+
required: true,
|
|
425
470
|
},
|
|
426
471
|
};
|
|
427
472
|
/**
|
|
@@ -520,6 +565,12 @@ export default class Install extends Command {
|
|
|
520
565
|
preset.dbDialect = t;
|
|
521
566
|
}
|
|
522
567
|
}
|
|
568
|
+
if (flags['builtin-db-image'] !== undefined) {
|
|
569
|
+
const v = String(flags['builtin-db-image'] ?? '').trim();
|
|
570
|
+
if (v) {
|
|
571
|
+
preset.builtinDbImage = v;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
523
574
|
if (flags['db-host'] !== undefined) {
|
|
524
575
|
const v = String(flags['db-host'] ?? '').trim();
|
|
525
576
|
if (v) {
|
|
@@ -563,6 +614,7 @@ export default class Install extends Command {
|
|
|
563
614
|
return pickPresetKeys(Install.buildPresetValuesFromFlags(flags), [
|
|
564
615
|
'builtinDb',
|
|
565
616
|
'dbDialect',
|
|
617
|
+
'builtinDbImage',
|
|
566
618
|
'dbHost',
|
|
567
619
|
'dbPort',
|
|
568
620
|
'dbDatabase',
|
|
@@ -603,6 +655,7 @@ export default class Install extends Command {
|
|
|
603
655
|
const dbDatabase = Install.toOptionalPromptString(config.dbDatabase);
|
|
604
656
|
const dbUser = Install.toOptionalPromptString(config.dbUser);
|
|
605
657
|
const dbPassword = Install.toOptionalPromptString(config.dbPassword);
|
|
658
|
+
const builtinDbImage = Install.toOptionalPromptString(config.builtinDbImage);
|
|
606
659
|
const auth = config.auth;
|
|
607
660
|
const appPreset = {
|
|
608
661
|
...(appRootPath ? { appRootPath } : {}),
|
|
@@ -630,6 +683,7 @@ export default class Install extends Command {
|
|
|
630
683
|
const dbPreset = {
|
|
631
684
|
...(typeof config.builtinDb === 'boolean' ? { builtinDb: config.builtinDb } : {}),
|
|
632
685
|
...(dbDialect ? { dbDialect } : {}),
|
|
686
|
+
...(builtinDbImage ? { builtinDbImage } : {}),
|
|
633
687
|
...(dbHost ? { dbHost } : {}),
|
|
634
688
|
...(dbPort ? { dbPort } : {}),
|
|
635
689
|
...(dbDatabase ? { dbDatabase } : {}),
|
|
@@ -719,7 +773,7 @@ export default class Install extends Command {
|
|
|
719
773
|
if (params.flags['app-port'] === undefined) {
|
|
720
774
|
initialValues.appPort = await Install.resolveAvailableDefaultPort(DEFAULT_INSTALL_APP_PORT, {
|
|
721
775
|
label: 'Default app port',
|
|
722
|
-
warn: true,
|
|
776
|
+
warn: params.warnOnPortFallback ?? true,
|
|
723
777
|
});
|
|
724
778
|
}
|
|
725
779
|
return initialValues;
|
|
@@ -745,7 +799,7 @@ export default class Install extends Command {
|
|
|
745
799
|
return {
|
|
746
800
|
dbPort: await Install.resolveAvailableDefaultPort(defaultPort, {
|
|
747
801
|
label: `Default ${dialect} port`,
|
|
748
|
-
warn: true,
|
|
802
|
+
warn: params.warnOnPortFallback ?? true,
|
|
749
803
|
}),
|
|
750
804
|
};
|
|
751
805
|
}
|
|
@@ -900,6 +954,7 @@ export default class Install extends Command {
|
|
|
900
954
|
const dbDialect = String(params.dbDialect ?? 'postgres').trim() || 'postgres';
|
|
901
955
|
const dbPort = String(params.dbPort ?? defaultDbPortForDialect(dbDialect)).trim()
|
|
902
956
|
|| defaultDbPortForDialect(dbDialect);
|
|
957
|
+
const defaultDbDatabase = defaultDbDatabaseForDialect(dbDialect);
|
|
903
958
|
const networkName = Install.buildBuiltinDbNetworkName(params.envName, params.workspaceName);
|
|
904
959
|
const containerName = Install.buildBuiltinDbContainerName(params.envName, dbDialect, params.workspaceName);
|
|
905
960
|
const dbHostInput = String(params.dbHost ?? '').trim();
|
|
@@ -915,6 +970,7 @@ export default class Install extends Command {
|
|
|
915
970
|
? dbHostInput
|
|
916
971
|
: containerName);
|
|
917
972
|
if (dbDialect === 'postgres') {
|
|
973
|
+
const image = String(params.builtinDbImage ?? '').trim() || DEFAULT_INSTALL_BUILTIN_DB_IMAGES.postgres;
|
|
918
974
|
const dataDir = path.resolve(params.storagePath, 'db', 'postgres');
|
|
919
975
|
const args = [
|
|
920
976
|
'run',
|
|
@@ -937,14 +993,14 @@ export default class Install extends Command {
|
|
|
937
993
|
if (Install.shouldPublishBuiltinDbPort(params.source)) {
|
|
938
994
|
args.push('-p', `${dbPort}:5432`);
|
|
939
995
|
}
|
|
940
|
-
args.push(
|
|
996
|
+
args.push(image, 'postgres', '-c', 'wal_level=logical');
|
|
941
997
|
return {
|
|
942
998
|
source: String(params.source ?? '').trim() || undefined,
|
|
943
999
|
dbDialect,
|
|
944
1000
|
dbHost,
|
|
945
1001
|
dbPort,
|
|
946
|
-
dbDatabase: String(params.dbDatabase ??
|
|
947
|
-
||
|
|
1002
|
+
dbDatabase: String(params.dbDatabase ?? defaultDbDatabase).trim()
|
|
1003
|
+
|| defaultDbDatabase,
|
|
948
1004
|
dbUser: String(params.dbUser ?? DEFAULT_INSTALL_DB_USER).trim()
|
|
949
1005
|
|| DEFAULT_INSTALL_DB_USER,
|
|
950
1006
|
dbPassword: String(params.dbPassword ?? DEFAULT_INSTALL_DB_PASSWORD)
|
|
@@ -952,14 +1008,16 @@ export default class Install extends Command {
|
|
|
952
1008
|
networkName,
|
|
953
1009
|
containerName,
|
|
954
1010
|
dataDir,
|
|
955
|
-
|
|
1011
|
+
builtinDbImage: image,
|
|
1012
|
+
image,
|
|
956
1013
|
args,
|
|
957
1014
|
};
|
|
958
1015
|
}
|
|
959
1016
|
if (dbDialect === 'mysql') {
|
|
1017
|
+
const image = String(params.builtinDbImage ?? '').trim() || DEFAULT_INSTALL_BUILTIN_DB_IMAGES.mysql;
|
|
960
1018
|
const dataDir = path.resolve(params.storagePath, 'db', 'mysql');
|
|
961
1019
|
const dbUser = String(params.dbUser ?? DEFAULT_INSTALL_DB_USER).trim() || DEFAULT_INSTALL_DB_USER;
|
|
962
|
-
const dbDatabase = String(params.dbDatabase ??
|
|
1020
|
+
const dbDatabase = String(params.dbDatabase ?? defaultDbDatabase).trim() || defaultDbDatabase;
|
|
963
1021
|
const dbPassword = String(params.dbPassword ?? DEFAULT_INSTALL_DB_PASSWORD) || DEFAULT_INSTALL_DB_PASSWORD;
|
|
964
1022
|
const args = [
|
|
965
1023
|
'run',
|
|
@@ -984,7 +1042,7 @@ export default class Install extends Command {
|
|
|
984
1042
|
if (Install.shouldPublishBuiltinDbPort(params.source)) {
|
|
985
1043
|
args.push('-p', `${dbPort}:3306`);
|
|
986
1044
|
}
|
|
987
|
-
args.push(
|
|
1045
|
+
args.push(image);
|
|
988
1046
|
return {
|
|
989
1047
|
source: String(params.source ?? '').trim() || undefined,
|
|
990
1048
|
dbDialect,
|
|
@@ -996,14 +1054,16 @@ export default class Install extends Command {
|
|
|
996
1054
|
networkName,
|
|
997
1055
|
containerName,
|
|
998
1056
|
dataDir,
|
|
999
|
-
|
|
1057
|
+
builtinDbImage: image,
|
|
1058
|
+
image,
|
|
1000
1059
|
args,
|
|
1001
1060
|
};
|
|
1002
1061
|
}
|
|
1003
1062
|
if (dbDialect === 'mariadb') {
|
|
1063
|
+
const image = String(params.builtinDbImage ?? '').trim() || DEFAULT_INSTALL_BUILTIN_DB_IMAGES.mariadb;
|
|
1004
1064
|
const dataDir = path.resolve(params.storagePath, 'db', 'mariadb');
|
|
1005
1065
|
const dbUser = String(params.dbUser ?? DEFAULT_INSTALL_DB_USER).trim() || DEFAULT_INSTALL_DB_USER;
|
|
1006
|
-
const dbDatabase = String(params.dbDatabase ??
|
|
1066
|
+
const dbDatabase = String(params.dbDatabase ?? defaultDbDatabase).trim() || defaultDbDatabase;
|
|
1007
1067
|
const dbPassword = String(params.dbPassword ?? DEFAULT_INSTALL_DB_PASSWORD) || DEFAULT_INSTALL_DB_PASSWORD;
|
|
1008
1068
|
const args = [
|
|
1009
1069
|
'run',
|
|
@@ -1028,7 +1088,58 @@ export default class Install extends Command {
|
|
|
1028
1088
|
if (Install.shouldPublishBuiltinDbPort(params.source)) {
|
|
1029
1089
|
args.push('-p', `${dbPort}:3306`);
|
|
1030
1090
|
}
|
|
1031
|
-
args.push(
|
|
1091
|
+
args.push(image);
|
|
1092
|
+
return {
|
|
1093
|
+
source: String(params.source ?? '').trim() || undefined,
|
|
1094
|
+
dbDialect,
|
|
1095
|
+
dbHost,
|
|
1096
|
+
dbPort,
|
|
1097
|
+
dbDatabase,
|
|
1098
|
+
dbUser,
|
|
1099
|
+
dbPassword,
|
|
1100
|
+
networkName,
|
|
1101
|
+
containerName,
|
|
1102
|
+
dataDir,
|
|
1103
|
+
builtinDbImage: image,
|
|
1104
|
+
image,
|
|
1105
|
+
args,
|
|
1106
|
+
};
|
|
1107
|
+
}
|
|
1108
|
+
if (dbDialect === 'kingbase') {
|
|
1109
|
+
const image = String(params.builtinDbImage ?? '').trim() || DEFAULT_INSTALL_BUILTIN_DB_IMAGES.kingbase;
|
|
1110
|
+
const dataDir = path.resolve(params.storagePath, 'db', 'kingbase');
|
|
1111
|
+
const dbUser = String(params.dbUser ?? DEFAULT_INSTALL_DB_USER).trim() || DEFAULT_INSTALL_DB_USER;
|
|
1112
|
+
const dbDatabase = String(params.dbDatabase ?? defaultDbDatabase).trim() || defaultDbDatabase;
|
|
1113
|
+
const dbPassword = String(params.dbPassword ?? DEFAULT_INSTALL_DB_PASSWORD) || DEFAULT_INSTALL_DB_PASSWORD;
|
|
1114
|
+
const args = [
|
|
1115
|
+
'run',
|
|
1116
|
+
'-d',
|
|
1117
|
+
'--name',
|
|
1118
|
+
containerName,
|
|
1119
|
+
'--restart',
|
|
1120
|
+
'always',
|
|
1121
|
+
'--network',
|
|
1122
|
+
networkName,
|
|
1123
|
+
'--platform',
|
|
1124
|
+
'linux/amd64',
|
|
1125
|
+
'--privileged',
|
|
1126
|
+
'-e',
|
|
1127
|
+
'ENABLE_CI=no',
|
|
1128
|
+
'-e',
|
|
1129
|
+
`DB_USER=${dbUser}`,
|
|
1130
|
+
'-e',
|
|
1131
|
+
`DB_PASSWORD=${dbPassword}`,
|
|
1132
|
+
'-e',
|
|
1133
|
+
'DB_MODE=pg',
|
|
1134
|
+
'-e',
|
|
1135
|
+
'NEED_START=yes',
|
|
1136
|
+
'-v',
|
|
1137
|
+
`${dataDir}:/home/kingbase/userdata`,
|
|
1138
|
+
];
|
|
1139
|
+
if (Install.shouldPublishBuiltinDbPort(params.source)) {
|
|
1140
|
+
args.push('-p', `${dbPort}:54321`);
|
|
1141
|
+
}
|
|
1142
|
+
args.push(image, '/usr/sbin/init');
|
|
1032
1143
|
return {
|
|
1033
1144
|
source: String(params.source ?? '').trim() || undefined,
|
|
1034
1145
|
dbDialect,
|
|
@@ -1040,11 +1151,12 @@ export default class Install extends Command {
|
|
|
1040
1151
|
networkName,
|
|
1041
1152
|
containerName,
|
|
1042
1153
|
dataDir,
|
|
1043
|
-
|
|
1154
|
+
builtinDbImage: image,
|
|
1155
|
+
image,
|
|
1044
1156
|
args,
|
|
1045
1157
|
};
|
|
1046
1158
|
}
|
|
1047
|
-
throw new Error(`Built-in database does not support "${dbDialect}" yet. Please choose PostgreSQL, MySQL, or
|
|
1159
|
+
throw new Error(`Built-in database does not support "${dbDialect}" yet. Please choose PostgreSQL, MySQL, MariaDB, or KingbaseES.`);
|
|
1048
1160
|
}
|
|
1049
1161
|
async ensureDockerNetwork(name) {
|
|
1050
1162
|
p.log.step(`Checking Docker network: ${name}`);
|
|
@@ -1138,6 +1250,7 @@ export default class Install extends Command {
|
|
|
1138
1250
|
dbDatabase: params.dbResults.dbDatabase,
|
|
1139
1251
|
dbUser: params.dbResults.dbUser,
|
|
1140
1252
|
dbPassword: params.dbResults.dbPassword,
|
|
1253
|
+
builtinDbImage: params.dbResults.builtinDbImage,
|
|
1141
1254
|
});
|
|
1142
1255
|
p.log.step(`Preparing built-in ${plan.dbDialect} database`);
|
|
1143
1256
|
await this.ensureDockerNetwork(plan.networkName);
|
|
@@ -1519,6 +1632,7 @@ export default class Install extends Command {
|
|
|
1519
1632
|
Install.pushArgIfValue(argv, '--timezone', params.appResults.timeZone);
|
|
1520
1633
|
Install.pushBooleanArgIfSet(argv, '--builtin-db', params.dbResults.builtinDb);
|
|
1521
1634
|
Install.pushArgIfValue(argv, '--db-dialect', params.dbResults.dbDialect);
|
|
1635
|
+
Install.pushArgIfValue(argv, '--builtin-db-image', params.dbResults.builtinDbImage);
|
|
1522
1636
|
Install.pushArgIfValue(argv, '--db-host', params.dbResults.dbHost);
|
|
1523
1637
|
Install.pushArgIfValue(argv, '--db-port', params.dbResults.dbPort);
|
|
1524
1638
|
Install.pushArgIfValue(argv, '--db-database', params.dbResults.dbDatabase);
|
|
@@ -1624,6 +1738,7 @@ export default class Install extends Command {
|
|
|
1624
1738
|
}
|
|
1625
1739
|
async run() {
|
|
1626
1740
|
const parsedResult = await this.parse(Install);
|
|
1741
|
+
applyCliLocale(parsedResult.flags.locale);
|
|
1627
1742
|
const flags = parsedResult.flags;
|
|
1628
1743
|
const parsed = {
|
|
1629
1744
|
...flags,
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { Args, Command, Flags } from '@oclif/core';
|
|
10
10
|
import { runPromptCatalog, } from "../lib/prompt-catalog.js";
|
|
11
|
+
import { applyCliLocale, CLI_LOCALE_FLAG_DESCRIPTION, CLI_LOCALE_FLAG_OPTIONS, } from "../lib/cli-locale.js";
|
|
11
12
|
import { runPromptCatalogWebUI } from "../lib/prompt-web-ui.js";
|
|
12
13
|
import PromptsTest from "./prompts-test.js";
|
|
13
14
|
function buildWebUiStagesFromTestPrompts(c) {
|
|
@@ -67,6 +68,10 @@ export default class PromptsStages extends Command {
|
|
|
67
68
|
description: 'Accept defaults only in the terminal (no Clack after submit); same semantics as `prompts-test` when used with the submitted or default preset.',
|
|
68
69
|
default: false,
|
|
69
70
|
}),
|
|
71
|
+
locale: Flags.string({
|
|
72
|
+
description: CLI_LOCALE_FLAG_DESCRIPTION,
|
|
73
|
+
options: CLI_LOCALE_FLAG_OPTIONS,
|
|
74
|
+
}),
|
|
70
75
|
file: Flags.string({
|
|
71
76
|
char: 'f',
|
|
72
77
|
description: 'Merged into initialValues.file (default for the text prompt when set)',
|
|
@@ -96,6 +101,7 @@ export default class PromptsStages extends Command {
|
|
|
96
101
|
};
|
|
97
102
|
async run() {
|
|
98
103
|
const { args, flags } = await this.parse(PromptsStages);
|
|
104
|
+
applyCliLocale(flags.locale);
|
|
99
105
|
let presetValues = this.buildPresetValuesFromParsed(args, flags);
|
|
100
106
|
if (flags.ui) {
|
|
101
107
|
presetValues = await runPromptCatalogWebUI({
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import * as p from '@clack/prompts';
|
|
10
10
|
import { Args, Command, Flags } from '@oclif/core';
|
|
11
11
|
import { runPromptCatalog, } from "../lib/prompt-catalog.js";
|
|
12
|
+
import { applyCliLocale, CLI_LOCALE_FLAG_DESCRIPTION, CLI_LOCALE_FLAG_OPTIONS, } from "../lib/cli-locale.js";
|
|
12
13
|
import { runPromptCatalogWebUI, } from "../lib/prompt-web-ui.js";
|
|
13
14
|
export default class PromptsTest extends Command {
|
|
14
15
|
static hidden = true;
|
|
@@ -33,6 +34,10 @@ export default class PromptsTest extends Command {
|
|
|
33
34
|
description: 'Accept defaults only (no prompts); uses `yesInitialValues` merged over `initialValues`, then per-block `yesInitialValue`. Same as non-TTY.',
|
|
34
35
|
default: false,
|
|
35
36
|
}),
|
|
37
|
+
locale: Flags.string({
|
|
38
|
+
description: CLI_LOCALE_FLAG_DESCRIPTION,
|
|
39
|
+
options: CLI_LOCALE_FLAG_OPTIONS,
|
|
40
|
+
}),
|
|
36
41
|
file: Flags.string({
|
|
37
42
|
char: 'f',
|
|
38
43
|
description: 'Merged into initialValues.file (default for the text prompt when set)',
|
|
@@ -127,6 +132,7 @@ export default class PromptsTest extends Command {
|
|
|
127
132
|
};
|
|
128
133
|
async run() {
|
|
129
134
|
const { args, flags } = await this.parse(PromptsTest);
|
|
135
|
+
applyCliLocale(flags.locale);
|
|
130
136
|
let presetValues = this.buildPresetValuesFromParsed(args, flags);
|
|
131
137
|
if (flags.ui) {
|
|
132
138
|
presetValues = await runPromptCatalogWebUI(PromptsTest.prompts, {
|
package/dist/lib/api-client.js
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
1
9
|
import { promises as fs } from 'node:fs';
|
|
2
10
|
import { resolveServerRequestTarget } from './env-auth.js';
|
|
11
|
+
const CLI_REQUEST_SOURCE_HEADER = 'x-request-source';
|
|
12
|
+
const CLI_REQUEST_SOURCE_VALUE = 'cli';
|
|
13
|
+
function stripUtf8Bom(text) {
|
|
14
|
+
return text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;
|
|
15
|
+
}
|
|
16
|
+
function parseJsonInput(raw, flagName) {
|
|
17
|
+
const content = stripUtf8Bom(raw);
|
|
18
|
+
try {
|
|
19
|
+
return JSON.parse(content);
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
throw new Error(`Invalid JSON for --${flagName}: ${error?.message ?? 'parse failed'}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
3
25
|
function normalizeBaseUrl(baseUrl) {
|
|
4
26
|
return baseUrl.replace(/\/+$/, '');
|
|
5
27
|
}
|
|
@@ -59,6 +81,28 @@ function listProvidedBodyFlags(flags, parameters) {
|
|
|
59
81
|
.filter((parameter) => hasParameterValue(flags, parameter))
|
|
60
82
|
.map((parameter) => `--${parameter.flagName}`);
|
|
61
83
|
}
|
|
84
|
+
function parseBodyFieldValue(rawValue, parameter) {
|
|
85
|
+
if (rawValue === undefined) {
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
if (parameter.isArray && !parameter.jsonEncoded) {
|
|
89
|
+
return Array.isArray(rawValue) ? rawValue : rawValue ? [rawValue] : undefined;
|
|
90
|
+
}
|
|
91
|
+
if (parameter.jsonEncoded || parameter.type === 'object' || parameter.type === 'array') {
|
|
92
|
+
if (typeof rawValue !== 'string') {
|
|
93
|
+
return rawValue;
|
|
94
|
+
}
|
|
95
|
+
const parsed = parseJsonInput(rawValue, parameter.flagName);
|
|
96
|
+
if (parameter.type === 'array' && !Array.isArray(parsed)) {
|
|
97
|
+
throw new Error(`--${parameter.flagName} must be a JSON array`);
|
|
98
|
+
}
|
|
99
|
+
if (parameter.type === 'object' && (parsed === null || Array.isArray(parsed) || typeof parsed !== 'object')) {
|
|
100
|
+
throw new Error(`--${parameter.flagName} must be a JSON object`);
|
|
101
|
+
}
|
|
102
|
+
return parsed;
|
|
103
|
+
}
|
|
104
|
+
return parseScalarValue(rawValue, parameter.type);
|
|
105
|
+
}
|
|
62
106
|
export async function parseBody(flags, operation) {
|
|
63
107
|
const inlineBody = flags.body;
|
|
64
108
|
const bodyFile = flags['body-file'];
|
|
@@ -70,10 +114,10 @@ export async function parseBody(flags, operation) {
|
|
|
70
114
|
throw new Error(`Conflicting request body inputs: received ${rawBodyInput} together with body field flags (${providedBodyFlags.join(', ')}). Use either body field flags or --body/--body-file.`);
|
|
71
115
|
}
|
|
72
116
|
if (inlineBody) {
|
|
73
|
-
return
|
|
117
|
+
return parseJsonInput(inlineBody, 'body');
|
|
74
118
|
}
|
|
75
119
|
if (bodyFile) {
|
|
76
|
-
return fs.readFile(bodyFile, 'utf8').then((content) =>
|
|
120
|
+
return fs.readFile(bodyFile, 'utf8').then((content) => parseJsonInput(content, 'body-file'));
|
|
77
121
|
}
|
|
78
122
|
if (!bodyParameters.length) {
|
|
79
123
|
return undefined;
|
|
@@ -81,9 +125,7 @@ export async function parseBody(flags, operation) {
|
|
|
81
125
|
const body = {};
|
|
82
126
|
for (const parameter of bodyParameters) {
|
|
83
127
|
const rawValue = flags[parameter.flagName];
|
|
84
|
-
const value =
|
|
85
|
-
? (Array.isArray(rawValue) ? rawValue : rawValue ? [rawValue] : undefined)
|
|
86
|
-
: parseScalarValue(rawValue, parameter.type);
|
|
128
|
+
const value = parseBodyFieldValue(rawValue, parameter);
|
|
87
129
|
if (parameter.required && (value === undefined || value === '')) {
|
|
88
130
|
throw new Error(`Missing required body field --${parameter.flagName}`);
|
|
89
131
|
}
|
|
@@ -103,6 +145,7 @@ export async function parseBody(flags, operation) {
|
|
|
103
145
|
export async function executeApiRequest(options) {
|
|
104
146
|
const { baseUrl, token } = await resolveServerRequestTarget(options);
|
|
105
147
|
const headers = new Headers();
|
|
148
|
+
headers.set(CLI_REQUEST_SOURCE_HEADER, CLI_REQUEST_SOURCE_VALUE);
|
|
106
149
|
if (token) {
|
|
107
150
|
headers.set('authorization', `Bearer ${token}`);
|
|
108
151
|
}
|
|
@@ -162,6 +205,7 @@ export async function executeApiRequest(options) {
|
|
|
162
205
|
export async function executeRawApiRequest(options) {
|
|
163
206
|
const { baseUrl, token } = await resolveServerRequestTarget(options);
|
|
164
207
|
const headers = new Headers();
|
|
208
|
+
headers.set(CLI_REQUEST_SOURCE_HEADER, CLI_REQUEST_SOURCE_VALUE);
|
|
165
209
|
if (token) {
|
|
166
210
|
headers.set('authorization', `Bearer ${token}`);
|
|
167
211
|
}
|