feat: add yaml egress proxy benchmark
This commit is contained in:
@@ -7,6 +7,7 @@ import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "n
|
||||
import { dirname, isAbsolute, join } from "node:path";
|
||||
import type { UniDeskConfig } from "../config";
|
||||
import { rootPath } from "../config";
|
||||
import { resolveEgressProxySourceRef } from "../egress-proxy-sources";
|
||||
import { startJob } from "../jobs";
|
||||
import type { RenderedCliResult } from "../output";
|
||||
import { pk01CaddyMergeManagedBlocksPython, renderCaddyManagedBlock, renderSimpleReverseProxyCaddySiteBlock } from "../pk01-caddy";
|
||||
@@ -282,12 +283,16 @@ export function parseEgressProxyConfig(value: unknown, path: string): Sub2ApiEgr
|
||||
if (value === undefined || value === null) return null;
|
||||
if (typeof value !== "object" || Array.isArray(value)) throw new Error(`${configPath}.${path}.egressProxy must be an object`);
|
||||
const record = value as Record<string, unknown>;
|
||||
const sourceType = enumField(record, "sourceType", `${path}.egressProxy`, ["subscription-url", "master-shadowsocks"] as const);
|
||||
const sourceConfigRef = optionalConfigStringField(record, "sourceConfigRef", `${path}.egressProxy`);
|
||||
const source = sourceConfigRef === null ? null : resolveEgressProxySourceRef(sourceConfigRef, `${configPath}.${path}.egressProxy.sourceConfigRef`);
|
||||
const sourceType = source?.sourceType ?? enumField(record, "sourceType", `${path}.egressProxy`, ["subscription-url", "master-shadowsocks"] as const);
|
||||
assertOptionalMatch(record, "sourceType", sourceType, `${path}.egressProxy`);
|
||||
const preferredOutbound = sourceType === "subscription-url" || record.preferredOutbound !== undefined
|
||||
? enumField(record, "preferredOutbound", `${path}.egressProxy`, ["vless-reality", "hysteria2"] as const)
|
||||
? source?.preferredOutbound ?? enumField(record, "preferredOutbound", `${path}.egressProxy`, ["vless-reality", "hysteria2"] as const)
|
||||
: null;
|
||||
if (source !== null && source.preferredOutbound !== null && record.preferredOutbound !== undefined) assertOptionalMatch(record, "preferredOutbound", source.preferredOutbound, `${path}.egressProxy`);
|
||||
const masterShadowsocks = sourceType === "master-shadowsocks"
|
||||
? parseMasterShadowsocksConfig(record.masterShadowsocks, `${path}.egressProxy.masterShadowsocks`)
|
||||
? source?.masterShadowsocks ?? parseMasterShadowsocksConfig(record.masterShadowsocks, `${path}.egressProxy.masterShadowsocks`)
|
||||
: null;
|
||||
const imagePullPolicy = enumField(record, "imagePullPolicy", `${path}.egressProxy`, ["Always", "IfNotPresent", "Never"] as const);
|
||||
const noProxy = stringArrayField(record, "noProxy", `${path}.egressProxy`);
|
||||
@@ -300,16 +305,23 @@ export function parseEgressProxyConfig(value: unknown, path: string): Sub2ApiEgr
|
||||
image: stringField(record, "image", `${path}.egressProxy`),
|
||||
imagePullPolicy,
|
||||
listenPort: integerField(record, "listenPort", `${path}.egressProxy`),
|
||||
sourceRef: stringField(record, "sourceRef", `${path}.egressProxy`),
|
||||
sourceKey: stringField(record, "sourceKey", `${path}.egressProxy`),
|
||||
sourceConfigRef,
|
||||
sourceFingerprint: source?.fingerprint ?? null,
|
||||
sourceRef: source?.sourceRef ?? stringField(record, "sourceRef", `${path}.egressProxy`),
|
||||
sourceKey: source?.sourceKey ?? stringField(record, "sourceKey", `${path}.egressProxy`),
|
||||
sourceType,
|
||||
preferredOutbound,
|
||||
masterShadowsocks,
|
||||
noProxy,
|
||||
applyToSub2Api: booleanField(record, "applyToSub2Api", `${path}.egressProxy`),
|
||||
applyToSentinel: booleanField(record, "applyToSentinel", `${path}.egressProxy`),
|
||||
healthProbeUrl: stringField(record, "healthProbeUrl", `${path}.egressProxy`),
|
||||
healthProbeUrl: source?.healthProbeUrl ?? stringField(record, "healthProbeUrl", `${path}.egressProxy`),
|
||||
};
|
||||
if (source !== null) {
|
||||
assertOptionalMatch(record, "sourceRef", source.sourceRef, `${path}.egressProxy`);
|
||||
assertOptionalMatch(record, "sourceKey", source.sourceKey, `${path}.egressProxy`);
|
||||
assertOptionalMatch(record, "healthProbeUrl", source.healthProbeUrl, `${path}.egressProxy`);
|
||||
}
|
||||
validateEgressProxyConfig(proxy, path);
|
||||
return proxy;
|
||||
}
|
||||
@@ -406,6 +418,19 @@ export function stringArrayField(obj: Record<string, unknown>, key: string, path
|
||||
return value.map((item) => item.trim());
|
||||
}
|
||||
|
||||
function optionalConfigStringField(obj: Record<string, unknown>, key: string, path: string): string | null {
|
||||
const value = obj[key];
|
||||
if (value === undefined || value === null) return null;
|
||||
if (typeof value !== "string" || value.trim().length === 0) throw new Error(`${fieldLabel(path, key)} must be a non-empty string when set`);
|
||||
return value.trim();
|
||||
}
|
||||
|
||||
function assertOptionalMatch(obj: Record<string, unknown>, key: string, expected: string | null, path: string): void {
|
||||
if (obj[key] === undefined || obj[key] === null) return;
|
||||
const actual = stringField(obj, key, path);
|
||||
if (actual !== expected) throw new Error(`${fieldLabel(path, key)} must match sourceConfigRef value ${expected}`);
|
||||
}
|
||||
|
||||
export function enumField<const T extends readonly string[]>(obj: Record<string, unknown>, key: string, path: string, values: T): T[number] {
|
||||
const value = stringField(obj, key, path);
|
||||
if (!(values as readonly string[]).includes(value)) throw new Error(`${fieldLabel(path, key)} must be one of ${values.join(", ")}`);
|
||||
@@ -483,6 +508,9 @@ export function validateEgressProxyConfig(config: Sub2ApiEgressProxyConfig, path
|
||||
if (config.secretKey !== "config.json") throw new Error(`${configPath}.${path}.egressProxy.secretKey must be config.json`);
|
||||
if (!isImageReference(config.image)) throw new Error(`${configPath}.${path}.egressProxy.image has an unsupported format`);
|
||||
validatePort(config.listenPort, `${path}.egressProxy.listenPort`);
|
||||
if (config.sourceConfigRef !== null && (!config.sourceConfigRef.startsWith("config/") || !config.sourceConfigRef.includes("#sources.") || config.sourceConfigRef.includes(".."))) {
|
||||
throw new Error(`${configPath}.${path}.egressProxy.sourceConfigRef must reference config/*.yaml#sources.<id>`);
|
||||
}
|
||||
if (!/^[A-Za-z0-9_./-]+$/u.test(config.sourceRef)) throw new Error(`${configPath}.${path}.egressProxy.sourceRef has an unsupported format`);
|
||||
if (!/^[A-Z0-9_]+$/u.test(config.sourceKey)) throw new Error(`${configPath}.${path}.egressProxy.sourceKey must be an env key`);
|
||||
if (config.sourceType === "subscription-url" && config.preferredOutbound === null) {
|
||||
|
||||
Reference in New Issue
Block a user