@nocobase/cli 2.2.0-alpha.1 → 2.2.0-alpha.11

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.
Files changed (78) hide show
  1. package/assets/env-proxy/nginx/snippets/proxy-location.conf +1 -0
  2. package/assets/env-proxy/nginx/snippets/uploads-location.conf +4 -1
  3. package/bin/early-locale.js +89 -0
  4. package/bin/node-version.js +35 -0
  5. package/bin/run.js +9 -0
  6. package/bin/windows-admin.js +60 -0
  7. package/dist/commands/app/destroy.js +4 -3
  8. package/dist/commands/app/restart.js +38 -0
  9. package/dist/commands/app/shared.js +49 -3
  10. package/dist/commands/app/start.js +95 -0
  11. package/dist/commands/app/upgrade.js +11 -0
  12. package/dist/commands/config/set.js +1 -0
  13. package/dist/commands/env/info.js +11 -1
  14. package/dist/commands/examples/prompts-stages.js +2 -2
  15. package/dist/commands/examples/prompts-test.js +2 -2
  16. package/dist/commands/init.js +152 -14
  17. package/dist/commands/install.js +256 -109
  18. package/dist/commands/license/activate.js +4 -1
  19. package/dist/commands/license/shared.js +24 -15
  20. package/dist/commands/portal/config.js +88 -0
  21. package/dist/commands/portal/create.js +104 -0
  22. package/dist/commands/portal/deploy.js +81 -0
  23. package/dist/commands/portal/destroy.js +104 -0
  24. package/dist/commands/portal/dev.js +71 -0
  25. package/dist/commands/portal/index.js +20 -0
  26. package/dist/commands/portal/info.js +82 -0
  27. package/dist/commands/portal/list.js +98 -0
  28. package/dist/commands/portal/pull.js +84 -0
  29. package/dist/commands/portal/push.js +79 -0
  30. package/dist/commands/proxy/caddy/generate.js +93 -7
  31. package/dist/commands/proxy/nginx/generate.js +98 -7
  32. package/dist/commands/revision/create.js +1 -1
  33. package/dist/commands/self/check.js +1 -1
  34. package/dist/commands/self/update.js +4 -4
  35. package/dist/commands/skills/check.js +4 -5
  36. package/dist/commands/skills/install.js +18 -1
  37. package/dist/commands/skills/update.js +19 -4
  38. package/dist/commands/source/dev.js +10 -6
  39. package/dist/commands/source/download.js +85 -16
  40. package/dist/lib/api-command-compat.js +51 -8
  41. package/dist/lib/app-managed-resources.js +104 -5
  42. package/dist/lib/auth-store.js +105 -13
  43. package/dist/lib/cli-config.js +93 -2
  44. package/dist/lib/docker-image.js +94 -6
  45. package/dist/lib/env-auth.js +291 -45
  46. package/dist/lib/env-config.js +14 -0
  47. package/dist/lib/env-proxy-config.js +48 -0
  48. package/dist/lib/env-proxy.js +276 -61
  49. package/dist/lib/hook-script.js +160 -0
  50. package/dist/lib/managed-init-env.js +6 -1
  51. package/dist/lib/portal-command-env.js +31 -0
  52. package/dist/lib/portal-config.js +133 -0
  53. package/dist/lib/portal-configure.js +117 -0
  54. package/dist/lib/portal-create.js +433 -0
  55. package/dist/lib/portal-deploy.js +283 -0
  56. package/dist/lib/portal-destroy.js +100 -0
  57. package/dist/lib/portal-dev.js +79 -0
  58. package/dist/lib/portal-env-files.js +53 -0
  59. package/dist/lib/portal-info.js +31 -0
  60. package/dist/lib/portal-list.js +211 -0
  61. package/dist/lib/portal-source.js +523 -0
  62. package/dist/lib/prompt-catalog-terminal.js +32 -19
  63. package/dist/lib/prompt-validators.js +1 -1
  64. package/dist/lib/prompt-web-ui.js +20 -13
  65. package/dist/lib/proxy-caddy.js +77 -9
  66. package/dist/lib/proxy-nginx.js +71 -11
  67. package/dist/lib/run-npm.js +21 -16
  68. package/dist/lib/self-manager.js +254 -46
  69. package/dist/lib/skills-manager.js +116 -23
  70. package/dist/lib/source-publish.js +2 -2
  71. package/dist/lib/startup-update.js +1 -1
  72. package/dist/lib/ui.js +28 -1
  73. package/dist/locale/en-US.json +227 -43
  74. package/dist/locale/zh-CN.json +227 -43
  75. package/package.json +11 -2
  76. package/scripts/build.mjs +0 -34
  77. package/scripts/clean.mjs +0 -9
  78. package/tsconfig.json +0 -19
@@ -11,7 +11,9 @@ import path from 'node:path';
11
11
  import { resolveAppPublicPath } from './app-public-path.js';
12
12
  import { resolveCliHomeDir, resolveConfiguredEnvPath, resolveEnvRelativePath } from './cli-home.js';
13
13
  import { normalizeCliLocale } from './cli-locale.js';
14
+ import { normalizeEnvProxyConfig, normalizeEnvProxyProviderConfig, } from './env-proxy-config.js';
14
15
  import { inferConfiguredAppPathFromLegacyConfig, resolveConfiguredAppPath, resolveConfiguredSourcePath, resolveConfiguredStoragePath, } from './env-paths.js';
16
+ import { ENV_CONFIG_SCHEMA_VERSION, normalizeEnvConfigSchemaVersion } from './env-config.js';
15
17
  import { cleanupCurrentSessionAfterEnvRemoval, resolveEffectiveCurrentEnv, setSessionCurrentEnv, } from './session-store.js';
16
18
  function normalizeStoredEnvKind(value) {
17
19
  const kind = String(value ?? '').trim();
@@ -76,14 +78,18 @@ function normalizeEnvConfigEntry(entry) {
76
78
  if (!entry) {
77
79
  return entry;
78
80
  }
79
- const { kind: _kind, apiBaseUrl: _apiBaseUrl, baseUrl: _baseUrl, apibaseUrl: _legacyApiBaseUrl, ...rest } = entry;
81
+ const { kind: _kind, apiBaseUrl: _apiBaseUrl, baseUrl: _baseUrl, apibaseUrl: _legacyApiBaseUrl, schemaVersion: _schemaVersion, ...rest } = entry;
80
82
  const normalizedKind = resolveEnvKind(entry);
81
83
  const apiBaseUrl = readEnvApiBaseUrl(entry);
84
+ const schemaVersion = normalizeEnvConfigSchemaVersion(entry.schemaVersion);
85
+ const proxy = normalizeEnvProxyConfig(entry.proxy);
82
86
  return {
83
87
  ...rest,
88
+ ...(schemaVersion ? { schemaVersion } : {}),
84
89
  ...(normalizedKind ? { kind: normalizedKind } : {}),
85
90
  ...(apiBaseUrl !== undefined ? { apiBaseUrl } : {}),
86
91
  ...(normalizeOptionalString(entry.appPublicPath) ? { appPublicPath: resolveAppPublicPath(entry.appPublicPath) } : {}),
92
+ ...(proxy ? { proxy } : {}),
87
93
  };
88
94
  }
89
95
  function normalizeAuthConfig(config) {
@@ -91,51 +97,68 @@ function normalizeAuthConfig(config) {
91
97
  const locale = normalizeOptionalCliLocale(settings.locale);
92
98
  const defaultUiHost = normalizeOptionalString(settings.init?.defaultUiHost);
93
99
  const defaultApiHost = normalizeOptionalString(settings.init?.defaultApiHost);
100
+ const defaultPortalTemplate = normalizeOptionalString(settings.init?.defaultPortalTemplate);
94
101
  const updatePolicy = normalizeOptionalCliUpdatePolicy(settings.update?.policy);
95
102
  const logRetentionDays = typeof settings.log?.retentionDays === 'number' && Number.isInteger(settings.log.retentionDays)
96
103
  ? settings.log.retentionDays
97
104
  : undefined;
98
105
  const logEnabled = typeof settings.log?.enabled === 'boolean' ? settings.log.enabled : undefined;
106
+ const hasBinSettings = settings.bin?.docker ||
107
+ settings.bin?.caddy ||
108
+ settings.bin?.git ||
109
+ settings.bin?.nginx ||
110
+ settings.bin?.pnpm ||
111
+ settings.bin?.yarn;
112
+ const hasProxySettings = settings.proxy?.nbCliRoot ||
113
+ settings.proxy?.caddyDriver ||
114
+ settings.proxy?.nginxDriver ||
115
+ settings.proxy?.upstreamHost ||
116
+ settings.proxy?.host;
99
117
  return {
100
118
  name: config.name || config.dockerResourcePrefix,
101
119
  settings: {
102
120
  ...(locale ? { locale } : {}),
103
- ...(defaultUiHost || defaultApiHost
121
+ ...(defaultUiHost || defaultApiHost || defaultPortalTemplate
104
122
  ? {
105
123
  init: {
106
124
  ...(defaultUiHost ? { defaultUiHost } : {}),
107
125
  ...(defaultApiHost ? { defaultApiHost } : {}),
126
+ ...(defaultPortalTemplate ? { defaultPortalTemplate } : {}),
108
127
  },
109
128
  }
110
129
  : {}),
111
130
  ...(updatePolicy ? { update: { policy: updatePolicy } } : {}),
112
131
  ...(settings.license?.pkgUrl ? { license: { pkgUrl: normalizeOptionalString(settings.license.pkgUrl) } } : {}),
113
132
  ...(settings.docker?.network || settings.docker?.containerPrefix
133
+ || settings.docker?.nbImageRegistry || settings.docker?.nbImageVariant
114
134
  ? {
115
135
  docker: {
116
136
  ...(settings.docker?.network ? { network: normalizeOptionalString(settings.docker.network) } : {}),
117
137
  ...(settings.docker?.containerPrefix
118
138
  ? { containerPrefix: normalizeOptionalString(settings.docker.containerPrefix) }
119
139
  : {}),
140
+ ...(settings.docker?.nbImageRegistry
141
+ ? { nbImageRegistry: normalizeOptionalString(settings.docker.nbImageRegistry) }
142
+ : {}),
143
+ ...(settings.docker?.nbImageVariant
144
+ ? { nbImageVariant: normalizeOptionalString(settings.docker.nbImageVariant) }
145
+ : {}),
120
146
  },
121
147
  }
122
148
  : {}),
123
- ...(settings.bin?.docker || settings.bin?.caddy || settings.bin?.git || settings.bin?.nginx || settings.bin?.yarn
149
+ ...(hasBinSettings
124
150
  ? {
125
151
  bin: {
126
152
  ...(settings.bin?.docker ? { docker: normalizeOptionalString(settings.bin.docker) } : {}),
127
153
  ...(settings.bin?.caddy ? { caddy: normalizeOptionalString(settings.bin.caddy) } : {}),
128
154
  ...(settings.bin?.git ? { git: normalizeOptionalString(settings.bin.git) } : {}),
129
155
  ...(settings.bin?.nginx ? { nginx: normalizeOptionalString(settings.bin.nginx) } : {}),
156
+ ...(settings.bin?.pnpm ? { pnpm: normalizeOptionalString(settings.bin.pnpm) } : {}),
130
157
  ...(settings.bin?.yarn ? { yarn: normalizeOptionalString(settings.bin.yarn) } : {}),
131
158
  },
132
159
  }
133
160
  : {}),
134
- ...(settings.proxy?.nbCliRoot ||
135
- settings.proxy?.caddyDriver ||
136
- settings.proxy?.nginxDriver ||
137
- settings.proxy?.upstreamHost ||
138
- settings.proxy?.host
161
+ ...(hasProxySettings
139
162
  ? {
140
163
  proxy: {
141
164
  ...(settings.proxy?.nbCliRoot ? { nbCliRoot: normalizeOptionalString(settings.proxy.nbCliRoot) } : {}),
@@ -366,7 +389,13 @@ function areAuthConfigsEquivalent(left, right) {
366
389
  async function writeEnv(envName, updater, options = {}) {
367
390
  const config = await loadExactAuthConfig(options);
368
391
  const previous = config.envs[envName];
369
- config.envs[envName] = updater(previous);
392
+ const next = updater(previous);
393
+ config.envs[envName] = {
394
+ ...next,
395
+ schemaVersion: normalizeEnvConfigSchemaVersion(next.schemaVersion) ??
396
+ normalizeEnvConfigSchemaVersion(previous?.schemaVersion) ??
397
+ ENV_CONFIG_SCHEMA_VERSION,
398
+ };
370
399
  await saveAuthConfig(config, options);
371
400
  }
372
401
  function normalizeConfiguredAuthType(value) {
@@ -377,18 +406,19 @@ export function resolveConfiguredAuthType(config) {
377
406
  }
378
407
  export async function upsertEnv(envName, config, options = {}) {
379
408
  await writeEnv(envName, (previous) => {
380
- const { apiBaseUrl: _apiBaseUrl, baseUrl: _baseUrl, apibaseUrl: _legacyApiBaseUrl, accessToken, authType, authUsername, ...rest } = config;
409
+ const { apiBaseUrl: _apiBaseUrl, baseUrl: _baseUrl, apibaseUrl: _legacyApiBaseUrl, accessToken, authType, authUsername, schemaVersion, ...rest } = config;
381
410
  const nextApiBaseUrl = readEnvApiBaseUrl(config);
382
411
  const previousApiBaseUrl = readEnvApiBaseUrl(previous);
383
412
  const baseUrlChanged = previousApiBaseUrl !== nextApiBaseUrl;
384
413
  const previousAuthType = resolveConfiguredAuthType(previous);
385
414
  const requestedAuthType = normalizeConfiguredAuthType(authType);
386
- const nextAuthType = requestedAuthType ?? (accessToken ? 'token' : previousAuthType);
415
+ const nextAccessToken = normalizeOptionalString(accessToken);
416
+ const nextAuthType = requestedAuthType ?? (nextAccessToken ? 'token' : previousAuthType);
387
417
  const nextAuthUsername = nextAuthType === 'basic' ? normalizeOptionalString(authUsername) ?? previous?.authUsername : undefined;
388
- const nextAuth = accessToken
418
+ const nextAuth = nextAccessToken
389
419
  ? {
390
420
  type: 'token',
391
- accessToken,
421
+ accessToken: nextAccessToken,
392
422
  }
393
423
  : nextAuthType === 'oauth' && !baseUrlChanged && previous?.auth?.type === 'oauth'
394
424
  ? previous.auth
@@ -396,6 +426,9 @@ export async function upsertEnv(envName, config, options = {}) {
396
426
  const authChanged = !areAuthConfigsEquivalent(previous?.auth, nextAuth);
397
427
  const authTypeChanged = previousAuthType !== nextAuthType;
398
428
  const authUsernameChanged = previous?.authUsername !== nextAuthUsername;
429
+ const nextSchemaVersion = normalizeEnvConfigSchemaVersion(schemaVersion) ??
430
+ normalizeEnvConfigSchemaVersion(previous?.schemaVersion) ??
431
+ ENV_CONFIG_SCHEMA_VERSION;
399
432
  return {
400
433
  ...previous,
401
434
  apiBaseUrl: nextApiBaseUrl,
@@ -403,6 +436,7 @@ export async function upsertEnv(envName, config, options = {}) {
403
436
  authUsername: nextAuthUsername,
404
437
  auth: nextAuth,
405
438
  ...rest,
439
+ schemaVersion: nextSchemaVersion,
406
440
  runtime: baseUrlChanged || authChanged || authTypeChanged || authUsernameChanged ? undefined : previous?.runtime,
407
441
  };
408
442
  }, options);
@@ -487,6 +521,64 @@ export async function setEnvRuntime(envName, runtime, options = {}) {
487
521
  };
488
522
  await saveAuthConfig(config, options);
489
523
  }
524
+ export function resolveEnvProxyEntry(config, provider) {
525
+ const proxy = normalizeEnvProxyConfig(config?.proxy);
526
+ const resolved = {
527
+ ...(proxy?.host ? { host: proxy.host } : {}),
528
+ ...(proxy?.port !== undefined ? { port: proxy.port } : {}),
529
+ ...((provider === 'nginx' ? proxy?.nginx : proxy?.caddy) ?? {}),
530
+ };
531
+ return Object.keys(resolved).length > 0 ? resolved : undefined;
532
+ }
533
+ export async function setEnvProxyEntry(envName, provider, entry, options = {}) {
534
+ await writeEnv(envName, (previous) => {
535
+ const currentProxy = normalizeEnvProxyConfig(previous?.proxy) ?? {};
536
+ const nextEntry = normalizeEnvProxyProviderConfig(entry);
537
+ const nextProxy = { ...currentProxy };
538
+ if (nextEntry && 'host' in nextEntry) {
539
+ const host = normalizeOptionalString(nextEntry.host);
540
+ if (host) {
541
+ nextProxy.host = host;
542
+ }
543
+ else {
544
+ delete nextProxy.host;
545
+ }
546
+ }
547
+ if (nextEntry && 'port' in nextEntry) {
548
+ const portValue = nextEntry.port;
549
+ const port = typeof portValue === 'number' && Number.isInteger(portValue) && portValue >= 1 && portValue <= 65535
550
+ ? portValue
551
+ : undefined;
552
+ if (port !== undefined) {
553
+ nextProxy.port = port;
554
+ }
555
+ else {
556
+ delete nextProxy.port;
557
+ }
558
+ }
559
+ const providerConfig = nextEntry && Object.keys(nextEntry).some((key) => key !== 'host' && key !== 'port')
560
+ ? Object.fromEntries(Object.entries(nextEntry).filter(([key]) => key !== 'host' && key !== 'port'))
561
+ : undefined;
562
+ if (provider === 'nginx') {
563
+ if (providerConfig && Object.keys(providerConfig).length > 0) {
564
+ nextProxy.nginx = providerConfig;
565
+ }
566
+ else {
567
+ delete nextProxy.nginx;
568
+ }
569
+ }
570
+ else if (providerConfig && Object.keys(providerConfig).length > 0) {
571
+ nextProxy.caddy = providerConfig;
572
+ }
573
+ else {
574
+ delete nextProxy.caddy;
575
+ }
576
+ return {
577
+ ...previous,
578
+ proxy: nextProxy,
579
+ };
580
+ }, options);
581
+ }
490
582
  export async function clearEnvRootSetup(envName, options = {}) {
491
583
  const config = await loadExactAuthConfig(options);
492
584
  const current = config.envs[envName];
@@ -9,6 +9,7 @@
9
9
  import { loadExactAuthConfig, saveAuthConfig } from './auth-store.js';
10
10
  import { resolveCliHomeRoot, resolveDefaultConfigScope } from './cli-home.js';
11
11
  import { CLI_LOCALE_FLAG_OPTIONS, normalizeCliLocale, resolveCliLocale } from './cli-locale.js';
12
+ import { DEFAULT_NB_IMAGE_REGISTRY, DEFAULT_NB_IMAGE_VARIANT, NB_IMAGE_REGISTRY_OPTIONS, NB_IMAGE_VARIANT_OPTIONS, normalizeNbImageRegistry, normalizeNbImageVariant, } from './docker-image.js';
12
13
  export const DEFAULT_LICENSE_PKG_URL = 'https://pkg.nocobase.com/';
13
14
  export const DEFAULT_DOCKER_NETWORK = 'nocobase';
14
15
  export const DEFAULT_DOCKER_CONTAINER_PREFIX = 'nb';
@@ -16,6 +17,7 @@ export const DEFAULT_DOCKER_BIN = 'docker';
16
17
  export const DEFAULT_CADDY_BIN = 'caddy';
17
18
  export const DEFAULT_GIT_BIN = 'git';
18
19
  export const DEFAULT_NGINX_BIN = 'nginx';
20
+ export const DEFAULT_PNPM_BIN = 'pnpm';
19
21
  export const PROXY_PROVIDER_OPTIONS = ['nginx', 'caddy'];
20
22
  export const DEFAULT_PROXY_PROVIDER = 'nginx';
21
23
  export const NGINX_PROXY_DRIVER_OPTIONS = ['local', 'docker'];
@@ -32,14 +34,18 @@ export const SUPPORTED_CLI_CONFIG_KEYS = [
32
34
  'locale',
33
35
  'default-ui-host',
34
36
  'default-api-host',
37
+ 'default-portal-template',
35
38
  'update.policy',
36
39
  'license.pkg-url',
37
40
  'docker.network',
38
41
  'docker.container-prefix',
42
+ 'nb-image-registry',
43
+ 'nb-image-variant',
39
44
  'bin.docker',
40
45
  'bin.caddy',
41
46
  'bin.git',
42
47
  'bin.nginx',
48
+ 'bin.pnpm',
43
49
  'proxy.nb-cli-root',
44
50
  'proxy.caddy-driver',
45
51
  'proxy.nginx-driver',
@@ -115,7 +121,10 @@ function pruneSettings(config) {
115
121
  delete config.settings.locale;
116
122
  }
117
123
  const init = config.settings?.init;
118
- if (init && !trimValue(init.defaultUiHost) && !trimValue(init.defaultApiHost)) {
124
+ if (init &&
125
+ !trimValue(init.defaultUiHost) &&
126
+ !trimValue(init.defaultApiHost) &&
127
+ !trimValue(init.defaultPortalTemplate)) {
119
128
  delete config.settings?.init;
120
129
  }
121
130
  const update = config.settings?.update;
@@ -127,7 +136,11 @@ function pruneSettings(config) {
127
136
  delete config.settings?.license;
128
137
  }
129
138
  const docker = config.settings?.docker;
130
- if (docker && !trimValue(docker.network) && !trimValue(docker.containerPrefix)) {
139
+ if (docker &&
140
+ !trimValue(docker.network) &&
141
+ !trimValue(docker.containerPrefix) &&
142
+ !trimValue(docker.nbImageRegistry) &&
143
+ !trimValue(docker.nbImageVariant)) {
131
144
  delete config.settings?.docker;
132
145
  }
133
146
  const bin = config.settings?.bin;
@@ -136,6 +149,7 @@ function pruneSettings(config) {
136
149
  !trimValue(bin.caddy) &&
137
150
  !trimValue(bin.git) &&
138
151
  !trimValue(bin.nginx) &&
152
+ !trimValue(bin.pnpm) &&
139
153
  !trimValue(bin.yarn)) {
140
154
  delete config.settings?.bin;
141
155
  }
@@ -171,6 +185,8 @@ export function getExplicitCliConfigValue(config, key) {
171
185
  return trimValue(config.settings?.init?.defaultUiHost);
172
186
  case 'default-api-host':
173
187
  return trimValue(config.settings?.init?.defaultApiHost);
188
+ case 'default-portal-template':
189
+ return trimValue(config.settings?.init?.defaultPortalTemplate);
174
190
  case 'update.policy':
175
191
  return normalizeCliUpdatePolicy(config.settings?.update?.policy);
176
192
  case 'license.pkg-url':
@@ -179,6 +195,10 @@ export function getExplicitCliConfigValue(config, key) {
179
195
  return trimValue(config.settings?.docker?.network);
180
196
  case 'docker.container-prefix':
181
197
  return trimValue(config.settings?.docker?.containerPrefix);
198
+ case 'nb-image-registry':
199
+ return normalizeNbImageRegistry(config.settings?.docker?.nbImageRegistry);
200
+ case 'nb-image-variant':
201
+ return normalizeNbImageVariant(config.settings?.docker?.nbImageVariant);
182
202
  case 'bin.docker':
183
203
  return trimValue(config.settings?.bin?.docker);
184
204
  case 'bin.caddy':
@@ -187,6 +207,8 @@ export function getExplicitCliConfigValue(config, key) {
187
207
  return trimValue(config.settings?.bin?.git);
188
208
  case 'bin.nginx':
189
209
  return trimValue(config.settings?.bin?.nginx);
210
+ case 'bin.pnpm':
211
+ return trimValue(config.settings?.bin?.pnpm);
190
212
  case 'proxy.nb-cli-root':
191
213
  return trimValue(config.settings?.proxy?.nbCliRoot);
192
214
  case 'proxy.caddy-driver':
@@ -217,6 +239,8 @@ export function getEffectiveCliConfigValue(config, key) {
217
239
  return '127.0.0.1';
218
240
  case 'default-api-host':
219
241
  return '127.0.0.1';
242
+ case 'default-portal-template':
243
+ return explicit ?? '';
220
244
  case 'update.policy':
221
245
  return explicit ?? DEFAULT_UPDATE_POLICY;
222
246
  case 'license.pkg-url':
@@ -225,6 +249,12 @@ export function getEffectiveCliConfigValue(config, key) {
225
249
  return trimValue(config.name) || DEFAULT_DOCKER_NETWORK;
226
250
  case 'docker.container-prefix':
227
251
  return trimValue(config.name) || DEFAULT_DOCKER_CONTAINER_PREFIX;
252
+ case 'nb-image-registry':
253
+ return explicit ?? (resolveCliLocale(undefined, { configuredLocale: trimValue(config.settings?.locale) }) === 'zh-CN'
254
+ ? 'aliyun'
255
+ : DEFAULT_NB_IMAGE_REGISTRY);
256
+ case 'nb-image-variant':
257
+ return explicit ?? DEFAULT_NB_IMAGE_VARIANT;
228
258
  case 'bin.docker':
229
259
  return DEFAULT_DOCKER_BIN;
230
260
  case 'bin.caddy':
@@ -233,6 +263,8 @@ export function getEffectiveCliConfigValue(config, key) {
233
263
  return DEFAULT_GIT_BIN;
234
264
  case 'bin.nginx':
235
265
  return DEFAULT_NGINX_BIN;
266
+ case 'bin.pnpm':
267
+ return DEFAULT_PNPM_BIN;
236
268
  case 'proxy.nb-cli-root':
237
269
  return explicit ?? resolveCliHomeRoot();
238
270
  case 'proxy.caddy-driver':
@@ -298,6 +330,20 @@ export function normalizeCliConfigValue(key, value) {
298
330
  }
299
331
  return driver;
300
332
  }
333
+ if (key === 'nb-image-registry') {
334
+ const registry = normalizeNbImageRegistry(normalized);
335
+ if (!registry) {
336
+ throw new Error(`Config key "${key}" must be one of: ${NB_IMAGE_REGISTRY_OPTIONS.join(', ')}`);
337
+ }
338
+ return registry;
339
+ }
340
+ if (key === 'nb-image-variant') {
341
+ const variant = normalizeNbImageVariant(normalized);
342
+ if (!variant) {
343
+ throw new Error(`Config key "${key}" must be one of: ${NB_IMAGE_VARIANT_OPTIONS.join(', ')}`);
344
+ }
345
+ return variant;
346
+ }
301
347
  return normalized;
302
348
  }
303
349
  export async function loadCliConfig(options = {}) {
@@ -339,6 +385,12 @@ export async function setCliConfigValue(key, value, options = {}) {
339
385
  defaultApiHost: normalized,
340
386
  };
341
387
  break;
388
+ case 'default-portal-template':
389
+ config.settings.init = {
390
+ ...(config.settings.init ?? {}),
391
+ defaultPortalTemplate: normalized,
392
+ };
393
+ break;
342
394
  case 'update.policy':
343
395
  config.settings.update = {
344
396
  ...(config.settings.update ?? {}),
@@ -363,6 +415,18 @@ export async function setCliConfigValue(key, value, options = {}) {
363
415
  containerPrefix: normalized,
364
416
  };
365
417
  break;
418
+ case 'nb-image-registry':
419
+ config.settings.docker = {
420
+ ...(config.settings.docker ?? {}),
421
+ nbImageRegistry: normalized,
422
+ };
423
+ break;
424
+ case 'nb-image-variant':
425
+ config.settings.docker = {
426
+ ...(config.settings.docker ?? {}),
427
+ nbImageVariant: normalized,
428
+ };
429
+ break;
366
430
  case 'bin.docker':
367
431
  config.settings.bin = {
368
432
  ...(config.settings.bin ?? {}),
@@ -387,6 +451,12 @@ export async function setCliConfigValue(key, value, options = {}) {
387
451
  nginx: normalized,
388
452
  };
389
453
  break;
454
+ case 'bin.pnpm':
455
+ config.settings.bin = {
456
+ ...(config.settings.bin ?? {}),
457
+ pnpm: normalized,
458
+ };
459
+ break;
390
460
  case 'proxy.nb-cli-root':
391
461
  config.settings.proxy = {
392
462
  ...(config.settings.proxy ?? {}),
@@ -456,6 +526,11 @@ export async function deleteCliConfigValue(key, options = {}) {
456
526
  delete config.settings.init.defaultApiHost;
457
527
  }
458
528
  break;
529
+ case 'default-portal-template':
530
+ if (config.settings.init) {
531
+ delete config.settings.init.defaultPortalTemplate;
532
+ }
533
+ break;
459
534
  case 'update.policy':
460
535
  if (config.settings.update) {
461
536
  delete config.settings.update.policy;
@@ -476,6 +551,16 @@ export async function deleteCliConfigValue(key, options = {}) {
476
551
  delete config.settings.docker.containerPrefix;
477
552
  }
478
553
  break;
554
+ case 'nb-image-registry':
555
+ if (config.settings.docker) {
556
+ delete config.settings.docker.nbImageRegistry;
557
+ }
558
+ break;
559
+ case 'nb-image-variant':
560
+ if (config.settings.docker) {
561
+ delete config.settings.docker.nbImageVariant;
562
+ }
563
+ break;
479
564
  case 'bin.docker':
480
565
  if (config.settings.bin) {
481
566
  delete config.settings.bin.docker;
@@ -496,6 +581,11 @@ export async function deleteCliConfigValue(key, options = {}) {
496
581
  delete config.settings.bin.nginx;
497
582
  }
498
583
  break;
584
+ case 'bin.pnpm':
585
+ if (config.settings.bin) {
586
+ delete config.settings.bin.pnpm;
587
+ }
588
+ break;
499
589
  case 'proxy.nb-cli-root':
500
590
  if (config.settings.proxy) {
501
591
  delete config.settings.proxy.nbCliRoot;
@@ -556,6 +646,7 @@ const CONFIGURABLE_COMMAND_KEYS = {
556
646
  caddy: 'bin.caddy',
557
647
  git: 'bin.git',
558
648
  nginx: 'bin.nginx',
649
+ pnpm: 'bin.pnpm',
559
650
  yarn: 'bin.yarn',
560
651
  };
561
652
  export function isConfigurableCommandName(value) {
@@ -9,7 +9,30 @@
9
9
  export const DEFAULT_DOCKER_REGISTRY = 'nocobase/nocobase';
10
10
  export const DEFAULT_DOCKER_REGISTRY_ZH_CN = 'registry.cn-shanghai.aliyuncs.com/nocobase/nocobase';
11
11
  export const DEFAULT_DOCKER_VERSION = 'alpha';
12
+ export const NB_IMAGE_REGISTRY_OPTIONS = ['dockerhub', 'aliyun'];
13
+ export const DEFAULT_NB_IMAGE_REGISTRY = 'dockerhub';
14
+ export const NB_IMAGE_VARIANT_OPTIONS = ['standard', 'no-nginx', 'full', 'full-no-nginx'];
15
+ export const DEFAULT_NB_IMAGE_VARIANT = 'full';
12
16
  export const DOCKER_IMAGE_FULL_SUFFIX = '-full';
17
+ export const DOCKER_IMAGE_NO_NGINX_SUFFIX = '-no-nginx';
18
+ export const DOCKER_IMAGE_FULL_NO_NGINX_SUFFIX = '-full-no-nginx';
19
+ export const DEFAULT_KINGBASE_IMAGE = 'registry.cn-shanghai.aliyuncs.com/nocobase/kingbase:v009r001c001b0030_single_x86';
20
+ const OFFICIAL_DOCKER_REGISTRY_REPOSITORIES = {
21
+ dockerhub: DEFAULT_DOCKER_REGISTRY,
22
+ aliyun: DEFAULT_DOCKER_REGISTRY_ZH_CN,
23
+ };
24
+ const DEFAULT_BUILTIN_DB_IMAGES = {
25
+ postgres: 'postgres:16',
26
+ mysql: 'mysql:8',
27
+ mariadb: 'mariadb:11',
28
+ kingbase: DEFAULT_KINGBASE_IMAGE,
29
+ };
30
+ const ALIYUN_BUILTIN_DB_IMAGES = {
31
+ postgres: 'registry.cn-shanghai.aliyuncs.com/nocobase/postgres:16',
32
+ mysql: 'registry.cn-shanghai.aliyuncs.com/nocobase/mysql:8',
33
+ mariadb: 'registry.cn-shanghai.aliyuncs.com/nocobase/mariadb:11',
34
+ kingbase: DEFAULT_KINGBASE_IMAGE,
35
+ };
13
36
  const OFFICIAL_FULL_IMAGE_REGISTRIES = new Set([
14
37
  DEFAULT_DOCKER_REGISTRY,
15
38
  DEFAULT_DOCKER_REGISTRY_ZH_CN,
@@ -17,21 +40,86 @@ const OFFICIAL_FULL_IMAGE_REGISTRIES = new Set([
17
40
  function trimValue(value) {
18
41
  return String(value ?? '').trim();
19
42
  }
43
+ export function normalizeNbImageRegistry(value) {
44
+ const normalized = trimValue(value);
45
+ if (!normalized) {
46
+ return undefined;
47
+ }
48
+ return NB_IMAGE_REGISTRY_OPTIONS.includes(normalized)
49
+ ? normalized
50
+ : undefined;
51
+ }
52
+ export function normalizeNbImageVariant(value) {
53
+ const normalized = trimValue(value);
54
+ if (!normalized) {
55
+ return undefined;
56
+ }
57
+ return NB_IMAGE_VARIANT_OPTIONS.includes(normalized)
58
+ ? normalized
59
+ : undefined;
60
+ }
61
+ export function resolveOfficialDockerRegistry(value) {
62
+ const registry = normalizeNbImageRegistry(value) ?? DEFAULT_NB_IMAGE_REGISTRY;
63
+ return OFFICIAL_DOCKER_REGISTRY_REPOSITORIES[registry];
64
+ }
65
+ export function inferNbImageRegistryFromRepository(value) {
66
+ const repository = trimValue(value);
67
+ return Object.entries(OFFICIAL_DOCKER_REGISTRY_REPOSITORIES).find(([, candidate]) => candidate === repository)?.[0];
68
+ }
69
+ function hasKnownVariantSuffix(tag) {
70
+ return (tag.endsWith(DOCKER_IMAGE_FULL_NO_NGINX_SUFFIX) ||
71
+ tag.endsWith(DOCKER_IMAGE_NO_NGINX_SUFFIX) ||
72
+ tag.endsWith(DOCKER_IMAGE_FULL_SUFFIX));
73
+ }
20
74
  export function shouldUseFullDockerImageTag(registry) {
21
75
  return OFFICIAL_FULL_IMAGE_REGISTRIES.has(trimValue(registry));
22
76
  }
23
- export function normalizeDockerImageTag(registry, version) {
77
+ export function normalizeDockerImageTag(registry, version, options) {
24
78
  const tag = trimValue(version) || DEFAULT_DOCKER_VERSION;
25
- if (!shouldUseFullDockerImageTag(registry)) {
79
+ if (hasKnownVariantSuffix(tag)) {
26
80
  return tag;
27
81
  }
28
- return tag.endsWith(DOCKER_IMAGE_FULL_SUFFIX)
29
- ? tag
30
- : `${tag}${DOCKER_IMAGE_FULL_SUFFIX}`;
82
+ const explicitVariant = normalizeNbImageVariant(options?.variant) ?? normalizeNbImageVariant(options?.defaultVariant);
83
+ const inferredVariant = shouldUseFullDockerImageTag(registry) ? DEFAULT_NB_IMAGE_VARIANT : 'standard';
84
+ const variant = explicitVariant ?? inferredVariant;
85
+ switch (variant) {
86
+ case 'standard':
87
+ return tag;
88
+ case 'no-nginx':
89
+ return `${tag}${DOCKER_IMAGE_NO_NGINX_SUFFIX}`;
90
+ case 'full':
91
+ return `${tag}${DOCKER_IMAGE_FULL_SUFFIX}`;
92
+ case 'full-no-nginx':
93
+ return `${tag}${DOCKER_IMAGE_FULL_NO_NGINX_SUFFIX}`;
94
+ }
31
95
  }
32
96
  export function resolveDockerImageRef(registry, version, options) {
33
97
  const resolvedRegistry = trimValue(registry) || options?.defaultRegistry || DEFAULT_DOCKER_REGISTRY;
34
98
  const rawVersion = trimValue(version) || options?.defaultVersion || DEFAULT_DOCKER_VERSION;
35
- const normalizedTag = normalizeDockerImageTag(resolvedRegistry, rawVersion);
99
+ const normalizedTag = normalizeDockerImageTag(resolvedRegistry, rawVersion, {
100
+ variant: options?.variant,
101
+ defaultVariant: options?.defaultVariant,
102
+ });
36
103
  return `${resolvedRegistry}:${normalizedTag}`;
37
104
  }
105
+ function extractDockerImageTag(imageRef) {
106
+ const ref = trimValue(imageRef);
107
+ const lastSlashIndex = ref.lastIndexOf('/');
108
+ const lastColonIndex = ref.lastIndexOf(':');
109
+ if (lastColonIndex <= lastSlashIndex) {
110
+ return undefined;
111
+ }
112
+ return ref.slice(lastColonIndex + 1) || undefined;
113
+ }
114
+ export function resolveDockerImageContainerPort(imageRef) {
115
+ const tag = extractDockerImageTag(imageRef);
116
+ return tag?.endsWith(DOCKER_IMAGE_NO_NGINX_SUFFIX) ? '13000' : '80';
117
+ }
118
+ export function resolveBuiltinDbImage(dbDialect, options) {
119
+ const dialect = trimValue(dbDialect) || 'postgres';
120
+ const configuredRegistry = normalizeNbImageRegistry(options?.registry) ??
121
+ inferNbImageRegistryFromRepository(options?.registry) ??
122
+ DEFAULT_NB_IMAGE_REGISTRY;
123
+ const defaults = configuredRegistry === 'aliyun' ? ALIYUN_BUILTIN_DB_IMAGES : DEFAULT_BUILTIN_DB_IMAGES;
124
+ return defaults[dialect] ?? defaults.postgres;
125
+ }