@nocobase/cli 2.2.0-alpha.4 → 2.2.0-alpha.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/env-proxy/nginx/snippets/proxy-location.conf +1 -0
- package/dist/commands/app/start.js +4 -1
- package/dist/commands/install.js +53 -128
- package/dist/commands/proxy/caddy/generate.js +23 -15
- package/dist/commands/proxy/nginx/generate.js +23 -15
- package/dist/commands/revision/create.js +1 -1
- package/dist/commands/source/download.js +16 -12
- package/dist/lib/app-managed-resources.js +3 -2
- package/dist/lib/auth-store.js +68 -0
- package/dist/lib/cli-config.js +52 -1
- package/dist/lib/docker-image.js +94 -6
- package/dist/lib/env-config.js +5 -0
- package/dist/lib/env-proxy-config.js +48 -0
- package/dist/lib/env-proxy.js +29 -3
- package/dist/lib/proxy-caddy.js +53 -7
- package/dist/lib/proxy-nginx.js +53 -7
- package/package.json +2 -2
|
@@ -12,9 +12,10 @@ import path from 'node:path';
|
|
|
12
12
|
import { stdin as stdinStream, stdout as stdoutStream } from 'node:process';
|
|
13
13
|
import { runPromptCatalog, } from "../../lib/prompt-catalog.js";
|
|
14
14
|
import { applyCliLocale, CLI_LOCALE_FLAG_DESCRIPTION, CLI_LOCALE_FLAG_OPTIONS, localeText, resolveCliLocale, translateCli, } from "../../lib/cli-locale.js";
|
|
15
|
-
import { DEFAULT_DOCKER_REGISTRY, DEFAULT_DOCKER_REGISTRY_ZH_CN, resolveDockerImageRef, } from "../../lib/docker-image.js";
|
|
15
|
+
import { DEFAULT_DOCKER_REGISTRY, DEFAULT_DOCKER_REGISTRY_ZH_CN, resolveOfficialDockerRegistry, resolveDockerImageRef, } from "../../lib/docker-image.js";
|
|
16
16
|
import { getEnv } from '../../lib/auth-store.js';
|
|
17
17
|
import { resolveDefaultConfigScope } from '../../lib/cli-home.js';
|
|
18
|
+
import { getCliConfigValue } from '../../lib/cli-config.js';
|
|
18
19
|
import { buildBeforeDependencyInstallHookContext, runBeforeDependencyInstallHook, } from '../../lib/hook-script.js';
|
|
19
20
|
import { run } from "../../lib/run-npm.js";
|
|
20
21
|
import { printVerbose, setVerboseMode, startTask, stopTask, updateTask } from '../../lib/ui.js';
|
|
@@ -450,9 +451,9 @@ export default class SourceDownload extends Command {
|
|
|
450
451
|
}
|
|
451
452
|
return outputAbs;
|
|
452
453
|
}
|
|
453
|
-
dockerTarPath(flags, outputAbs) {
|
|
454
|
+
async dockerTarPath(flags, outputAbs) {
|
|
454
455
|
const imageRef = resolveDockerImageRef(flags['docker-registry'], flags.version, {
|
|
455
|
-
defaultRegistry:
|
|
456
|
+
defaultRegistry: await this.resolveConfiguredDockerRegistryDefault(),
|
|
456
457
|
defaultVersion: 'latest',
|
|
457
458
|
});
|
|
458
459
|
const safeBase = imageRef.replace(/[\\/:]/g, '-');
|
|
@@ -462,9 +463,11 @@ export default class SourceDownload extends Command {
|
|
|
462
463
|
* Defaults for prompts only. Keys present in **`preset`** are omitted so `runPromptCatalog` uses
|
|
463
464
|
* **`values`** (preset) alone for those steps — no duplicate prefill for skipped prompts.
|
|
464
465
|
*/
|
|
465
|
-
|
|
466
|
+
async resolveConfiguredDockerRegistryDefault() {
|
|
467
|
+
return resolveOfficialDockerRegistry(await getCliConfigValue('nb-image-registry'));
|
|
468
|
+
}
|
|
469
|
+
buildInitialValuesFromParsed(flags, preset, defaultDockerRegistry) {
|
|
466
470
|
const initialValues = {};
|
|
467
|
-
const localeDefaultDockerRegistry = defaultDockerRegistryForLang(flags.locale ?? process.env.NB_LOCALE);
|
|
468
471
|
const source = flags.source?.trim();
|
|
469
472
|
if (source) {
|
|
470
473
|
initialValues.source = source;
|
|
@@ -488,7 +491,7 @@ export default class SourceDownload extends Command {
|
|
|
488
491
|
initialValues.dockerRegistry = String(flags['docker-registry'] ?? '').trim();
|
|
489
492
|
}
|
|
490
493
|
else {
|
|
491
|
-
initialValues.dockerRegistry =
|
|
494
|
+
initialValues.dockerRegistry = defaultDockerRegistry;
|
|
492
495
|
}
|
|
493
496
|
initialValues.dockerPlatform = normalizeDockerPlatform(flags['docker-platform']);
|
|
494
497
|
initialValues.dockerSave = flags['docker-save'];
|
|
@@ -569,7 +572,7 @@ export default class SourceDownload extends Command {
|
|
|
569
572
|
}
|
|
570
573
|
return flags.build;
|
|
571
574
|
}
|
|
572
|
-
mapCatalogResultsToResolved(results, flags) {
|
|
575
|
+
mapCatalogResultsToResolved(results, flags, defaultDockerRegistry) {
|
|
573
576
|
const source = String(results.source);
|
|
574
577
|
const version = resolveVersionFromResults(results, flags.version) || 'latest';
|
|
575
578
|
const devDependencies = source === 'npm' ? Boolean(results.devDependencies) : undefined;
|
|
@@ -583,7 +586,7 @@ export default class SourceDownload extends Command {
|
|
|
583
586
|
const dockerRegistry = source === 'docker'
|
|
584
587
|
? results.dockerRegistry !== undefined
|
|
585
588
|
? String(results.dockerRegistry).trim() || undefined
|
|
586
|
-
: flags['docker-registry']?.trim() ||
|
|
589
|
+
: flags['docker-registry']?.trim() || defaultDockerRegistry
|
|
587
590
|
: undefined;
|
|
588
591
|
const dockerPlatform = source === 'docker'
|
|
589
592
|
? normalizeDockerPlatform(results.dockerPlatform !== undefined ? results.dockerPlatform : flags['docker-platform'])
|
|
@@ -628,7 +631,8 @@ export default class SourceDownload extends Command {
|
|
|
628
631
|
this.error('Download source is required in non-interactive mode. Use --source npm, --source git, or --source docker.');
|
|
629
632
|
}
|
|
630
633
|
const presetValues = this.buildPresetValuesFromFlags(flags);
|
|
631
|
-
const
|
|
634
|
+
const defaultDockerRegistry = await this.resolveConfiguredDockerRegistryDefault();
|
|
635
|
+
const initialValues = this.buildInitialValuesFromParsed(flags, presetValues, defaultDockerRegistry);
|
|
632
636
|
const results = await runPromptCatalog(SourceDownload.prompts, {
|
|
633
637
|
initialValues,
|
|
634
638
|
values: presetValues,
|
|
@@ -654,7 +658,7 @@ export default class SourceDownload extends Command {
|
|
|
654
658
|
if (flags['docker-save'] && source !== 'docker') {
|
|
655
659
|
this.error('--docker-save is only available when --source docker is selected.');
|
|
656
660
|
}
|
|
657
|
-
return this.mapCatalogResultsToResolved(results, flags);
|
|
661
|
+
return this.mapCatalogResultsToResolved(results, flags, defaultDockerRegistry);
|
|
658
662
|
}
|
|
659
663
|
npmRegistryUrl(flags) {
|
|
660
664
|
const url = flags['npm-registry']?.trim();
|
|
@@ -784,7 +788,7 @@ export default class SourceDownload extends Command {
|
|
|
784
788
|
}
|
|
785
789
|
async downloadFromDocker(flags) {
|
|
786
790
|
const imageRef = resolveDockerImageRef(flags['docker-registry'], flags.version, {
|
|
787
|
-
defaultRegistry:
|
|
791
|
+
defaultRegistry: await this.resolveConfiguredDockerRegistryDefault(),
|
|
788
792
|
defaultVersion: 'latest',
|
|
789
793
|
});
|
|
790
794
|
const platform = dockerPlatformArg(flags['docker-platform']);
|
|
@@ -811,7 +815,7 @@ export default class SourceDownload extends Command {
|
|
|
811
815
|
await fsp.rm(outAbs, { recursive: true, force: true });
|
|
812
816
|
}
|
|
813
817
|
await fsp.mkdir(outAbs, { recursive: true });
|
|
814
|
-
const tarPath = this.dockerTarPath(flags, outAbs);
|
|
818
|
+
const tarPath = await this.dockerTarPath(flags, outAbs);
|
|
815
819
|
this.log(`Saving Docker image tarball to ${tarPath}`);
|
|
816
820
|
await this.runExternalCommand('docker', ['save', '-o', tarPath, imageRef], {
|
|
817
821
|
errorName: 'docker save',
|
|
@@ -14,7 +14,7 @@ import { dockerContainerExists, managedAppLifecycleEnvVars, runLocalNocoBaseComm
|
|
|
14
14
|
import { deriveBuiltinDbConnection, resolveBuiltinDbConnection } from './builtin-db.js';
|
|
15
15
|
import { resolveConfiguredStoragePath } from './env-paths.js';
|
|
16
16
|
import { resolveDockerEnvFileArg } from "./docker-env-file.js";
|
|
17
|
-
import { DEFAULT_DOCKER_REGISTRY, DEFAULT_DOCKER_VERSION, resolveDockerImageRef, } from "./docker-image.js";
|
|
17
|
+
import { DEFAULT_DOCKER_REGISTRY, DEFAULT_DOCKER_VERSION, resolveDockerImageContainerPort, resolveDockerImageRef, } from "./docker-image.js";
|
|
18
18
|
import { resolveHookScriptPath } from './hook-script.js';
|
|
19
19
|
import { commandSucceeds, ensureDockerDaemonRunning, run } from './run-npm.js';
|
|
20
20
|
import Install from '../commands/install.js';
|
|
@@ -192,6 +192,7 @@ export async function buildSavedDockerRunArgs(runtime, options) {
|
|
|
192
192
|
defaultRegistry: DEFAULT_DOCKER_REGISTRY,
|
|
193
193
|
defaultVersion: DEFAULT_DOCKER_VERSION,
|
|
194
194
|
});
|
|
195
|
+
const containerPort = resolveDockerImageContainerPort(imageRef);
|
|
195
196
|
const missing = [];
|
|
196
197
|
if (!storagePath) {
|
|
197
198
|
missing.push('storagePath');
|
|
@@ -233,7 +234,7 @@ export async function buildSavedDockerRunArgs(runtime, options) {
|
|
|
233
234
|
args.push('--platform', dockerPlatform);
|
|
234
235
|
}
|
|
235
236
|
if (appPort) {
|
|
236
|
-
args.push('-p', `${appPort}
|
|
237
|
+
args.push('-p', `${appPort}:${containerPort}`);
|
|
237
238
|
}
|
|
238
239
|
if (envFile) {
|
|
239
240
|
args.push('--env-file', envFile);
|
package/dist/lib/auth-store.js
CHANGED
|
@@ -11,6 +11,7 @@ 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';
|
|
15
16
|
import { ENV_CONFIG_SCHEMA_VERSION, normalizeEnvConfigSchemaVersion } from './env-config.js';
|
|
16
17
|
import { cleanupCurrentSessionAfterEnvRemoval, resolveEffectiveCurrentEnv, setSessionCurrentEnv, } from './session-store.js';
|
|
@@ -81,12 +82,14 @@ function normalizeEnvConfigEntry(entry) {
|
|
|
81
82
|
const normalizedKind = resolveEnvKind(entry);
|
|
82
83
|
const apiBaseUrl = readEnvApiBaseUrl(entry);
|
|
83
84
|
const schemaVersion = normalizeEnvConfigSchemaVersion(entry.schemaVersion);
|
|
85
|
+
const proxy = normalizeEnvProxyConfig(entry.proxy);
|
|
84
86
|
return {
|
|
85
87
|
...rest,
|
|
86
88
|
...(schemaVersion ? { schemaVersion } : {}),
|
|
87
89
|
...(normalizedKind ? { kind: normalizedKind } : {}),
|
|
88
90
|
...(apiBaseUrl !== undefined ? { apiBaseUrl } : {}),
|
|
89
91
|
...(normalizeOptionalString(entry.appPublicPath) ? { appPublicPath: resolveAppPublicPath(entry.appPublicPath) } : {}),
|
|
92
|
+
...(proxy ? { proxy } : {}),
|
|
90
93
|
};
|
|
91
94
|
}
|
|
92
95
|
function normalizeAuthConfig(config) {
|
|
@@ -125,12 +128,19 @@ function normalizeAuthConfig(config) {
|
|
|
125
128
|
...(updatePolicy ? { update: { policy: updatePolicy } } : {}),
|
|
126
129
|
...(settings.license?.pkgUrl ? { license: { pkgUrl: normalizeOptionalString(settings.license.pkgUrl) } } : {}),
|
|
127
130
|
...(settings.docker?.network || settings.docker?.containerPrefix
|
|
131
|
+
|| settings.docker?.nbImageRegistry || settings.docker?.nbImageVariant
|
|
128
132
|
? {
|
|
129
133
|
docker: {
|
|
130
134
|
...(settings.docker?.network ? { network: normalizeOptionalString(settings.docker.network) } : {}),
|
|
131
135
|
...(settings.docker?.containerPrefix
|
|
132
136
|
? { containerPrefix: normalizeOptionalString(settings.docker.containerPrefix) }
|
|
133
137
|
: {}),
|
|
138
|
+
...(settings.docker?.nbImageRegistry
|
|
139
|
+
? { nbImageRegistry: normalizeOptionalString(settings.docker.nbImageRegistry) }
|
|
140
|
+
: {}),
|
|
141
|
+
...(settings.docker?.nbImageVariant
|
|
142
|
+
? { nbImageVariant: normalizeOptionalString(settings.docker.nbImageVariant) }
|
|
143
|
+
: {}),
|
|
134
144
|
},
|
|
135
145
|
}
|
|
136
146
|
: {}),
|
|
@@ -509,6 +519,64 @@ export async function setEnvRuntime(envName, runtime, options = {}) {
|
|
|
509
519
|
};
|
|
510
520
|
await saveAuthConfig(config, options);
|
|
511
521
|
}
|
|
522
|
+
export function resolveEnvProxyEntry(config, provider) {
|
|
523
|
+
const proxy = normalizeEnvProxyConfig(config?.proxy);
|
|
524
|
+
const resolved = {
|
|
525
|
+
...(proxy?.host ? { host: proxy.host } : {}),
|
|
526
|
+
...(proxy?.port !== undefined ? { port: proxy.port } : {}),
|
|
527
|
+
...((provider === 'nginx' ? proxy?.nginx : proxy?.caddy) ?? {}),
|
|
528
|
+
};
|
|
529
|
+
return Object.keys(resolved).length > 0 ? resolved : undefined;
|
|
530
|
+
}
|
|
531
|
+
export async function setEnvProxyEntry(envName, provider, entry, options = {}) {
|
|
532
|
+
await writeEnv(envName, (previous) => {
|
|
533
|
+
const currentProxy = normalizeEnvProxyConfig(previous?.proxy) ?? {};
|
|
534
|
+
const nextEntry = normalizeEnvProxyProviderConfig(entry);
|
|
535
|
+
const nextProxy = { ...currentProxy };
|
|
536
|
+
if (nextEntry && 'host' in nextEntry) {
|
|
537
|
+
const host = normalizeOptionalString(nextEntry.host);
|
|
538
|
+
if (host) {
|
|
539
|
+
nextProxy.host = host;
|
|
540
|
+
}
|
|
541
|
+
else {
|
|
542
|
+
delete nextProxy.host;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
if (nextEntry && 'port' in nextEntry) {
|
|
546
|
+
const portValue = nextEntry.port;
|
|
547
|
+
const port = typeof portValue === 'number' && Number.isInteger(portValue) && portValue >= 1 && portValue <= 65535
|
|
548
|
+
? portValue
|
|
549
|
+
: undefined;
|
|
550
|
+
if (port !== undefined) {
|
|
551
|
+
nextProxy.port = port;
|
|
552
|
+
}
|
|
553
|
+
else {
|
|
554
|
+
delete nextProxy.port;
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
const providerConfig = nextEntry && Object.keys(nextEntry).some((key) => key !== 'host' && key !== 'port')
|
|
558
|
+
? Object.fromEntries(Object.entries(nextEntry).filter(([key]) => key !== 'host' && key !== 'port'))
|
|
559
|
+
: undefined;
|
|
560
|
+
if (provider === 'nginx') {
|
|
561
|
+
if (providerConfig && Object.keys(providerConfig).length > 0) {
|
|
562
|
+
nextProxy.nginx = providerConfig;
|
|
563
|
+
}
|
|
564
|
+
else {
|
|
565
|
+
delete nextProxy.nginx;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
else if (providerConfig && Object.keys(providerConfig).length > 0) {
|
|
569
|
+
nextProxy.caddy = providerConfig;
|
|
570
|
+
}
|
|
571
|
+
else {
|
|
572
|
+
delete nextProxy.caddy;
|
|
573
|
+
}
|
|
574
|
+
return {
|
|
575
|
+
...previous,
|
|
576
|
+
proxy: nextProxy,
|
|
577
|
+
};
|
|
578
|
+
}, options);
|
|
579
|
+
}
|
|
512
580
|
export async function clearEnvRootSetup(envName, options = {}) {
|
|
513
581
|
const config = await loadExactAuthConfig(options);
|
|
514
582
|
const current = config.envs[envName];
|
package/dist/lib/cli-config.js
CHANGED
|
@@ -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';
|
|
@@ -37,6 +38,8 @@ export const SUPPORTED_CLI_CONFIG_KEYS = [
|
|
|
37
38
|
'license.pkg-url',
|
|
38
39
|
'docker.network',
|
|
39
40
|
'docker.container-prefix',
|
|
41
|
+
'nb-image-registry',
|
|
42
|
+
'nb-image-variant',
|
|
40
43
|
'bin.docker',
|
|
41
44
|
'bin.caddy',
|
|
42
45
|
'bin.git',
|
|
@@ -129,7 +132,11 @@ function pruneSettings(config) {
|
|
|
129
132
|
delete config.settings?.license;
|
|
130
133
|
}
|
|
131
134
|
const docker = config.settings?.docker;
|
|
132
|
-
if (docker &&
|
|
135
|
+
if (docker &&
|
|
136
|
+
!trimValue(docker.network) &&
|
|
137
|
+
!trimValue(docker.containerPrefix) &&
|
|
138
|
+
!trimValue(docker.nbImageRegistry) &&
|
|
139
|
+
!trimValue(docker.nbImageVariant)) {
|
|
133
140
|
delete config.settings?.docker;
|
|
134
141
|
}
|
|
135
142
|
const bin = config.settings?.bin;
|
|
@@ -182,6 +189,10 @@ export function getExplicitCliConfigValue(config, key) {
|
|
|
182
189
|
return trimValue(config.settings?.docker?.network);
|
|
183
190
|
case 'docker.container-prefix':
|
|
184
191
|
return trimValue(config.settings?.docker?.containerPrefix);
|
|
192
|
+
case 'nb-image-registry':
|
|
193
|
+
return normalizeNbImageRegistry(config.settings?.docker?.nbImageRegistry);
|
|
194
|
+
case 'nb-image-variant':
|
|
195
|
+
return normalizeNbImageVariant(config.settings?.docker?.nbImageVariant);
|
|
185
196
|
case 'bin.docker':
|
|
186
197
|
return trimValue(config.settings?.bin?.docker);
|
|
187
198
|
case 'bin.caddy':
|
|
@@ -230,6 +241,10 @@ export function getEffectiveCliConfigValue(config, key) {
|
|
|
230
241
|
return trimValue(config.name) || DEFAULT_DOCKER_NETWORK;
|
|
231
242
|
case 'docker.container-prefix':
|
|
232
243
|
return trimValue(config.name) || DEFAULT_DOCKER_CONTAINER_PREFIX;
|
|
244
|
+
case 'nb-image-registry':
|
|
245
|
+
return explicit ?? DEFAULT_NB_IMAGE_REGISTRY;
|
|
246
|
+
case 'nb-image-variant':
|
|
247
|
+
return explicit ?? DEFAULT_NB_IMAGE_VARIANT;
|
|
233
248
|
case 'bin.docker':
|
|
234
249
|
return DEFAULT_DOCKER_BIN;
|
|
235
250
|
case 'bin.caddy':
|
|
@@ -305,6 +320,20 @@ export function normalizeCliConfigValue(key, value) {
|
|
|
305
320
|
}
|
|
306
321
|
return driver;
|
|
307
322
|
}
|
|
323
|
+
if (key === 'nb-image-registry') {
|
|
324
|
+
const registry = normalizeNbImageRegistry(normalized);
|
|
325
|
+
if (!registry) {
|
|
326
|
+
throw new Error(`Config key "${key}" must be one of: ${NB_IMAGE_REGISTRY_OPTIONS.join(', ')}`);
|
|
327
|
+
}
|
|
328
|
+
return registry;
|
|
329
|
+
}
|
|
330
|
+
if (key === 'nb-image-variant') {
|
|
331
|
+
const variant = normalizeNbImageVariant(normalized);
|
|
332
|
+
if (!variant) {
|
|
333
|
+
throw new Error(`Config key "${key}" must be one of: ${NB_IMAGE_VARIANT_OPTIONS.join(', ')}`);
|
|
334
|
+
}
|
|
335
|
+
return variant;
|
|
336
|
+
}
|
|
308
337
|
return normalized;
|
|
309
338
|
}
|
|
310
339
|
export async function loadCliConfig(options = {}) {
|
|
@@ -370,6 +399,18 @@ export async function setCliConfigValue(key, value, options = {}) {
|
|
|
370
399
|
containerPrefix: normalized,
|
|
371
400
|
};
|
|
372
401
|
break;
|
|
402
|
+
case 'nb-image-registry':
|
|
403
|
+
config.settings.docker = {
|
|
404
|
+
...(config.settings.docker ?? {}),
|
|
405
|
+
nbImageRegistry: normalized,
|
|
406
|
+
};
|
|
407
|
+
break;
|
|
408
|
+
case 'nb-image-variant':
|
|
409
|
+
config.settings.docker = {
|
|
410
|
+
...(config.settings.docker ?? {}),
|
|
411
|
+
nbImageVariant: normalized,
|
|
412
|
+
};
|
|
413
|
+
break;
|
|
373
414
|
case 'bin.docker':
|
|
374
415
|
config.settings.bin = {
|
|
375
416
|
...(config.settings.bin ?? {}),
|
|
@@ -489,6 +530,16 @@ export async function deleteCliConfigValue(key, options = {}) {
|
|
|
489
530
|
delete config.settings.docker.containerPrefix;
|
|
490
531
|
}
|
|
491
532
|
break;
|
|
533
|
+
case 'nb-image-registry':
|
|
534
|
+
if (config.settings.docker) {
|
|
535
|
+
delete config.settings.docker.nbImageRegistry;
|
|
536
|
+
}
|
|
537
|
+
break;
|
|
538
|
+
case 'nb-image-variant':
|
|
539
|
+
if (config.settings.docker) {
|
|
540
|
+
delete config.settings.docker.nbImageVariant;
|
|
541
|
+
}
|
|
542
|
+
break;
|
|
492
543
|
case 'bin.docker':
|
|
493
544
|
if (config.settings.bin) {
|
|
494
545
|
delete config.settings.bin.docker;
|
package/dist/lib/docker-image.js
CHANGED
|
@@ -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 (
|
|
79
|
+
if (hasKnownVariantSuffix(tag)) {
|
|
26
80
|
return tag;
|
|
27
81
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
+
}
|
package/dist/lib/env-config.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
|
+
import { normalizeEnvProxyConfig } from './env-proxy-config.js';
|
|
9
10
|
import { resolveAppPublicPath } from './app-public-path.js';
|
|
10
11
|
const STRING_ENV_CONFIG_KEYS = [
|
|
11
12
|
'source',
|
|
@@ -110,5 +111,9 @@ export function buildStoredEnvConfig(input) {
|
|
|
110
111
|
if ((authType === 'basic' || authType === 'token') && accessToken) {
|
|
111
112
|
envConfig.accessToken = accessToken;
|
|
112
113
|
}
|
|
114
|
+
const proxy = normalizeEnvProxyConfig(input.proxy);
|
|
115
|
+
if (proxy) {
|
|
116
|
+
envConfig.proxy = proxy;
|
|
117
|
+
}
|
|
113
118
|
return envConfig;
|
|
114
119
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
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 normalizeOptionalString(value) {
|
|
10
|
+
const normalized = String(value ?? '').trim();
|
|
11
|
+
return normalized || undefined;
|
|
12
|
+
}
|
|
13
|
+
function normalizeOptionalPort(value) {
|
|
14
|
+
const normalized = normalizeOptionalString(value);
|
|
15
|
+
if (!normalized || !/^\d+$/.test(normalized)) {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
const port = Number(normalized);
|
|
19
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
return port;
|
|
23
|
+
}
|
|
24
|
+
export function normalizeEnvProxyProviderConfig(value) {
|
|
25
|
+
if (!value || typeof value !== 'object') {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
return { ...value };
|
|
29
|
+
}
|
|
30
|
+
export function normalizeEnvProxyConfig(value) {
|
|
31
|
+
if (!value || typeof value !== 'object') {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
const proxy = value;
|
|
35
|
+
const host = normalizeOptionalString(proxy.host);
|
|
36
|
+
const port = normalizeOptionalPort(proxy.port);
|
|
37
|
+
const nginx = normalizeEnvProxyProviderConfig(proxy.nginx);
|
|
38
|
+
const caddy = normalizeEnvProxyProviderConfig(proxy.caddy);
|
|
39
|
+
if (!host && port === undefined && !nginx && !caddy) {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
...(host ? { host } : {}),
|
|
44
|
+
...(port !== undefined ? { port } : {}),
|
|
45
|
+
...(nginx ? { nginx } : {}),
|
|
46
|
+
...(caddy ? { caddy } : {}),
|
|
47
|
+
};
|
|
48
|
+
}
|
package/dist/lib/env-proxy.js
CHANGED
|
@@ -309,17 +309,30 @@ function createManualProxyEnvSettings(input) {
|
|
|
309
309
|
};
|
|
310
310
|
}
|
|
311
311
|
function normalizeManualNginxInput(input) {
|
|
312
|
+
const upstreamPort = trimValue(input.upstreamPort) ?? trimValue(input.appPort);
|
|
312
313
|
return {
|
|
313
314
|
name: String(input.name).trim(),
|
|
314
|
-
appPort: String(input.appPort).trim(),
|
|
315
315
|
storagePath: String(input.storagePath).trim(),
|
|
316
316
|
distRootPath: String(input.distRootPath).trim(),
|
|
317
317
|
runtimeVersion: String(input.runtimeVersion).trim(),
|
|
318
318
|
appPublicPath: trimValue(input.appPublicPath),
|
|
319
319
|
upstreamHost: trimValue(input.upstreamHost),
|
|
320
|
+
upstreamPort,
|
|
321
|
+
appPort: trimValue(input.appPort),
|
|
320
322
|
cdnBaseUrl: trimValue(input.cdnBaseUrl),
|
|
321
323
|
};
|
|
322
324
|
}
|
|
325
|
+
function normalizeProxyPort(value) {
|
|
326
|
+
const normalized = trimValue(value);
|
|
327
|
+
if (!normalized || !/^\d+$/.test(normalized)) {
|
|
328
|
+
return undefined;
|
|
329
|
+
}
|
|
330
|
+
const port = Number(normalized);
|
|
331
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
332
|
+
return undefined;
|
|
333
|
+
}
|
|
334
|
+
return normalized;
|
|
335
|
+
}
|
|
323
336
|
async function parseVersionFromPackageJson(content, sourceLabel) {
|
|
324
337
|
let parsed;
|
|
325
338
|
try {
|
|
@@ -474,6 +487,7 @@ function rewriteHtmlAssetPublicPath(html, currentPublicPath, nextPublicPath) {
|
|
|
474
487
|
}
|
|
475
488
|
function buildNginxManagedConfigBlock(context) {
|
|
476
489
|
const v2PublicPathNoTrailingSlash = trimTrailingSlash(context.v2PublicPath);
|
|
490
|
+
const apiBasePathNoTrailingSlash = trimTrailingSlash(context.apiBasePath);
|
|
477
491
|
const appPublicPathNoTrailingSlash = trimTrailingSlash(context.appPublicPath);
|
|
478
492
|
const isRootMounted = context.appPublicPath === '/';
|
|
479
493
|
const appPublicPathRedirectBlock = isRootMounted
|
|
@@ -512,6 +526,10 @@ function buildNginxManagedConfigBlock(context) {
|
|
|
512
526
|
` include ${context.snippetsDir}/proxy-location.conf;`,
|
|
513
527
|
' }',
|
|
514
528
|
'',
|
|
529
|
+
` location = ${apiBasePathNoTrailingSlash} {`,
|
|
530
|
+
` return 308 ${context.apiBasePath}$is_args$args;`,
|
|
531
|
+
' }',
|
|
532
|
+
'',
|
|
515
533
|
` location ^~ ${context.apiBasePath} {`,
|
|
516
534
|
` proxy_pass ${context.backendUrl};`,
|
|
517
535
|
` include ${context.snippetsDir}/proxy-location.conf;`,
|
|
@@ -563,7 +581,8 @@ function buildCaddyRuntimeConfig(context, variant) {
|
|
|
563
581
|
}
|
|
564
582
|
async function buildEnvProxyNginxRenderContext(source, options) {
|
|
565
583
|
const proxyHost = await resolveProxyUpstreamHost(options);
|
|
566
|
-
const
|
|
584
|
+
const upstreamPort = normalizeProxyPort(options?.upstreamPort) ?? source.apiPort;
|
|
585
|
+
const backendUrl = `http://${proxyHost}:${upstreamPort}`;
|
|
567
586
|
const cdnBaseUrl = source.settings.cdnBaseUrl ?? buildDefaultCdnBaseUrl(source.settings.appPublicPath, source.activeVersion);
|
|
568
587
|
const entryDir = resolveEnvProxyEntryDir(source.envName, { scope: options?.scope });
|
|
569
588
|
const publicDir = resolveEnvProxyNginxPublicOutputDir(source.envName, { scope: options?.scope });
|
|
@@ -637,7 +656,7 @@ async function resolveManualNginxBundleSource(input) {
|
|
|
637
656
|
storagePath: normalized.storagePath,
|
|
638
657
|
distRootPath: normalized.distRootPath,
|
|
639
658
|
settings: createManualProxyEnvSettings(normalized),
|
|
640
|
-
apiPort: normalized.
|
|
659
|
+
apiPort: normalized.upstreamPort,
|
|
641
660
|
activeVersion: normalized.runtimeVersion,
|
|
642
661
|
};
|
|
643
662
|
}
|
|
@@ -704,6 +723,7 @@ export async function buildManualEnvProxyNginxBundle(input, options) {
|
|
|
704
723
|
return await buildNginxBundleFromSource(await resolveManualNginxBundleSource(input), {
|
|
705
724
|
...options,
|
|
706
725
|
upstreamHost: trimValue(input.upstreamHost) ?? options?.upstreamHost,
|
|
726
|
+
upstreamPort: normalizeProxyPort(input.upstreamPort) ?? options?.upstreamPort,
|
|
707
727
|
});
|
|
708
728
|
}
|
|
709
729
|
async function buildNginxBundleFromSource(source, options) {
|
|
@@ -771,6 +791,7 @@ export async function buildManualEnvProxyCaddyBundle(input, options) {
|
|
|
771
791
|
return await buildCaddyBundleFromSource(await resolveManualNginxBundleSource(input), {
|
|
772
792
|
...options,
|
|
773
793
|
upstreamHost: trimValue(input.upstreamHost) ?? options?.upstreamHost,
|
|
794
|
+
upstreamPort: normalizeProxyPort(input.upstreamPort) ?? options?.upstreamPort,
|
|
774
795
|
});
|
|
775
796
|
}
|
|
776
797
|
async function buildCaddyBundleFromSource(source, options) {
|
|
@@ -973,6 +994,7 @@ function buildNginxOtherLocation(appPublicPath, v2PublicPath, modernClientPrefix
|
|
|
973
994
|
function renderNginxLocationTemplate(context) {
|
|
974
995
|
const proxyPassBlock = buildNginxProxyPassBlock(context.proxyHost, context.apiPort);
|
|
975
996
|
const wsProxyPassTarget = `http://${context.proxyHost}:${context.apiPort}${context.wsPath}`;
|
|
997
|
+
const apiBasePathNoTrailingSlash = trimTrailingSlash(context.apiBasePath);
|
|
976
998
|
return ` location ~* ^${context.appPublicPath}storage/uploads/(.*\\.md)$ {
|
|
977
999
|
alias ${context.uploadsPath}/$1;
|
|
978
1000
|
default_type text/markdown;
|
|
@@ -1018,6 +1040,10 @@ function renderNginxLocationTemplate(context) {
|
|
|
1018
1040
|
${proxyPassBlock}
|
|
1019
1041
|
}${context.otherLocation}
|
|
1020
1042
|
|
|
1043
|
+
location = ${apiBasePathNoTrailingSlash} {
|
|
1044
|
+
return 308 ${context.apiBasePath}$is_args$args;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1021
1047
|
location ^~ ${context.apiBasePath} {
|
|
1022
1048
|
${proxyPassBlock}
|
|
1023
1049
|
}
|