@nocobase/cli 2.1.0-beta.46 → 2.1.0-beta.48

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 (48) hide show
  1. package/bin/run.js +2 -1
  2. package/dist/commands/app/destroy.js +3 -3
  3. package/dist/commands/app/restart.js +1 -2
  4. package/dist/commands/app/start.js +28 -3
  5. package/dist/commands/app/upgrade.js +1 -1
  6. package/dist/commands/env/update.js +11 -1
  7. package/dist/commands/init.js +44 -76
  8. package/dist/commands/install.js +68 -50
  9. package/dist/commands/license/activate.js +1 -1
  10. package/dist/commands/proxy/caddy/current.js +17 -0
  11. package/dist/commands/proxy/caddy/generate.js +69 -0
  12. package/dist/commands/proxy/caddy/index.js +28 -0
  13. package/dist/commands/proxy/caddy/info.js +31 -0
  14. package/dist/commands/proxy/caddy/reload.js +30 -0
  15. package/dist/commands/proxy/caddy/restart.js +28 -0
  16. package/dist/commands/proxy/caddy/start.js +30 -0
  17. package/dist/commands/proxy/caddy/status.js +19 -0
  18. package/dist/commands/proxy/caddy/stop.js +30 -0
  19. package/dist/commands/proxy/caddy/use.js +26 -0
  20. package/dist/commands/proxy/index.js +28 -0
  21. package/dist/commands/proxy/nginx/current.js +18 -0
  22. package/dist/commands/proxy/nginx/generate.js +68 -0
  23. package/dist/commands/proxy/nginx/index.js +28 -0
  24. package/dist/commands/proxy/nginx/info.js +34 -0
  25. package/dist/commands/proxy/nginx/reload.js +30 -0
  26. package/dist/commands/proxy/nginx/restart.js +28 -0
  27. package/dist/commands/proxy/nginx/start.js +30 -0
  28. package/dist/commands/proxy/nginx/status.js +19 -0
  29. package/dist/commands/proxy/nginx/stop.js +30 -0
  30. package/dist/commands/proxy/nginx/use.js +31 -0
  31. package/dist/commands/revision/create.js +31 -2
  32. package/dist/lib/app-managed-resources.js +7 -2
  33. package/dist/lib/auth-store.js +8 -0
  34. package/dist/lib/cli-config.js +73 -1
  35. package/dist/lib/env-config.js +8 -0
  36. package/dist/lib/env-proxy.js +105 -75
  37. package/dist/lib/managed-env-file.js +7 -4
  38. package/dist/lib/managed-init-env.js +32 -0
  39. package/dist/lib/prompt-catalog-terminal.js +1 -2
  40. package/dist/lib/prompt-validators.js +6 -0
  41. package/dist/lib/proxy-caddy.js +274 -0
  42. package/dist/lib/proxy-nginx.js +330 -0
  43. package/dist/locale/en-US.json +4 -3
  44. package/dist/locale/zh-CN.json +4 -3
  45. package/package.json +2 -2
  46. package/dist/commands/env/proxy/caddy.js +0 -28
  47. package/dist/commands/env/proxy/index.js +0 -353
  48. package/dist/commands/env/proxy/nginx.js +0 -28
@@ -0,0 +1,31 @@
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
+ */
9
+ import { Args, Command } from '@oclif/core';
10
+ import { NGINX_PROXY_DRIVER_OPTIONS, normalizeNginxProxyDriver } from '../../../lib/cli-config.js';
11
+ import { setNginxProxyDriver } from '../../../lib/proxy-nginx.js';
12
+ export default class ProxyNginxUse extends Command {
13
+ static summary = 'Choose whether nginx runs locally or in Docker';
14
+ static examples = ['<%= config.bin %> proxy nginx use local', '<%= config.bin %> proxy nginx use docker'];
15
+ static args = {
16
+ driver: Args.string({
17
+ description: 'Nginx runtime driver',
18
+ required: true,
19
+ options: [...NGINX_PROXY_DRIVER_OPTIONS],
20
+ }),
21
+ };
22
+ async run() {
23
+ const { args } = await this.parse(ProxyNginxUse);
24
+ const driver = normalizeNginxProxyDriver(args.driver);
25
+ if (!driver) {
26
+ this.error(`Unsupported nginx driver "${args.driver}". Use one of: ${NGINX_PROXY_DRIVER_OPTIONS.join(', ')}`);
27
+ }
28
+ const saved = await setNginxProxyDriver(driver);
29
+ this.log(saved);
30
+ }
31
+ }
@@ -9,6 +9,17 @@
9
9
  import { Args, Command, Flags } from '@oclif/core';
10
10
  import { executeRawApiRequest } from '../../lib/api-client.js';
11
11
  import { ensureCrossEnvConfirmed } from '../../lib/env-guard.js';
12
+ const VERSION_CONTROL_PLUGIN_PACKAGE = '@nocobase/plugin-version-control';
13
+ function isRecord(value) {
14
+ return typeof value === 'object' && value !== null;
15
+ }
16
+ function extractPluginList(data) {
17
+ const list = Array.isArray(data) ? data : isRecord(data) && Array.isArray(data.data) ? data.data : [];
18
+ return list.filter(isRecord).map((item) => ({
19
+ packageName: typeof item.packageName === 'string' ? item.packageName : undefined,
20
+ enabled: typeof item.enabled === 'boolean' ? item.enabled : undefined,
21
+ }));
22
+ }
12
23
  export default class RevisionCreate extends Command {
13
24
  static summary = 'Save the current NocoBase build as a restorable revision';
14
25
  static examples = [
@@ -44,7 +55,7 @@ export default class RevisionCreate extends Command {
44
55
  'json-output': Flags.boolean({
45
56
  char: 'j',
46
57
  description: 'Print raw JSON response',
47
- default: true,
58
+ default: false,
48
59
  allowNo: true,
49
60
  }),
50
61
  };
@@ -62,6 +73,24 @@ export default class RevisionCreate extends Command {
62
73
  if (!confirmed) {
63
74
  return;
64
75
  }
76
+ const pluginListResponse = await executeRawApiRequest({
77
+ envName: flags.env,
78
+ baseUrl: flags['api-base-url'],
79
+ role: flags.role,
80
+ token: flags.token,
81
+ method: 'GET',
82
+ path: '/pm:list',
83
+ query: {
84
+ mode: 'summary',
85
+ },
86
+ });
87
+ if (!pluginListResponse.ok) {
88
+ this.error(`Failed to check plugin status with status ${pluginListResponse.status}\n${JSON.stringify(pluginListResponse.data, null, 2)}`);
89
+ }
90
+ const versionControlPlugin = extractPluginList(pluginListResponse.data).find((plugin) => plugin.packageName === VERSION_CONTROL_PLUGIN_PACKAGE);
91
+ if (!versionControlPlugin?.enabled) {
92
+ this.error(`The ${VERSION_CONTROL_PLUGIN_PACKAGE} plugin is not enabled. Enable it first with \`nb plugin enable ${VERSION_CONTROL_PLUGIN_PACKAGE}\`.`);
93
+ }
65
94
  const response = await executeRawApiRequest({
66
95
  envName: flags.env,
67
96
  baseUrl: flags['api-base-url'],
@@ -84,6 +113,6 @@ export default class RevisionCreate extends Command {
84
113
  this.log(JSON.stringify(response.data, null, 2));
85
114
  return;
86
115
  }
87
- this.log(`HTTP ${response.status}`);
116
+ this.log('Revision created successfully');
88
117
  }
89
118
  }
@@ -114,7 +114,7 @@ async function localProjectHasFiles(projectRoot) {
114
114
  return false;
115
115
  }
116
116
  }
117
- export async function buildSavedDockerRunArgs(runtime) {
117
+ export async function buildSavedDockerRunArgs(runtime, options) {
118
118
  const config = runtime.env.config ?? {};
119
119
  const storagePath = trimValue(resolveConfiguredStoragePath(config));
120
120
  const envFile = await resolveDockerEnvFileArg(runtime.envName, config);
@@ -189,6 +189,9 @@ export async function buildSavedDockerRunArgs(runtime) {
189
189
  if (envFile) {
190
190
  args.push('--env-file', envFile);
191
191
  }
192
+ for (const [key, value] of Object.entries(options?.initEnvVars ?? {})) {
193
+ args.push('-e', `${key}=${value}`);
194
+ }
192
195
  const lifecycleEnvVars = managedAppLifecycleEnvVars();
193
196
  args.push('-e', `APP_ENV=${lifecycleEnvVars.APP_ENV}`, '-e', `NODE_ENV=${lifecycleEnvVars.NODE_ENV}`, '-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}:${DOCKER_APP_STORAGE_DESTINATION}`);
194
197
  pushOptionalEnvArg(args, 'APP_PUBLIC_PATH', appPublicPath ? resolveAppPublicPath(appPublicPath) : undefined);
@@ -206,7 +209,9 @@ export async function buildSavedDockerRunArgs(runtime) {
206
209
  };
207
210
  }
208
211
  export async function recreateSavedDockerApp(runtime, options) {
209
- const plan = await buildSavedDockerRunArgs(runtime);
212
+ const plan = await buildSavedDockerRunArgs(runtime, {
213
+ initEnvVars: options?.initEnvVars,
214
+ });
210
215
  try {
211
216
  await ensureDockerNetwork(runtime.workspaceName);
212
217
  await mkdir(plan.storagePath, { recursive: true });
@@ -132,11 +132,19 @@ function normalizeAuthConfig(config) {
132
132
  }
133
133
  : {}),
134
134
  ...(settings.proxy?.nbCliRoot ||
135
+ settings.proxy?.caddyDriver ||
136
+ settings.proxy?.nginxDriver ||
135
137
  settings.proxy?.upstreamHost ||
136
138
  settings.proxy?.host
137
139
  ? {
138
140
  proxy: {
139
141
  ...(settings.proxy?.nbCliRoot ? { nbCliRoot: normalizeOptionalString(settings.proxy.nbCliRoot) } : {}),
142
+ ...(settings.proxy?.caddyDriver
143
+ ? { caddyDriver: normalizeOptionalString(settings.proxy.caddyDriver) }
144
+ : {}),
145
+ ...(settings.proxy?.nginxDriver
146
+ ? { nginxDriver: normalizeOptionalString(settings.proxy.nginxDriver) }
147
+ : {}),
140
148
  ...(settings.proxy?.upstreamHost || settings.proxy?.host
141
149
  ? {
142
150
  upstreamHost: normalizeOptionalString(settings.proxy?.upstreamHost ?? settings.proxy.host),
@@ -18,6 +18,10 @@ export const DEFAULT_GIT_BIN = 'git';
18
18
  export const DEFAULT_NGINX_BIN = 'nginx';
19
19
  export const PROXY_PROVIDER_OPTIONS = ['nginx', 'caddy'];
20
20
  export const DEFAULT_PROXY_PROVIDER = 'nginx';
21
+ export const NGINX_PROXY_DRIVER_OPTIONS = ['local', 'docker'];
22
+ export const DEFAULT_NGINX_PROXY_DRIVER = 'local';
23
+ export const CADDY_PROXY_DRIVER_OPTIONS = ['local', 'docker'];
24
+ export const DEFAULT_CADDY_PROXY_DRIVER = 'local';
21
25
  export const DEFAULT_PROXY_HOST = '127.0.0.1';
22
26
  export const DEFAULT_YARN_BIN = 'yarn';
23
27
  export const DEFAULT_LOG_RETENTION_DAYS = 14;
@@ -37,6 +41,8 @@ export const SUPPORTED_CLI_CONFIG_KEYS = [
37
41
  'bin.git',
38
42
  'bin.nginx',
39
43
  'proxy.nb-cli-root',
44
+ 'proxy.caddy-driver',
45
+ 'proxy.nginx-driver',
40
46
  'proxy.upstream-host',
41
47
  'bin.yarn',
42
48
  'log.enabled',
@@ -74,6 +80,24 @@ export function normalizeProxyProvider(value) {
74
80
  }
75
81
  return PROXY_PROVIDER_OPTIONS.includes(normalized) ? normalized : undefined;
76
82
  }
83
+ export function normalizeNginxProxyDriver(value) {
84
+ const normalized = trimValue(value);
85
+ if (!normalized) {
86
+ return undefined;
87
+ }
88
+ return NGINX_PROXY_DRIVER_OPTIONS.includes(normalized)
89
+ ? normalized
90
+ : undefined;
91
+ }
92
+ export function normalizeCaddyProxyDriver(value) {
93
+ const normalized = trimValue(value);
94
+ if (!normalized) {
95
+ return undefined;
96
+ }
97
+ return CADDY_PROXY_DRIVER_OPTIONS.includes(normalized)
98
+ ? normalized
99
+ : undefined;
100
+ }
77
101
  function cloneSettings(config) {
78
102
  return {
79
103
  ...(config.settings?.locale ? { locale: trimValue(config.settings.locale) } : {}),
@@ -116,7 +140,11 @@ function pruneSettings(config) {
116
140
  delete config.settings?.bin;
117
141
  }
118
142
  const proxy = config.settings?.proxy;
119
- if (proxy && !trimValue(proxy.nbCliRoot) && !trimValue(proxy.upstreamHost)) {
143
+ if (proxy &&
144
+ !trimValue(proxy.nbCliRoot) &&
145
+ !trimValue(proxy.caddyDriver) &&
146
+ !trimValue(proxy.nginxDriver) &&
147
+ !trimValue(proxy.upstreamHost)) {
120
148
  delete config.settings?.proxy;
121
149
  }
122
150
  const log = config.settings?.log;
@@ -161,6 +189,10 @@ export function getExplicitCliConfigValue(config, key) {
161
189
  return trimValue(config.settings?.bin?.nginx);
162
190
  case 'proxy.nb-cli-root':
163
191
  return trimValue(config.settings?.proxy?.nbCliRoot);
192
+ case 'proxy.caddy-driver':
193
+ return normalizeCaddyProxyDriver(config.settings?.proxy?.caddyDriver);
194
+ case 'proxy.nginx-driver':
195
+ return normalizeNginxProxyDriver(config.settings?.proxy?.nginxDriver);
164
196
  case 'proxy.upstream-host':
165
197
  return trimValue(config.settings?.proxy?.upstreamHost);
166
198
  case 'bin.yarn':
@@ -203,6 +235,10 @@ export function getEffectiveCliConfigValue(config, key) {
203
235
  return DEFAULT_NGINX_BIN;
204
236
  case 'proxy.nb-cli-root':
205
237
  return explicit ?? resolveCliHomeRoot();
238
+ case 'proxy.caddy-driver':
239
+ return explicit ?? DEFAULT_CADDY_PROXY_DRIVER;
240
+ case 'proxy.nginx-driver':
241
+ return explicit ?? DEFAULT_NGINX_PROXY_DRIVER;
206
242
  case 'proxy.upstream-host':
207
243
  return explicit ?? DEFAULT_PROXY_HOST;
208
244
  case 'bin.yarn':
@@ -248,6 +284,20 @@ export function normalizeCliConfigValue(key, value) {
248
284
  }
249
285
  return normalized;
250
286
  }
287
+ if (key === 'proxy.nginx-driver') {
288
+ const driver = normalizeNginxProxyDriver(normalized);
289
+ if (!driver) {
290
+ throw new Error(`Config key "${key}" must be one of: ${NGINX_PROXY_DRIVER_OPTIONS.join(', ')}`);
291
+ }
292
+ return driver;
293
+ }
294
+ if (key === 'proxy.caddy-driver') {
295
+ const driver = normalizeCaddyProxyDriver(normalized);
296
+ if (!driver) {
297
+ throw new Error(`Config key "${key}" must be one of: ${CADDY_PROXY_DRIVER_OPTIONS.join(', ')}`);
298
+ }
299
+ return driver;
300
+ }
251
301
  return normalized;
252
302
  }
253
303
  export async function loadCliConfig(options = {}) {
@@ -343,6 +393,18 @@ export async function setCliConfigValue(key, value, options = {}) {
343
393
  nbCliRoot: normalized,
344
394
  };
345
395
  break;
396
+ case 'proxy.caddy-driver':
397
+ config.settings.proxy = {
398
+ ...(config.settings.proxy ?? {}),
399
+ caddyDriver: normalized,
400
+ };
401
+ break;
402
+ case 'proxy.nginx-driver':
403
+ config.settings.proxy = {
404
+ ...(config.settings.proxy ?? {}),
405
+ nginxDriver: normalized,
406
+ };
407
+ break;
346
408
  case 'proxy.upstream-host':
347
409
  config.settings.proxy = {
348
410
  ...(config.settings.proxy ?? {}),
@@ -439,6 +501,16 @@ export async function deleteCliConfigValue(key, options = {}) {
439
501
  delete config.settings.proxy.nbCliRoot;
440
502
  }
441
503
  break;
504
+ case 'proxy.caddy-driver':
505
+ if (config.settings.proxy) {
506
+ delete config.settings.proxy.caddyDriver;
507
+ }
508
+ break;
509
+ case 'proxy.nginx-driver':
510
+ if (config.settings.proxy) {
511
+ delete config.settings.proxy.nginxDriver;
512
+ }
513
+ break;
442
514
  case 'proxy.upstream-host':
443
515
  if (config.settings.proxy) {
444
516
  delete config.settings.proxy.upstreamHost;
@@ -33,6 +33,7 @@ const STRING_ENV_CONFIG_KEYS = [
33
33
  'dbPassword',
34
34
  'dbSchema',
35
35
  'dbTablePrefix',
36
+ 'lang',
36
37
  'rootUsername',
37
38
  'rootEmail',
38
39
  'rootPassword',
@@ -49,6 +50,9 @@ function trimConfigValue(value) {
49
50
  const text = String(value ?? '').trim();
50
51
  return text || undefined;
51
52
  }
53
+ function resolveSetupState(value) {
54
+ return value === 'prepared' || value === 'installed' ? value : undefined;
55
+ }
52
56
  function resolveEnvKind(input) {
53
57
  const source = trimConfigValue(input.source);
54
58
  const appPath = trimConfigValue(input.appPath);
@@ -72,6 +76,10 @@ export function buildStoredEnvConfig(input) {
72
76
  envConfig[key] = key === 'appPublicPath' ? resolveAppPublicPath(value) : value;
73
77
  }
74
78
  }
79
+ const setupState = resolveSetupState(input.setupState);
80
+ if (setupState) {
81
+ envConfig.setupState = setupState;
82
+ }
75
83
  for (const key of BOOLEAN_ENV_CONFIG_KEYS) {
76
84
  const value = input[key];
77
85
  if (typeof value === 'boolean') {
@@ -109,8 +109,8 @@ export function buildManagedAppEntryGeneratedConfigBlock(provider, generatedConf
109
109
  return [
110
110
  ` ${MANAGED_APP_ENTRY_BLOCK_BEGIN}`,
111
111
  provider === 'caddy'
112
- ? ' # Keep this import so `nb env proxy` can refresh managed routes.'
113
- : ' # Keep this include so `nb env proxy` can refresh managed routes.',
112
+ ? ' # Keep this import so the CLI can refresh managed routes.'
113
+ : ' # Keep this include so the CLI can refresh managed routes.',
114
114
  ` ${referenceLine}`,
115
115
  ` ${MANAGED_APP_ENTRY_BLOCK_END}`,
116
116
  ].join('\n');
@@ -244,10 +244,11 @@ function applyCaddyAppEntryOptions(content, options) {
244
244
  }
245
245
  const currentAddress = parseCaddySiteAddress(content);
246
246
  const nextAddress = buildCaddySiteAddress(options, currentAddress);
247
+ let nextContent = content;
247
248
  if (currentAddress) {
248
- return content.replace(currentAddress, nextAddress);
249
+ nextContent = nextContent.replace(/^([^\s#][^\n{]*)\s*\{/m, `${nextAddress} {`);
249
250
  }
250
- return content;
251
+ return nextContent.replace(/^# host=.*$/m, `# host=${nextAddress}`);
251
252
  }
252
253
  export function applyEnvProxyAppEntryOptions(content, provider, options) {
253
254
  if (!trimValue(options?.host) && !trimValue(options?.port)) {
@@ -320,9 +321,15 @@ export async function resolveManagedProxyAppVersion(runtime) {
320
321
  return trimValue(runtime.env.config.downloadVersion);
321
322
  }
322
323
  export async function resolveProxyNbCliRoot(options) {
324
+ if (trimValue(options?.runtimeCliRoot)) {
325
+ return trimValue(options?.runtimeCliRoot);
326
+ }
323
327
  return await getCliConfigValue('proxy.nb-cli-root', { scope: options?.scope });
324
328
  }
325
329
  export async function resolveProxyUpstreamHost(options) {
330
+ if (trimValue(options?.upstreamHost)) {
331
+ return trimValue(options?.upstreamHost);
332
+ }
326
333
  return await getCliConfigValue('proxy.upstream-host', { scope: options?.scope });
327
334
  }
328
335
  function isPathInsideRoot(targetPath, rootPath) {
@@ -376,6 +383,19 @@ function ensureTrailingSlash(value) {
376
383
  function trimTrailingSlash(value) {
377
384
  return value.endsWith('/') ? value.slice(0, -1) : value;
378
385
  }
386
+ function dedupeAssetPrefix(value, prefix) {
387
+ const normalizedPrefix = ensureTrailingSlash(prefix);
388
+ const nestedPrefix = normalizedPrefix.replace(/^\/+/, '');
389
+ if (!nestedPrefix) {
390
+ return value;
391
+ }
392
+ let result = value;
393
+ const duplicatePrefix = `${normalizedPrefix}${nestedPrefix}`;
394
+ while (result.startsWith(duplicatePrefix)) {
395
+ result = `${normalizedPrefix}${result.slice(duplicatePrefix.length)}`;
396
+ }
397
+ return result;
398
+ }
379
399
  function renderTemplateString(template, values) {
380
400
  return template.replace(/{{\s*([\w.]+)\s*}}/g, (_match, key) => values[key] ?? '');
381
401
  }
@@ -413,6 +433,7 @@ function rewriteHtmlAssetPublicPath(html, currentPublicPath, nextPublicPath) {
413
433
  let rewritten = html.replace(new RegExp(`((?:src|href)=["'])${escapedCurrentPrefix}`, 'g'), `$1${nextPrefix}`);
414
434
  rewritten = rewritten.replace(/((?:src|href)=["'])(?:\.\/)?assets\//g, `$1${nextPrefix}assets/`);
415
435
  rewritten = rewritten.replace(/((?:src|href)=["'])\/assets\//g, `$1${nextPrefix}assets/`);
436
+ rewritten = rewritten.replace(/((?:src|href)=["'])([^"']+)/g, (_match, attributePrefix, assetPath) => `${attributePrefix}${dedupeAssetPrefix(assetPath, nextPrefix)}`);
416
437
  return rewritten.replace(new RegExp(`((?:src|href)=["'])${escapeRegExp(trimTrailingSlash(nextPrefix))}//+`, 'g'), '$1');
417
438
  }
418
439
  function buildNginxManagedConfigBlock(context) {
@@ -686,19 +707,11 @@ export async function buildEnvProxyCaddyBundle(runtime, options) {
686
707
  const sourceV2PublicPath = extractRuntimePublicPath(sourceIndexV2Content);
687
708
  const indexV1AssetPublicPath = context.cdnBaseUrl;
688
709
  const indexV2AssetPublicPath = `${trimTrailingSlash(context.cdnBaseUrl)}/${DEFAULT_MODERN_CLIENT_PREFIX}/`;
689
- const generatedConfigPath = resolveEnvProxyOutputPath(runtime.envName, {
690
- provider: 'caddy',
691
- scope: options?.scope,
692
- });
693
- const renderedGeneratedConfigPath = await mapProxyPathFromCliRoot(generatedConfigPath, {
694
- ...options,
695
- provider: 'caddy',
696
- });
697
710
  const appConfigPath = resolveEnvProxyAppOutputPath(runtime.envName, { scope: options?.scope, provider: 'caddy' });
698
711
  const entryDir = resolveEnvProxyEntryDir(runtime.envName, { scope: options?.scope, provider: 'caddy' });
699
712
  const publicDir = resolveEnvProxyCaddyPublicOutputDir(runtime.envName, { scope: options?.scope });
700
713
  const renderedPublicDir = await mapProxyPathFromCliRoot(publicDir, { ...options, provider: 'caddy' });
701
- const generatedConfigContent = renderCaddyGeneratedTemplate({
714
+ const appConfigContent = renderCaddyAppTemplate(buildCaddySiteAddress(), {
702
715
  appPublicPath: context.appPublicPath,
703
716
  apiBasePath: context.apiBasePath,
704
717
  apiPort: context.apiPort,
@@ -717,7 +730,6 @@ export async function buildEnvProxyCaddyBundle(runtime, options) {
717
730
  entryDir,
718
731
  publicDir,
719
732
  appConfigPath,
720
- generatedConfigPath,
721
733
  indexV1Path: resolveEnvProxyCaddyIndexOutputPath(runtime.envName, 'v1', { scope: options?.scope }),
722
734
  indexV2Path: resolveEnvProxyCaddyIndexOutputPath(runtime.envName, 'v2', { scope: options?.scope }),
723
735
  mainConfigPath: resolveEnvProxyMainOutputPath({ scope: options?.scope, provider: 'caddy' }),
@@ -729,8 +741,7 @@ export async function buildEnvProxyCaddyBundle(runtime, options) {
729
741
  activeVersion: context.activeVersion,
730
742
  cdnBaseUrl: context.cdnBaseUrl,
731
743
  backendUrl: context.backendUrl,
732
- appConfigContent: buildEnvProxyAppConfig('caddy', renderedGeneratedConfigPath),
733
- generatedConfigContent,
744
+ appConfigContent,
734
745
  mainConfigContent: await buildEnvProxyMainConfig({ provider: 'caddy', scope: options?.scope }),
735
746
  indexV1Content: injectRuntimeScriptIntoHtml(rewriteHtmlAssetPublicPath(sourceIndexV1Content, sourceV1PublicPath, indexV1AssetPublicPath), v1RuntimeScript),
736
747
  indexV2Content: injectRuntimeScriptIntoHtml(rewriteHtmlAssetPublicPath(sourceIndexV2Content, sourceV2PublicPath, indexV2AssetPublicPath), v2RuntimeScript),
@@ -957,11 +968,26 @@ ${renderNginxLocationTemplate(context)}}
957
968
  `;
958
969
  }
959
970
  function renderNginxGeneratedTemplate(context) {
960
- return `# Managed by \`nb env proxy\`. Changes will be overwritten.
971
+ return `# Managed by NocoBase CLI. Changes will be overwritten.
961
972
 
962
973
  ${renderNginxLocationTemplate(context)}`;
963
974
  }
964
- function renderCaddyGeneratedTemplate(context, publicDir) {
975
+ function buildCaddyContextCommentLines(siteAddress, context, publicDir) {
976
+ return [
977
+ '# Rendered by `nb proxy caddy generate`.',
978
+ '# Context:',
979
+ `# host=${siteAddress}`,
980
+ `# publicBasePath=${context.appPublicPath}`,
981
+ `# apiBasePath=${context.apiBasePath}`,
982
+ `# wsPath=${context.wsPath}`,
983
+ `# v2PublicPath=${context.v2PublicPath}`,
984
+ `# backendUrl=http://${context.proxyHost}:${context.apiPort}`,
985
+ `# uploadsDir=${context.uploadsPath}`,
986
+ `# distRootDir=${context.distClientRoot}`,
987
+ `# publicDir=${publicDir}`,
988
+ ];
989
+ }
990
+ function renderCaddyAppTemplate(siteAddress, context, publicDir) {
965
991
  const uploadsPath = `${context.appPublicPath}storage/uploads/`;
966
992
  const distPathMatcher = toCaddyPathMatcher(context.distPath);
967
993
  const uploadsPathMatcher = toCaddyPathMatcher(uploadsPath);
@@ -998,61 +1024,65 @@ function renderCaddyGeneratedTemplate(context, publicDir) {
998
1024
  handle /${context.modernClientPrefix}/* {
999
1025
  redir * ${appPublicPathNoTrailingSlash}{uri} 302
1000
1026
  }`;
1001
- return `# Managed by \`nb env proxy\`. Changes will be overwritten.
1002
-
1003
- route {
1004
- encode zstd gzip${rootRedirectBlock}${appPublicPathRedirectBlock}${modernClientRedirectBlock}${shorthandModernClientRedirectBlock}
1005
-
1006
- handle_path ${uploadsPathMatcher} {
1007
- root * ${context.uploadsPath}
1008
- header Cache-Control public
1009
- header X-Content-Type-Options nosniff
1010
- file_server
1011
- }
1012
-
1013
- handle_path ${distPathMatcher} {
1014
- root * ${context.distClientRoot}
1015
- header Cache-Control public
1016
- file_server
1017
- }
1018
-
1019
- @oauth path_regexp oauth ^/\\.well-known/oauth-authorization-server/(.+)$
1020
- handle @oauth {
1021
- rewrite * /{re.oauth.1}/.well-known/oauth-authorization-server
1022
- reverse_proxy ${context.proxyHost}:${context.apiPort}
1023
- }
1024
-
1025
- @openid path_regexp openid ^/\\.well-known/openid-configuration/(.+)$
1026
- handle @openid {
1027
- rewrite * /{re.openid.1}/.well-known/openid-configuration
1028
- reverse_proxy ${context.proxyHost}:${context.apiPort}
1029
- }
1030
-
1031
- handle ${apiPathMatcher} {
1032
- reverse_proxy ${context.proxyHost}:${context.apiPort}
1033
- }
1034
-
1035
- handle ${context.wsPath} {
1036
- reverse_proxy ${context.proxyHost}:${context.apiPort}
1037
- }
1038
-
1039
- handle_path ${toCaddyPathMatcher(context.v2PublicPath)} {
1040
- root * ${publicDir}
1041
- header Cache-Control "no-store, no-cache, must-revalidate"
1042
- header X-Robots-Tag "noindex, nofollow"
1043
- try_files {path} /index-v2.html
1044
- file_server
1045
- }
1046
-
1047
- handle_path ${toCaddyPathMatcher(context.appPublicPath)} {
1048
- root * ${publicDir}
1049
- header Cache-Control "no-store, no-cache, must-revalidate"
1050
- header X-Robots-Tag "noindex, nofollow"
1051
- try_files {path} /index-v1.html
1052
- file_server
1053
- }
1054
- }
1055
- `;
1027
+ return [
1028
+ ...buildCaddyContextCommentLines(siteAddress, context, publicDir),
1029
+ '',
1030
+ `${siteAddress} {`,
1031
+ ` encode zstd gzip${rootRedirectBlock}${appPublicPathRedirectBlock}${modernClientRedirectBlock}${shorthandModernClientRedirectBlock}`,
1032
+ '',
1033
+ ` handle_path ${uploadsPathMatcher} {`,
1034
+ ` root * ${context.uploadsPath}`,
1035
+ ' header Cache-Control public',
1036
+ ' header X-Content-Type-Options nosniff',
1037
+ ' file_server',
1038
+ ' }',
1039
+ '',
1040
+ ` handle_path ${distPathMatcher} {`,
1041
+ ` root * ${context.distClientRoot}`,
1042
+ ' header Cache-Control public',
1043
+ ' file_server',
1044
+ ' }',
1045
+ '',
1046
+ ' @oauth path_regexp oauth ^/\\.well-known/oauth-authorization-server/(.+)$',
1047
+ ' handle @oauth {',
1048
+ ' rewrite * /{re.oauth.1}/.well-known/oauth-authorization-server',
1049
+ ` reverse_proxy ${context.proxyHost}:${context.apiPort}`,
1050
+ ' }',
1051
+ '',
1052
+ ' @openid path_regexp openid ^/\\.well-known/openid-configuration/(.+)$',
1053
+ ' handle @openid {',
1054
+ ' rewrite * /{re.openid.1}/.well-known/openid-configuration',
1055
+ ` reverse_proxy ${context.proxyHost}:${context.apiPort}`,
1056
+ ' }',
1057
+ '',
1058
+ ' # Keep API and WS routes above the SPA fallbacks.',
1059
+ ` handle ${apiPathMatcher} {`,
1060
+ ` reverse_proxy ${context.proxyHost}:${context.apiPort}`,
1061
+ ' }',
1062
+ '',
1063
+ ` handle ${context.wsPath} {`,
1064
+ ` reverse_proxy ${context.proxyHost}:${context.apiPort}`,
1065
+ ' }',
1066
+ '',
1067
+ ' # Keep the v2 SPA route above the fallback SPA route.',
1068
+ ` handle_path ${toCaddyPathMatcher(context.v2PublicPath)} {`,
1069
+ ` root * ${publicDir}`,
1070
+ ' header Cache-Control "no-store, no-cache, must-revalidate"',
1071
+ ' header X-Robots-Tag "noindex, nofollow"',
1072
+ ' try_files {path} /index-v2.html',
1073
+ ' file_server',
1074
+ ' }',
1075
+ '',
1076
+ ` handle_path ${toCaddyPathMatcher(context.appPublicPath)} {`,
1077
+ ` root * ${publicDir}`,
1078
+ ' header Cache-Control "no-store, no-cache, must-revalidate"',
1079
+ ' header X-Robots-Tag "noindex, nofollow"',
1080
+ ' try_files {path} /index-v1.html',
1081
+ ' file_server',
1082
+ ' }',
1083
+ '}',
1084
+ '',
1085
+ ].join('\n');
1056
1086
  }
1057
1087
  export function buildEnvProxyAppConfig(provider, generatedConfigPath, options) {
1058
1088
  if (provider === 'caddy') {
@@ -1082,7 +1112,7 @@ export function appConfigIncludesGeneratedConfig(content, generatedConfigPath, p
1082
1112
  return new RegExp(`\\binclude\\s+${escapedReference}\\s*;`).test(content);
1083
1113
  }
1084
1114
  function renderCaddyMainTemplate(appConfigImportPath) {
1085
- return `# Managed by \`nb env proxy\`. Changes will be overwritten.
1115
+ return `# Managed by NocoBase CLI. Changes will be overwritten.
1086
1116
 
1087
1117
  import ${appConfigImportPath}
1088
1118
  `;
@@ -1146,7 +1176,7 @@ export async function buildEnvProxyConfig(runtime, options) {
1146
1176
  return {
1147
1177
  ...base,
1148
1178
  content: provider === 'caddy'
1149
- ? renderCaddyGeneratedTemplate(templateContext, publicDir ?? '')
1179
+ ? renderCaddyAppTemplate(buildCaddySiteAddress(), templateContext, publicDir ?? '')
1150
1180
  : renderNginxGeneratedTemplate(templateContext),
1151
1181
  };
1152
1182
  }
@@ -15,6 +15,9 @@ function trimValue(value) {
15
15
  const text = String(value ?? '').trim();
16
16
  return text || undefined;
17
17
  }
18
+ function normalizeEnvFilePath(value) {
19
+ return value.replace(/\\/g, '/');
20
+ }
18
21
  function stripWrappingQuotes(value) {
19
22
  if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) {
20
23
  return value
@@ -54,16 +57,16 @@ export function resolveManagedLocalEnvFilePath(runtime) {
54
57
  const config = runtime.env.config ?? {};
55
58
  const explicitEnvFile = trimValue(config.envFile);
56
59
  if (explicitEnvFile) {
57
- return resolveConfiguredEnvPath(explicitEnvFile) ?? explicitEnvFile;
60
+ return normalizeEnvFilePath(resolveConfiguredEnvPath(explicitEnvFile) ?? explicitEnvFile);
58
61
  }
59
62
  const configuredAppPath = resolveConfiguredAppPath(config);
60
63
  if (configuredAppPath) {
61
- return path.join(configuredAppPath, '.env');
64
+ return normalizeEnvFilePath(path.join(configuredAppPath, '.env'));
62
65
  }
63
66
  if (path.basename(runtime.projectRoot) === 'source') {
64
- return path.resolve(runtime.projectRoot, '..', '.env');
67
+ return normalizeEnvFilePath(path.resolve(runtime.projectRoot, '..', '.env'));
65
68
  }
66
- return path.join(runtime.projectRoot, '.env');
69
+ return normalizeEnvFilePath(path.join(runtime.projectRoot, '.env'));
67
70
  }
68
71
  export async function resolveManagedRuntimeEnvFilePath(runtime) {
69
72
  if (runtime.kind === 'local') {
@@ -0,0 +1,32 @@
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
+ */
9
+ function trimValue(value) {
10
+ return String(value ?? '').trim();
11
+ }
12
+ export function resolveManagedSetupState(value) {
13
+ return value === 'prepared' || value === 'installed' ? value : undefined;
14
+ }
15
+ export function isPreparedSetupState(value) {
16
+ return resolveManagedSetupState(value) === 'prepared';
17
+ }
18
+ export function buildInitAppEnvVarsFromConfig(config) {
19
+ const out = {};
20
+ const put = (key, value) => {
21
+ const text = trimValue(value);
22
+ if (text) {
23
+ out[key] = text;
24
+ }
25
+ };
26
+ put('INIT_APP_LANG', config?.lang);
27
+ put('INIT_ROOT_USERNAME', config?.rootUsername);
28
+ put('INIT_ROOT_EMAIL', config?.rootEmail);
29
+ put('INIT_ROOT_PASSWORD', config?.rootPassword);
30
+ put('INIT_ROOT_NICKNAME', config?.rootNickname);
31
+ return out;
32
+ }
@@ -84,8 +84,7 @@ function defaultOnCancel(locale) {
84
84
  exit(0);
85
85
  }
86
86
  function defaultOnMissingNonInteractive(message) {
87
- console.error(message);
88
- exit(1);
87
+ throw new Error(message);
89
88
  }
90
89
  function createTerminalHooks(locale, overrides) {
91
90
  return createPromptCatalogHooks(locale, overrides, {