@nocobase/cli 2.2.0-beta.7 → 2.2.0-beta.9
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/app.conf.tpl +23 -0
- package/assets/env-proxy/nginx/nocobase.conf.tpl +5 -0
- package/assets/env-proxy/nginx/snippets/dist-location.conf +5 -0
- package/assets/env-proxy/nginx/snippets/gzip.conf +17 -0
- package/assets/env-proxy/nginx/snippets/log-format-http.conf +13 -0
- package/assets/env-proxy/nginx/snippets/maps-http.conf +14 -0
- package/assets/env-proxy/nginx/snippets/mime-types.conf +98 -0
- package/assets/env-proxy/nginx/snippets/proxy-location.conf +18 -0
- package/assets/env-proxy/nginx/snippets/spa-location.conf +6 -0
- package/assets/env-proxy/nginx/snippets/uploads-location.conf +21 -0
- package/dist/commands/app/start.js +4 -1
- package/dist/commands/install.js +53 -138
- package/dist/commands/proxy/caddy/generate.js +93 -7
- package/dist/commands/proxy/nginx/generate.js +98 -7
- package/dist/commands/revision/create.js +1 -1
- package/dist/commands/self/update.js +2 -2
- package/dist/commands/source/download.js +16 -12
- package/dist/lib/app-managed-resources.js +3 -4
- package/dist/lib/auth-store.js +82 -6
- package/dist/lib/cli-config.js +71 -1
- package/dist/lib/docker-image.js +94 -6
- package/dist/lib/env-auth.js +291 -45
- package/dist/lib/env-config.js +5 -0
- package/dist/lib/env-proxy-config.js +48 -0
- package/dist/lib/env-proxy.js +164 -58
- package/dist/lib/prompt-validators.js +1 -1
- package/dist/lib/proxy-caddy.js +77 -9
- package/dist/lib/proxy-nginx.js +71 -11
- package/dist/lib/run-npm.js +4 -0
- package/dist/lib/self-manager.js +251 -46
- package/dist/lib/startup-update.js +1 -1
- package/dist/locale/en-US.json +2 -2
- package/dist/locale/zh-CN.json +2 -2
- package/package.json +3 -2
package/dist/lib/env-proxy.js
CHANGED
|
@@ -259,7 +259,7 @@ export function applyEnvProxyAppEntryOptions(content, provider, options) {
|
|
|
259
259
|
function toCaddyPathMatcher(prefixPath) {
|
|
260
260
|
return `${prefixPath}*`;
|
|
261
261
|
}
|
|
262
|
-
export async function loadEnvProxySettings(runtime) {
|
|
262
|
+
export async function loadEnvProxySettings(runtime, options) {
|
|
263
263
|
const { envFilePath, envValues } = await readManagedRuntimeEnvValues(runtime);
|
|
264
264
|
const appPublicPath = resolveAppPublicPath(runtime.env.config.appPublicPath || envValues.APP_PUBLIC_PATH || DEFAULT_APP_PUBLIC_PATH);
|
|
265
265
|
const apiClientShareToken = /^true$/i.test(String(envValues.API_CLIENT_SHARE_TOKEN ?? '').trim());
|
|
@@ -274,7 +274,9 @@ export async function loadEnvProxySettings(runtime) {
|
|
|
274
274
|
wsPath: prefixRuntimePath(appPublicPath, envValues.WS_PATH || DEFAULT_WS_PATH),
|
|
275
275
|
pluginStaticsPath: prefixRuntimePath(appPublicPath, envValues.PLUGIN_STATICS_PATH || DEFAULT_PLUGIN_STATICS_PATH, { trailingSlash: true }),
|
|
276
276
|
modernClientPrefix: normalizeModernClientPrefix(envValues.APP_MODERN_CLIENT_PREFIX),
|
|
277
|
-
cdnBaseUrl: trimValue(
|
|
277
|
+
cdnBaseUrl: trimValue(options?.cdnBaseUrl) ??
|
|
278
|
+
trimValue(runtime.env.envVars?.CDN_BASE_URL) ??
|
|
279
|
+
trimValue(envValues.CDN_BASE_URL),
|
|
278
280
|
apiClientStoragePrefix: trimValue(envValues.API_CLIENT_STORAGE_PREFIX) ?? DEFAULT_API_CLIENT_STORAGE_PREFIX,
|
|
279
281
|
apiClientStorageType: trimValue(envValues.API_CLIENT_STORAGE_TYPE) ?? DEFAULT_API_CLIENT_STORAGE_TYPE,
|
|
280
282
|
apiClientShareToken,
|
|
@@ -284,6 +286,53 @@ export async function loadEnvProxySettings(runtime) {
|
|
|
284
286
|
},
|
|
285
287
|
};
|
|
286
288
|
}
|
|
289
|
+
function createManualProxyEnvSettings(input) {
|
|
290
|
+
const appPublicPath = resolveAppPublicPath(input.appPublicPath || DEFAULT_APP_PUBLIC_PATH);
|
|
291
|
+
return {
|
|
292
|
+
appPublicPath,
|
|
293
|
+
apiBasePath: prefixRuntimePath(appPublicPath, DEFAULT_API_BASE_PATH, {
|
|
294
|
+
trailingSlash: true,
|
|
295
|
+
}),
|
|
296
|
+
distPath: resolveDistPublicPath(appPublicPath),
|
|
297
|
+
wsPath: prefixRuntimePath(appPublicPath, DEFAULT_WS_PATH),
|
|
298
|
+
pluginStaticsPath: prefixRuntimePath(appPublicPath, DEFAULT_PLUGIN_STATICS_PATH, {
|
|
299
|
+
trailingSlash: true,
|
|
300
|
+
}),
|
|
301
|
+
modernClientPrefix: DEFAULT_MODERN_CLIENT_PREFIX,
|
|
302
|
+
cdnBaseUrl: trimValue(input.cdnBaseUrl),
|
|
303
|
+
apiClientStoragePrefix: DEFAULT_API_CLIENT_STORAGE_PREFIX,
|
|
304
|
+
apiClientStorageType: DEFAULT_API_CLIENT_STORAGE_TYPE,
|
|
305
|
+
apiClientShareToken: false,
|
|
306
|
+
wsUrl: '',
|
|
307
|
+
esmCdnBaseUrl: DEFAULT_ESM_CDN_BASE_URL,
|
|
308
|
+
esmCdnSuffix: '',
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
function normalizeManualNginxInput(input) {
|
|
312
|
+
const upstreamPort = trimValue(input.upstreamPort) ?? trimValue(input.appPort);
|
|
313
|
+
return {
|
|
314
|
+
name: String(input.name).trim(),
|
|
315
|
+
storagePath: String(input.storagePath).trim(),
|
|
316
|
+
distRootPath: String(input.distRootPath).trim(),
|
|
317
|
+
runtimeVersion: String(input.runtimeVersion).trim(),
|
|
318
|
+
appPublicPath: trimValue(input.appPublicPath),
|
|
319
|
+
upstreamHost: trimValue(input.upstreamHost),
|
|
320
|
+
upstreamPort,
|
|
321
|
+
appPort: trimValue(input.appPort),
|
|
322
|
+
cdnBaseUrl: trimValue(input.cdnBaseUrl),
|
|
323
|
+
};
|
|
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
|
+
}
|
|
287
336
|
async function parseVersionFromPackageJson(content, sourceLabel) {
|
|
288
337
|
let parsed;
|
|
289
338
|
try {
|
|
@@ -438,6 +487,7 @@ function rewriteHtmlAssetPublicPath(html, currentPublicPath, nextPublicPath) {
|
|
|
438
487
|
}
|
|
439
488
|
function buildNginxManagedConfigBlock(context) {
|
|
440
489
|
const v2PublicPathNoTrailingSlash = trimTrailingSlash(context.v2PublicPath);
|
|
490
|
+
const apiBasePathNoTrailingSlash = trimTrailingSlash(context.apiBasePath);
|
|
441
491
|
const appPublicPathNoTrailingSlash = trimTrailingSlash(context.appPublicPath);
|
|
442
492
|
const isRootMounted = context.appPublicPath === '/';
|
|
443
493
|
const appPublicPathRedirectBlock = isRootMounted
|
|
@@ -476,6 +526,10 @@ function buildNginxManagedConfigBlock(context) {
|
|
|
476
526
|
` include ${context.snippetsDir}/proxy-location.conf;`,
|
|
477
527
|
' }',
|
|
478
528
|
'',
|
|
529
|
+
` location = ${apiBasePathNoTrailingSlash} {`,
|
|
530
|
+
` return 308 ${context.apiBasePath}$is_args$args;`,
|
|
531
|
+
' }',
|
|
532
|
+
'',
|
|
479
533
|
` location ^~ ${context.apiBasePath} {`,
|
|
480
534
|
` proxy_pass ${context.backendUrl};`,
|
|
481
535
|
` include ${context.snippetsDir}/proxy-location.conf;`,
|
|
@@ -525,65 +579,92 @@ function buildNginxRuntimeConfig(context, variant) {
|
|
|
525
579
|
function buildCaddyRuntimeConfig(context, variant) {
|
|
526
580
|
return buildNginxRuntimeConfig(context, variant);
|
|
527
581
|
}
|
|
528
|
-
async function buildEnvProxyNginxRenderContext(
|
|
529
|
-
const apiPort = trimValue(runtime.env.appPort ?? runtime.env.config.appPort);
|
|
530
|
-
if (!apiPort) {
|
|
531
|
-
throw new Error(translateCli('commands.envProxy.errors.missingAppPort', { envName: runtime.envName }, {
|
|
532
|
-
fallback: `Missing appPort for env "${runtime.envName}". Save or update the app port before generating proxy config.`,
|
|
533
|
-
}));
|
|
534
|
-
}
|
|
535
|
-
const activeVersion = trimValue(await readDistClientActiveVersion(runtime.env.storagePath));
|
|
536
|
-
if (!activeVersion) {
|
|
537
|
-
throw new Error(translateCli('commands.envProxy.errors.missingVersion', { envName: runtime.envName }, {
|
|
538
|
-
fallback: `Couldn't determine the app version for env "${runtime.envName}". Run \`nb env update ${runtime.envName}\` and try again.`,
|
|
539
|
-
}));
|
|
540
|
-
}
|
|
541
|
-
const { envFilePath, settings } = await loadEnvProxySettings(runtime);
|
|
582
|
+
async function buildEnvProxyNginxRenderContext(source, options) {
|
|
542
583
|
const proxyHost = await resolveProxyUpstreamHost(options);
|
|
543
|
-
const
|
|
544
|
-
const
|
|
545
|
-
const
|
|
546
|
-
const
|
|
584
|
+
const upstreamPort = normalizeProxyPort(options?.upstreamPort) ?? source.apiPort;
|
|
585
|
+
const backendUrl = `http://${proxyHost}:${upstreamPort}`;
|
|
586
|
+
const cdnBaseUrl = source.settings.cdnBaseUrl ?? buildDefaultCdnBaseUrl(source.settings.appPublicPath, source.activeVersion);
|
|
587
|
+
const entryDir = resolveEnvProxyEntryDir(source.envName, { scope: options?.scope });
|
|
588
|
+
const publicDir = resolveEnvProxyNginxPublicOutputDir(source.envName, { scope: options?.scope });
|
|
547
589
|
const snippetsDir = resolveEnvProxyNginxSnippetsOutputDir({ scope: options?.scope });
|
|
548
|
-
const distRootDir =
|
|
549
|
-
const uploadsDir = path.join(
|
|
590
|
+
const distRootDir = source.distRootPath;
|
|
591
|
+
const uploadsDir = path.join(source.storagePath, 'uploads');
|
|
550
592
|
const mappedEntryDir = await mapProxyPathFromCliRoot(entryDir, options);
|
|
551
593
|
const mappedPublicDir = await mapProxyPathFromCliRoot(publicDir, options);
|
|
552
594
|
const mappedSnippetsDir = await mapProxyPathFromCliRoot(snippetsDir, options);
|
|
553
595
|
const mappedDistRootDir = await mapProxyPathFromCliRoot(distRootDir, options);
|
|
554
596
|
const mappedUploadsDir = await mapProxyPathFromCliRoot(uploadsDir, options);
|
|
555
|
-
const v2PublicPath = `${settings.appPublicPath.replace(/\/$/, '')}/${settings.modernClientPrefix}/`;
|
|
597
|
+
const v2PublicPath = `${source.settings.appPublicPath.replace(/\/$/, '')}/${source.settings.modernClientPrefix}/`;
|
|
556
598
|
return {
|
|
557
|
-
envName:
|
|
558
|
-
envFilePath,
|
|
559
|
-
apiBasePath: settings.apiBasePath,
|
|
560
|
-
apiClientShareToken: settings.apiClientShareToken,
|
|
561
|
-
apiClientStoragePrefix: settings.apiClientStoragePrefix,
|
|
562
|
-
apiClientStorageType: settings.apiClientStorageType,
|
|
563
|
-
apiPort,
|
|
564
|
-
appPublicPath: settings.appPublicPath,
|
|
599
|
+
envName: source.envName,
|
|
600
|
+
envFilePath: source.envFilePath,
|
|
601
|
+
apiBasePath: source.settings.apiBasePath,
|
|
602
|
+
apiClientShareToken: source.settings.apiClientShareToken,
|
|
603
|
+
apiClientStoragePrefix: source.settings.apiClientStoragePrefix,
|
|
604
|
+
apiClientStorageType: source.settings.apiClientStorageType,
|
|
605
|
+
apiPort: source.apiPort,
|
|
606
|
+
appPublicPath: source.settings.appPublicPath,
|
|
565
607
|
backendUrl,
|
|
566
608
|
cdnBaseUrl: ensureTrailingSlash(cdnBaseUrl),
|
|
567
|
-
distPath: settings.distPath,
|
|
609
|
+
distPath: source.settings.distPath,
|
|
568
610
|
distRootDir: mappedDistRootDir,
|
|
569
611
|
entryDir: mappedEntryDir,
|
|
570
612
|
publicDir: mappedPublicDir,
|
|
571
|
-
esmCdnBaseUrl: settings.esmCdnBaseUrl,
|
|
572
|
-
esmCdnSuffix: settings.esmCdnSuffix,
|
|
573
|
-
indexV1Path: await mapProxyPathFromCliRoot(resolveEnvProxyNginxIndexOutputPath(
|
|
574
|
-
indexV2Path: await mapProxyPathFromCliRoot(resolveEnvProxyNginxIndexOutputPath(
|
|
575
|
-
modernClientPrefix: settings.modernClientPrefix,
|
|
613
|
+
esmCdnBaseUrl: source.settings.esmCdnBaseUrl,
|
|
614
|
+
esmCdnSuffix: source.settings.esmCdnSuffix,
|
|
615
|
+
indexV1Path: await mapProxyPathFromCliRoot(resolveEnvProxyNginxIndexOutputPath(source.envName, 'v1', { scope: options?.scope }), options),
|
|
616
|
+
indexV2Path: await mapProxyPathFromCliRoot(resolveEnvProxyNginxIndexOutputPath(source.envName, 'v2', { scope: options?.scope }), options),
|
|
617
|
+
modernClientPrefix: source.settings.modernClientPrefix,
|
|
576
618
|
proxyHost,
|
|
577
619
|
snippetsDir: mappedSnippetsDir,
|
|
578
620
|
uploadsDir: mappedUploadsDir,
|
|
579
621
|
v2PublicPath,
|
|
580
|
-
wsPath: settings.wsPath,
|
|
581
|
-
wsUrl: settings.wsUrl,
|
|
622
|
+
wsPath: source.settings.wsPath,
|
|
623
|
+
wsUrl: source.settings.wsUrl,
|
|
624
|
+
activeVersion: source.activeVersion,
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
async function resolveRuntimeNginxBundleSource(runtime, options) {
|
|
628
|
+
const apiPort = trimValue(runtime.env.appPort ?? runtime.env.config.appPort);
|
|
629
|
+
if (!apiPort) {
|
|
630
|
+
throw new Error(translateCli('commands.envProxy.errors.missingAppPort', { envName: runtime.envName }, {
|
|
631
|
+
fallback: `Missing appPort for env "${runtime.envName}". Save or update the app port before generating proxy config.`,
|
|
632
|
+
}));
|
|
633
|
+
}
|
|
634
|
+
const activeVersion = trimValue(await readDistClientActiveVersion(runtime.env.storagePath));
|
|
635
|
+
if (!activeVersion) {
|
|
636
|
+
throw new Error(translateCli('commands.envProxy.errors.missingVersion', { envName: runtime.envName }, {
|
|
637
|
+
fallback: `Couldn't determine the app version for env "${runtime.envName}". Run \`nb env update ${runtime.envName}\` and try again.`,
|
|
638
|
+
}));
|
|
639
|
+
}
|
|
640
|
+
const { envFilePath, settings } = await loadEnvProxySettings(runtime, options);
|
|
641
|
+
return {
|
|
642
|
+
envName: runtime.envName,
|
|
643
|
+
envFilePath,
|
|
644
|
+
storagePath: runtime.env.storagePath,
|
|
645
|
+
distRootPath: resolveDistClientRoot(runtime.env.storagePath),
|
|
646
|
+
settings,
|
|
647
|
+
apiPort,
|
|
582
648
|
activeVersion,
|
|
583
649
|
};
|
|
584
650
|
}
|
|
651
|
+
async function resolveManualNginxBundleSource(input) {
|
|
652
|
+
const normalized = normalizeManualNginxInput(input);
|
|
653
|
+
return {
|
|
654
|
+
envName: normalized.name,
|
|
655
|
+
envFilePath: undefined,
|
|
656
|
+
storagePath: normalized.storagePath,
|
|
657
|
+
distRootPath: normalized.distRootPath,
|
|
658
|
+
settings: createManualProxyEnvSettings(normalized),
|
|
659
|
+
apiPort: normalized.upstreamPort,
|
|
660
|
+
activeVersion: normalized.runtimeVersion,
|
|
661
|
+
};
|
|
662
|
+
}
|
|
585
663
|
async function buildEnvProxyCaddyRenderContext(runtime, options) {
|
|
586
|
-
return await
|
|
664
|
+
return await buildEnvProxyCaddyRenderContextFromSource(await resolveRuntimeNginxBundleSource(runtime), options);
|
|
665
|
+
}
|
|
666
|
+
async function buildEnvProxyCaddyRenderContextFromSource(source, options) {
|
|
667
|
+
return await buildEnvProxyNginxRenderContext(source, {
|
|
587
668
|
...options,
|
|
588
669
|
provider: 'caddy',
|
|
589
670
|
});
|
|
@@ -636,11 +717,21 @@ export function resolveEnvProxyCaddyIndexOutputPath(envName, variant, options) {
|
|
|
636
717
|
return path.join(resolveEnvProxyCaddyPublicOutputDir(envName, { scope: options?.scope }), `index-${variant}.html`);
|
|
637
718
|
}
|
|
638
719
|
export async function buildEnvProxyNginxBundle(runtime, options) {
|
|
639
|
-
|
|
720
|
+
return await buildNginxBundleFromSource(await resolveRuntimeNginxBundleSource(runtime, options), options);
|
|
721
|
+
}
|
|
722
|
+
export async function buildManualEnvProxyNginxBundle(input, options) {
|
|
723
|
+
return await buildNginxBundleFromSource(await resolveManualNginxBundleSource(input), {
|
|
724
|
+
...options,
|
|
725
|
+
upstreamHost: trimValue(input.upstreamHost) ?? options?.upstreamHost,
|
|
726
|
+
upstreamPort: normalizeProxyPort(input.upstreamPort) ?? options?.upstreamPort,
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
async function buildNginxBundleFromSource(source, options) {
|
|
730
|
+
const context = await buildEnvProxyNginxRenderContext(source, options);
|
|
640
731
|
const appTemplate = await readEnvProxyNginxAssetText('app.conf.tpl');
|
|
641
732
|
const mainTemplate = await readEnvProxyNginxAssetText('nocobase.conf.tpl');
|
|
642
|
-
const sourceIndexV1Path = path.join(
|
|
643
|
-
const sourceIndexV2Path = path.join(
|
|
733
|
+
const sourceIndexV1Path = path.join(source.distRootPath, context.activeVersion, 'index.html');
|
|
734
|
+
const sourceIndexV2Path = path.join(source.distRootPath, context.activeVersion, DEFAULT_MODERN_CLIENT_PREFIX, 'index.html');
|
|
644
735
|
const [sourceIndexV1Content, sourceIndexV2Content] = await Promise.all([
|
|
645
736
|
readFile(sourceIndexV1Path, 'utf8'),
|
|
646
737
|
readFile(sourceIndexV2Path, 'utf8'),
|
|
@@ -667,13 +758,13 @@ export async function buildEnvProxyNginxBundle(runtime, options) {
|
|
|
667
758
|
wsPath: context.wsPath,
|
|
668
759
|
};
|
|
669
760
|
return {
|
|
670
|
-
envName:
|
|
761
|
+
envName: source.envName,
|
|
671
762
|
envFilePath: context.envFilePath,
|
|
672
|
-
entryDir: resolveEnvProxyEntryDir(
|
|
673
|
-
publicDir: resolveEnvProxyNginxPublicOutputDir(
|
|
674
|
-
appConfigPath: resolveEnvProxyAppOutputPath(
|
|
675
|
-
indexV1Path: resolveEnvProxyNginxIndexOutputPath(
|
|
676
|
-
indexV2Path: resolveEnvProxyNginxIndexOutputPath(
|
|
763
|
+
entryDir: resolveEnvProxyEntryDir(source.envName, { scope: options?.scope, provider: 'nginx' }),
|
|
764
|
+
publicDir: resolveEnvProxyNginxPublicOutputDir(source.envName, { scope: options?.scope }),
|
|
765
|
+
appConfigPath: resolveEnvProxyAppOutputPath(source.envName, { scope: options?.scope, provider: 'nginx' }),
|
|
766
|
+
indexV1Path: resolveEnvProxyNginxIndexOutputPath(source.envName, 'v1', { scope: options?.scope }),
|
|
767
|
+
indexV2Path: resolveEnvProxyNginxIndexOutputPath(source.envName, 'v2', { scope: options?.scope }),
|
|
677
768
|
mainConfigPath: resolveEnvProxyMainOutputPath({ scope: options?.scope, provider: 'nginx' }),
|
|
678
769
|
snippetsDir: resolveEnvProxyNginxSnippetsOutputDir({ scope: options?.scope }),
|
|
679
770
|
appPublicPath: context.appPublicPath,
|
|
@@ -694,9 +785,19 @@ export async function buildEnvProxyNginxBundle(runtime, options) {
|
|
|
694
785
|
};
|
|
695
786
|
}
|
|
696
787
|
export async function buildEnvProxyCaddyBundle(runtime, options) {
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
788
|
+
return await buildCaddyBundleFromSource(await resolveRuntimeNginxBundleSource(runtime), options);
|
|
789
|
+
}
|
|
790
|
+
export async function buildManualEnvProxyCaddyBundle(input, options) {
|
|
791
|
+
return await buildCaddyBundleFromSource(await resolveManualNginxBundleSource(input), {
|
|
792
|
+
...options,
|
|
793
|
+
upstreamHost: trimValue(input.upstreamHost) ?? options?.upstreamHost,
|
|
794
|
+
upstreamPort: normalizeProxyPort(input.upstreamPort) ?? options?.upstreamPort,
|
|
795
|
+
});
|
|
796
|
+
}
|
|
797
|
+
async function buildCaddyBundleFromSource(source, options) {
|
|
798
|
+
const context = await buildEnvProxyCaddyRenderContextFromSource(source, options);
|
|
799
|
+
const sourceIndexV1Path = path.join(source.distRootPath, context.activeVersion, 'index.html');
|
|
800
|
+
const sourceIndexV2Path = path.join(source.distRootPath, context.activeVersion, DEFAULT_MODERN_CLIENT_PREFIX, 'index.html');
|
|
700
801
|
const [sourceIndexV1Content, sourceIndexV2Content] = await Promise.all([
|
|
701
802
|
readFile(sourceIndexV1Path, 'utf8'),
|
|
702
803
|
readFile(sourceIndexV2Path, 'utf8'),
|
|
@@ -707,9 +808,9 @@ export async function buildEnvProxyCaddyBundle(runtime, options) {
|
|
|
707
808
|
const sourceV2PublicPath = extractRuntimePublicPath(sourceIndexV2Content);
|
|
708
809
|
const indexV1AssetPublicPath = context.cdnBaseUrl;
|
|
709
810
|
const indexV2AssetPublicPath = `${trimTrailingSlash(context.cdnBaseUrl)}/${DEFAULT_MODERN_CLIENT_PREFIX}/`;
|
|
710
|
-
const appConfigPath = resolveEnvProxyAppOutputPath(
|
|
711
|
-
const entryDir = resolveEnvProxyEntryDir(
|
|
712
|
-
const publicDir = resolveEnvProxyCaddyPublicOutputDir(
|
|
811
|
+
const appConfigPath = resolveEnvProxyAppOutputPath(source.envName, { scope: options?.scope, provider: 'caddy' });
|
|
812
|
+
const entryDir = resolveEnvProxyEntryDir(source.envName, { scope: options?.scope, provider: 'caddy' });
|
|
813
|
+
const publicDir = resolveEnvProxyCaddyPublicOutputDir(source.envName, { scope: options?.scope });
|
|
713
814
|
const renderedPublicDir = await mapProxyPathFromCliRoot(publicDir, { ...options, provider: 'caddy' });
|
|
714
815
|
const appConfigContent = renderCaddyAppTemplate(buildCaddySiteAddress(), {
|
|
715
816
|
appPublicPath: context.appPublicPath,
|
|
@@ -725,13 +826,13 @@ export async function buildEnvProxyCaddyBundle(runtime, options) {
|
|
|
725
826
|
wsPath: context.wsPath,
|
|
726
827
|
}, renderedPublicDir);
|
|
727
828
|
return {
|
|
728
|
-
envName:
|
|
829
|
+
envName: source.envName,
|
|
729
830
|
envFilePath: context.envFilePath,
|
|
730
831
|
entryDir,
|
|
731
832
|
publicDir,
|
|
732
833
|
appConfigPath,
|
|
733
|
-
indexV1Path: resolveEnvProxyCaddyIndexOutputPath(
|
|
734
|
-
indexV2Path: resolveEnvProxyCaddyIndexOutputPath(
|
|
834
|
+
indexV1Path: resolveEnvProxyCaddyIndexOutputPath(source.envName, 'v1', { scope: options?.scope }),
|
|
835
|
+
indexV2Path: resolveEnvProxyCaddyIndexOutputPath(source.envName, 'v2', { scope: options?.scope }),
|
|
735
836
|
mainConfigPath: resolveEnvProxyMainOutputPath({ scope: options?.scope, provider: 'caddy' }),
|
|
736
837
|
appPublicPath: context.appPublicPath,
|
|
737
838
|
apiBasePath: context.apiBasePath,
|
|
@@ -893,6 +994,7 @@ function buildNginxOtherLocation(appPublicPath, v2PublicPath, modernClientPrefix
|
|
|
893
994
|
function renderNginxLocationTemplate(context) {
|
|
894
995
|
const proxyPassBlock = buildNginxProxyPassBlock(context.proxyHost, context.apiPort);
|
|
895
996
|
const wsProxyPassTarget = `http://${context.proxyHost}:${context.apiPort}${context.wsPath}`;
|
|
997
|
+
const apiBasePathNoTrailingSlash = trimTrailingSlash(context.apiBasePath);
|
|
896
998
|
return ` location ~* ^${context.appPublicPath}storage/uploads/(.*\\.md)$ {
|
|
897
999
|
alias ${context.uploadsPath}/$1;
|
|
898
1000
|
default_type text/markdown;
|
|
@@ -938,6 +1040,10 @@ function renderNginxLocationTemplate(context) {
|
|
|
938
1040
|
${proxyPassBlock}
|
|
939
1041
|
}${context.otherLocation}
|
|
940
1042
|
|
|
1043
|
+
location = ${apiBasePathNoTrailingSlash} {
|
|
1044
|
+
return 308 ${context.apiBasePath}$is_args$args;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
941
1047
|
location ^~ ${context.apiBasePath} {
|
|
942
1048
|
${proxyPassBlock}
|
|
943
1049
|
}
|
|
@@ -10,7 +10,7 @@ import { spawn } from 'node:child_process';
|
|
|
10
10
|
import net from 'node:net';
|
|
11
11
|
import { translateCli } from "./cli-locale.js";
|
|
12
12
|
const API_BASE_URL_EXAMPLE = 'http://localhost:13000/api';
|
|
13
|
-
const ENV_KEY_PATTERN = /^[A-Za-z0-
|
|
13
|
+
const ENV_KEY_PATTERN = /^[A-Za-z0-9_-]+$/;
|
|
14
14
|
const APP_PUBLIC_PATH_PATTERN = /^\/(?:[A-Za-z0-9_-]+(?:\/[A-Za-z0-9_-]+)*)?\/?$/;
|
|
15
15
|
const TCP_PORT_EXAMPLE = '13000';
|
|
16
16
|
const API_BASE_URL_REQUEST_TIMEOUT_MS = 5_000;
|
package/dist/lib/proxy-caddy.js
CHANGED
|
@@ -9,14 +9,17 @@
|
|
|
9
9
|
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
10
10
|
import path from 'node:path';
|
|
11
11
|
import { dockerContainerExists, dockerContainerIsRunning, startDockerContainer, stopDockerContainer, } from './app-runtime.js';
|
|
12
|
+
import { loadAuthConfig } from './auth-store.js';
|
|
12
13
|
import { CADDY_PROXY_DRIVER_OPTIONS, DEFAULT_CADDY_PROXY_DRIVER, getCliConfigValue, normalizeCaddyProxyDriver, resolveDockerContainerPrefix, setCliConfigValue, } from './cli-config.js';
|
|
13
14
|
import { resolveCliHomeRoot } from './cli-home.js';
|
|
14
|
-
import { applyEnvProxyAppEntryOptions, buildEnvProxyCaddyBundle, buildEnvProxyMainConfig, mapProxyPathFromCliRoot, resolveEnvProxyMainOutputPath, } from './env-proxy.js';
|
|
15
|
-
import {
|
|
15
|
+
import { applyEnvProxyAppEntryOptions, buildManualEnvProxyCaddyBundle, buildEnvProxyCaddyBundle, buildEnvProxyMainConfig, mapProxyPathFromCliRoot, resolveEnvProxyMainOutputPath, } from './env-proxy.js';
|
|
16
|
+
import { normalizeEnvProxyConfig } from './env-proxy-config.js';
|
|
17
|
+
import { commandOutput, run } from './run-npm.js';
|
|
16
18
|
const DOCKER_CADDY_PROXY_CONTAINER_SUFFIX = 'caddy-proxy';
|
|
17
19
|
const DOCKER_CADDY_PROXY_IMAGE = 'caddy:latest';
|
|
18
20
|
const DOCKER_CADDY_PROXY_RUNTIME_ROOT = '/apps';
|
|
19
21
|
const DOCKER_CADDY_PROXY_CONF_DESTINATION = '/etc/caddy/Caddyfile';
|
|
22
|
+
const DEFAULT_DOCKER_CADDY_PROXY_PUBLISHED_PORTS = [80, 443];
|
|
20
23
|
async function readOptionalTextFile(filePath) {
|
|
21
24
|
try {
|
|
22
25
|
return await readFile(filePath, 'utf8');
|
|
@@ -62,8 +65,9 @@ export async function resolveCaddyProxyRuntimeContext(options) {
|
|
|
62
65
|
upstreamHost: resolveCaddyProxyUpstreamHost(driver),
|
|
63
66
|
};
|
|
64
67
|
}
|
|
65
|
-
export async function writeCaddyProxyBundle(runtime, appEntryOptions, runtimeContext) {
|
|
68
|
+
export async function writeCaddyProxyBundle(runtime, appEntryOptions, runtimeContext, options) {
|
|
66
69
|
const bundle = await buildEnvProxyCaddyBundle(runtime, {
|
|
70
|
+
cdnBaseUrl: options?.cdnBaseUrl,
|
|
67
71
|
runtimeCliRoot: runtimeContext.runtimeCliRoot,
|
|
68
72
|
upstreamHost: runtimeContext.upstreamHost,
|
|
69
73
|
});
|
|
@@ -82,6 +86,27 @@ export async function writeCaddyProxyBundle(runtime, appEntryOptions, runtimeCon
|
|
|
82
86
|
status,
|
|
83
87
|
};
|
|
84
88
|
}
|
|
89
|
+
export async function writeManualCaddyProxyBundle(input, appEntryOptions, runtimeContext, options) {
|
|
90
|
+
const bundle = await buildManualEnvProxyCaddyBundle(input, {
|
|
91
|
+
cdnBaseUrl: options?.cdnBaseUrl,
|
|
92
|
+
runtimeCliRoot: runtimeContext.runtimeCliRoot,
|
|
93
|
+
upstreamHost: input.upstreamHost || runtimeContext.upstreamHost,
|
|
94
|
+
});
|
|
95
|
+
const currentAppConfigContent = await readOptionalTextFile(bundle.appConfigPath);
|
|
96
|
+
const nextAppConfigContent = applyEnvProxyAppEntryOptions(bundle.appConfigContent, 'caddy', appEntryOptions);
|
|
97
|
+
const status = currentAppConfigContent ? 'updated' : 'created';
|
|
98
|
+
await Promise.all([mkdir(bundle.entryDir, { recursive: true }), mkdir(bundle.publicDir, { recursive: true })]);
|
|
99
|
+
await Promise.all([
|
|
100
|
+
writeFile(bundle.appConfigPath, nextAppConfigContent, 'utf8'),
|
|
101
|
+
writeFile(bundle.indexV1Path, bundle.indexV1Content, 'utf8'),
|
|
102
|
+
writeFile(bundle.indexV2Path, bundle.indexV2Content, 'utf8'),
|
|
103
|
+
writeFile(bundle.mainConfigPath, bundle.mainConfigContent, 'utf8'),
|
|
104
|
+
]);
|
|
105
|
+
return {
|
|
106
|
+
bundle,
|
|
107
|
+
status,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
85
110
|
function resolveLocalCaddyPidFilePath() {
|
|
86
111
|
return path.join(path.dirname(resolveEnvProxyMainOutputPath({ provider: 'caddy' })), 'caddy.pid');
|
|
87
112
|
}
|
|
@@ -149,11 +174,16 @@ async function reloadLocalCaddyProxy(runtimeContext) {
|
|
|
149
174
|
}
|
|
150
175
|
async function ensureDockerCaddyProxyContainer(runtimeContext) {
|
|
151
176
|
const containerName = await resolveCaddyProxyContainerName();
|
|
177
|
+
const mainConfigPath = await ensureCaddyProxyMainConfig(runtimeContext);
|
|
178
|
+
const publishedPorts = await resolveDockerCaddyPublishedPorts();
|
|
152
179
|
if (await dockerContainerExists(containerName)) {
|
|
153
|
-
|
|
180
|
+
if (await dockerCaddyProxyContainerMatchesPublishedPorts(containerName, publishedPorts)) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
await removeDockerCaddyProxyContainer(containerName);
|
|
154
184
|
}
|
|
155
185
|
const hostCliRoot = String(process.env.NB_CLI_ROOT ?? resolveCliHomeRoot()).trim() || resolveCliHomeRoot();
|
|
156
|
-
const
|
|
186
|
+
const dockerPortArgs = publishedPorts.flatMap((port) => ['-p', `${port}:${port}`]);
|
|
157
187
|
await run('docker', [
|
|
158
188
|
'run',
|
|
159
189
|
'-d',
|
|
@@ -161,8 +191,7 @@ async function ensureDockerCaddyProxyContainer(runtimeContext) {
|
|
|
161
191
|
containerName,
|
|
162
192
|
'--add-host',
|
|
163
193
|
'host.docker.internal:host-gateway',
|
|
164
|
-
|
|
165
|
-
'80:80',
|
|
194
|
+
...dockerPortArgs,
|
|
166
195
|
'-v',
|
|
167
196
|
`${hostCliRoot}:${DOCKER_CADDY_PROXY_RUNTIME_ROOT}`,
|
|
168
197
|
'-v',
|
|
@@ -173,14 +202,53 @@ async function ensureDockerCaddyProxyContainer(runtimeContext) {
|
|
|
173
202
|
stdio: 'ignore',
|
|
174
203
|
});
|
|
175
204
|
}
|
|
205
|
+
async function resolveDockerCaddyPublishedPorts() {
|
|
206
|
+
const config = await loadAuthConfig();
|
|
207
|
+
const ports = new Set(DEFAULT_DOCKER_CADDY_PROXY_PUBLISHED_PORTS);
|
|
208
|
+
for (const envConfig of Object.values(config.envs)) {
|
|
209
|
+
const port = normalizeEnvProxyConfig(envConfig.proxy)?.port;
|
|
210
|
+
if (port !== undefined) {
|
|
211
|
+
ports.add(port);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return Array.from(ports).sort((left, right) => left - right);
|
|
215
|
+
}
|
|
216
|
+
async function readDockerCaddyPublishedPorts(containerName) {
|
|
217
|
+
const output = await commandOutput('docker', ['inspect', '--format', '{{json .HostConfig.PortBindings}}', containerName], { errorName: 'docker inspect' });
|
|
218
|
+
const parsed = JSON.parse(output.trim() || '{}');
|
|
219
|
+
const ports = new Set();
|
|
220
|
+
for (const bindings of Object.values(parsed)) {
|
|
221
|
+
if (!Array.isArray(bindings)) {
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
for (const binding of bindings) {
|
|
225
|
+
const port = Number.parseInt(String(binding?.HostPort ?? '').trim(), 10);
|
|
226
|
+
if (Number.isInteger(port) && port > 0) {
|
|
227
|
+
ports.add(port);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return Array.from(ports).sort((left, right) => left - right);
|
|
232
|
+
}
|
|
233
|
+
async function dockerCaddyProxyContainerMatchesPublishedPorts(containerName, expectedPorts) {
|
|
234
|
+
const currentPorts = await readDockerCaddyPublishedPorts(containerName);
|
|
235
|
+
return currentPorts.length === expectedPorts.length && currentPorts.every((port, index) => port === expectedPorts[index]);
|
|
236
|
+
}
|
|
237
|
+
async function removeDockerCaddyProxyContainer(containerName) {
|
|
238
|
+
await run('docker', ['rm', '-f', containerName], {
|
|
239
|
+
errorName: 'docker rm',
|
|
240
|
+
stdio: 'ignore',
|
|
241
|
+
});
|
|
242
|
+
}
|
|
176
243
|
async function startDockerCaddyProxy(runtimeContext) {
|
|
177
244
|
const containerName = await resolveCaddyProxyContainerName();
|
|
178
245
|
await ensureCaddyProxyMainConfig(runtimeContext);
|
|
179
|
-
|
|
246
|
+
const existedBeforeEnsure = await dockerContainerExists(containerName);
|
|
247
|
+
await ensureDockerCaddyProxyContainer(runtimeContext);
|
|
248
|
+
if (existedBeforeEnsure && (await dockerContainerExists(containerName))) {
|
|
180
249
|
const state = await startDockerContainer(containerName, { stdio: 'ignore' });
|
|
181
250
|
return state === 'already-running' ? 'already-running' : 'started';
|
|
182
251
|
}
|
|
183
|
-
await ensureDockerCaddyProxyContainer(runtimeContext);
|
|
184
252
|
return 'started';
|
|
185
253
|
}
|
|
186
254
|
async function stopDockerCaddyProxy() {
|
package/dist/lib/proxy-nginx.js
CHANGED
|
@@ -9,14 +9,17 @@
|
|
|
9
9
|
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
10
10
|
import path from 'node:path';
|
|
11
11
|
import { dockerContainerExists, dockerContainerIsRunning, startDockerContainer, stopDockerContainer, } from './app-runtime.js';
|
|
12
|
+
import { loadAuthConfig } from './auth-store.js';
|
|
12
13
|
import { DEFAULT_NGINX_PROXY_DRIVER, getCliConfigValue, NGINX_PROXY_DRIVER_OPTIONS, normalizeNginxProxyDriver, resolveDockerContainerPrefix, setCliConfigValue, } from './cli-config.js';
|
|
13
14
|
import { resolveCliHomeRoot } from './cli-home.js';
|
|
14
|
-
import { applyEnvProxyAppEntryOptions, appConfigHasManagedNginxBlock, buildEnvProxyMainConfig, buildEnvProxyNginxBundle, extractManagedNginxConfigBlock, installEnvProxyProvider, mapProxyPathFromCliRoot, reloadEnvProxyProvider, resolveEnvProxyMainOutputPath, replaceManagedNginxConfigBlock, syncEnvProxyNginxSnippets, } from './env-proxy.js';
|
|
15
|
-
import {
|
|
15
|
+
import { applyEnvProxyAppEntryOptions, appConfigHasManagedNginxBlock, buildManualEnvProxyNginxBundle, buildEnvProxyMainConfig, buildEnvProxyNginxBundle, extractManagedNginxConfigBlock, installEnvProxyProvider, mapProxyPathFromCliRoot, reloadEnvProxyProvider, resolveEnvProxyMainOutputPath, replaceManagedNginxConfigBlock, syncEnvProxyNginxSnippets, } from './env-proxy.js';
|
|
16
|
+
import { normalizeEnvProxyConfig } from './env-proxy-config.js';
|
|
17
|
+
import { commandOutput, run } from './run-npm.js';
|
|
16
18
|
const DOCKER_NGINX_PROXY_CONTAINER_SUFFIX = 'nginx-proxy';
|
|
17
19
|
const DOCKER_NGINX_PROXY_IMAGE = 'nginx:latest';
|
|
18
20
|
const DOCKER_NGINX_PROXY_RUNTIME_ROOT = '/apps';
|
|
19
21
|
const DOCKER_NGINX_PROXY_CONF_DESTINATION = '/etc/nginx/conf.d/default.conf';
|
|
22
|
+
const DEFAULT_DOCKER_NGINX_PROXY_PUBLISHED_PORTS = [80, 443];
|
|
20
23
|
async function readOptionalTextFile(filePath) {
|
|
21
24
|
try {
|
|
22
25
|
return await readFile(filePath, 'utf8');
|
|
@@ -66,11 +69,23 @@ function buildNginxManagedBlockMissingMessage(appConfigPath) {
|
|
|
66
69
|
return (`The editable nginx app entry config at ${appConfigPath} does not contain the NocoBase managed block. ` +
|
|
67
70
|
'Restore the managed block or delete the file and regenerate the proxy config.');
|
|
68
71
|
}
|
|
69
|
-
export async function writeNginxProxyBundle(runtime, appEntryOptions, runtimeContext) {
|
|
72
|
+
export async function writeNginxProxyBundle(runtime, appEntryOptions, runtimeContext, options) {
|
|
70
73
|
const bundle = await buildEnvProxyNginxBundle(runtime, {
|
|
74
|
+
cdnBaseUrl: options?.cdnBaseUrl,
|
|
71
75
|
runtimeCliRoot: runtimeContext.runtimeCliRoot,
|
|
72
76
|
upstreamHost: runtimeContext.upstreamHost,
|
|
73
77
|
});
|
|
78
|
+
return await writeResolvedNginxProxyBundle(bundle, appEntryOptions, options);
|
|
79
|
+
}
|
|
80
|
+
export async function writeManualNginxProxyBundle(input, appEntryOptions, runtimeContext, options) {
|
|
81
|
+
const bundle = await buildManualEnvProxyNginxBundle(input, {
|
|
82
|
+
cdnBaseUrl: options?.cdnBaseUrl,
|
|
83
|
+
runtimeCliRoot: runtimeContext.runtimeCliRoot,
|
|
84
|
+
upstreamHost: runtimeContext.upstreamHost,
|
|
85
|
+
});
|
|
86
|
+
return await writeResolvedNginxProxyBundle(bundle, appEntryOptions, options);
|
|
87
|
+
}
|
|
88
|
+
async function writeResolvedNginxProxyBundle(bundle, appEntryOptions, options) {
|
|
74
89
|
const managedConfigBlock = extractManagedNginxConfigBlock(bundle.appConfigContent);
|
|
75
90
|
if (!managedConfigBlock) {
|
|
76
91
|
throw new Error('Failed to render the managed nginx config block.');
|
|
@@ -79,10 +94,12 @@ export async function writeNginxProxyBundle(runtime, appEntryOptions, runtimeCon
|
|
|
79
94
|
let nextAppConfigContent = applyEnvProxyAppEntryOptions(bundle.appConfigContent, 'nginx', appEntryOptions);
|
|
80
95
|
let status = 'created';
|
|
81
96
|
if (currentAppConfigContent) {
|
|
82
|
-
if (!appConfigHasManagedNginxBlock(currentAppConfigContent)) {
|
|
97
|
+
if (!appConfigHasManagedNginxBlock(currentAppConfigContent) && !options?.force) {
|
|
83
98
|
throw new Error(buildNginxManagedBlockMissingMessage(bundle.appConfigPath));
|
|
84
99
|
}
|
|
85
|
-
nextAppConfigContent =
|
|
100
|
+
nextAppConfigContent = appConfigHasManagedNginxBlock(currentAppConfigContent)
|
|
101
|
+
? applyEnvProxyAppEntryOptions(replaceManagedNginxConfigBlock(currentAppConfigContent, managedConfigBlock), 'nginx', appEntryOptions)
|
|
102
|
+
: applyEnvProxyAppEntryOptions(bundle.appConfigContent, 'nginx', appEntryOptions);
|
|
86
103
|
status = 'updated';
|
|
87
104
|
}
|
|
88
105
|
await Promise.all([mkdir(bundle.entryDir, { recursive: true }), mkdir(bundle.publicDir, { recursive: true })]);
|
|
@@ -217,11 +234,16 @@ async function reloadLocalNginxProxy(runtimeContext) {
|
|
|
217
234
|
}
|
|
218
235
|
async function ensureDockerNginxProxyContainer(runtimeContext) {
|
|
219
236
|
const containerName = await resolveNginxProxyContainerName();
|
|
237
|
+
const mainConfigPath = await ensureNginxProxyMainConfig(runtimeContext);
|
|
238
|
+
const publishedPorts = await resolveDockerNginxPublishedPorts();
|
|
220
239
|
if (await dockerContainerExists(containerName)) {
|
|
221
|
-
|
|
240
|
+
if (await dockerNginxProxyContainerMatchesPublishedPorts(containerName, publishedPorts)) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
await removeDockerNginxProxyContainer(containerName);
|
|
222
244
|
}
|
|
223
245
|
const hostCliRoot = String(process.env.NB_CLI_ROOT ?? resolveCliHomeRoot()).trim() || resolveCliHomeRoot();
|
|
224
|
-
const
|
|
246
|
+
const dockerPortArgs = publishedPorts.flatMap((port) => ['-p', `${port}:${port}`]);
|
|
225
247
|
await run('docker', [
|
|
226
248
|
'run',
|
|
227
249
|
'-d',
|
|
@@ -229,8 +251,7 @@ async function ensureDockerNginxProxyContainer(runtimeContext) {
|
|
|
229
251
|
containerName,
|
|
230
252
|
'--add-host',
|
|
231
253
|
'host.docker.internal:host-gateway',
|
|
232
|
-
|
|
233
|
-
'80:80',
|
|
254
|
+
...dockerPortArgs,
|
|
234
255
|
'-v',
|
|
235
256
|
`${hostCliRoot}:${DOCKER_NGINX_PROXY_RUNTIME_ROOT}`,
|
|
236
257
|
'-v',
|
|
@@ -241,14 +262,53 @@ async function ensureDockerNginxProxyContainer(runtimeContext) {
|
|
|
241
262
|
stdio: 'ignore',
|
|
242
263
|
});
|
|
243
264
|
}
|
|
265
|
+
async function resolveDockerNginxPublishedPorts() {
|
|
266
|
+
const config = await loadAuthConfig();
|
|
267
|
+
const ports = new Set(DEFAULT_DOCKER_NGINX_PROXY_PUBLISHED_PORTS);
|
|
268
|
+
for (const envConfig of Object.values(config.envs)) {
|
|
269
|
+
const port = normalizeEnvProxyConfig(envConfig.proxy)?.port;
|
|
270
|
+
if (port !== undefined) {
|
|
271
|
+
ports.add(port);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return Array.from(ports).sort((left, right) => left - right);
|
|
275
|
+
}
|
|
276
|
+
async function readDockerNginxPublishedPorts(containerName) {
|
|
277
|
+
const output = await commandOutput('docker', ['inspect', '--format', '{{json .HostConfig.PortBindings}}', containerName], { errorName: 'docker inspect' });
|
|
278
|
+
const parsed = JSON.parse(output.trim() || '{}');
|
|
279
|
+
const ports = new Set();
|
|
280
|
+
for (const bindings of Object.values(parsed)) {
|
|
281
|
+
if (!Array.isArray(bindings)) {
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
for (const binding of bindings) {
|
|
285
|
+
const port = Number.parseInt(String(binding?.HostPort ?? '').trim(), 10);
|
|
286
|
+
if (Number.isInteger(port) && port > 0) {
|
|
287
|
+
ports.add(port);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return Array.from(ports).sort((left, right) => left - right);
|
|
292
|
+
}
|
|
293
|
+
async function dockerNginxProxyContainerMatchesPublishedPorts(containerName, expectedPorts) {
|
|
294
|
+
const currentPorts = await readDockerNginxPublishedPorts(containerName);
|
|
295
|
+
return currentPorts.length === expectedPorts.length && currentPorts.every((port, index) => port === expectedPorts[index]);
|
|
296
|
+
}
|
|
297
|
+
async function removeDockerNginxProxyContainer(containerName) {
|
|
298
|
+
await run('docker', ['rm', '-f', containerName], {
|
|
299
|
+
errorName: 'docker rm',
|
|
300
|
+
stdio: 'ignore',
|
|
301
|
+
});
|
|
302
|
+
}
|
|
244
303
|
async function startDockerNginxProxy(runtimeContext) {
|
|
245
304
|
const containerName = await resolveNginxProxyContainerName();
|
|
246
305
|
await ensureNginxProxyMainConfig(runtimeContext);
|
|
247
|
-
|
|
306
|
+
const existedBeforeEnsure = await dockerContainerExists(containerName);
|
|
307
|
+
await ensureDockerNginxProxyContainer(runtimeContext);
|
|
308
|
+
if (existedBeforeEnsure && (await dockerContainerExists(containerName))) {
|
|
248
309
|
const state = await startDockerContainer(containerName, { stdio: 'ignore' });
|
|
249
310
|
return state === 'already-running' ? 'already-running' : 'started';
|
|
250
311
|
}
|
|
251
|
-
await ensureDockerNginxProxyContainer(runtimeContext);
|
|
252
312
|
return 'started';
|
|
253
313
|
}
|
|
254
314
|
async function stopDockerNginxProxy() {
|
package/dist/lib/run-npm.js
CHANGED