feat: add yaml egress proxy benchmark

This commit is contained in:
Codex
2026-06-26 10:32:43 +00:00
parent 0bfff97848
commit 0ef9129bef
14 changed files with 1090 additions and 46 deletions
+2
View File
@@ -66,6 +66,8 @@ export function egressProxySummary(proxy: Sub2ApiEgressProxyConfig): Record<stri
image: proxy.image,
imagePullPolicy: proxy.imagePullPolicy,
listenPort: proxy.listenPort,
sourceConfigRef: proxy.sourceConfigRef,
sourceFingerprint: proxy.sourceFingerprint,
sourceRef: proxy.sourceRef,
sourceType: proxy.sourceType,
preferredOutbound: proxy.preferredOutbound,
+34 -6
View File
@@ -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) {
+2
View File
@@ -169,6 +169,8 @@ export interface Sub2ApiEgressProxyConfig {
image: string;
imagePullPolicy: "Always" | "IfNotPresent" | "Never";
listenPort: number;
sourceConfigRef: string | null;
sourceFingerprint: string | null;
sourceRef: string;
sourceKey: string;
sourceType: "subscription-url" | "master-shadowsocks";
+2
View File
@@ -623,6 +623,8 @@ spec:
annotations:
unidesk.ai/proxy-source-ref: "${proxy.sourceRef}"
unidesk.ai/proxy-source-type: "${proxy.sourceType}"
unidesk.ai/proxy-source-config-ref: "${proxy.sourceConfigRef ?? ""}"
unidesk.ai/proxy-source-fingerprint: "${proxy.sourceFingerprint ?? ""}"
unidesk.ai/proxy-selected-outbound: "${proxy.sourceType === "subscription-url" ? proxy.preferredOutbound : "shadowsocks"}"
spec:
containers:
+5 -1
View File
@@ -36,6 +36,10 @@ export function sub2ApiHelpTargetSummary(): Record<string, unknown> {
export async function runPlatformInfraCommand(config: UniDeskConfig, args: string[]): Promise<Record<string, unknown> | RenderedCliResult> {
const [target, action] = args;
if (target === "egress-proxy") {
const { runPlatformInfraEgressProxyCommand } = await import("../platform-infra-egress-proxy");
return await runPlatformInfraEgressProxyCommand(config, args.slice(1));
}
if (target === "langbot") {
const { runLangBotCommand } = await import("../platform-infra-langbot");
return await runLangBotCommand(config, args.slice(1));
@@ -109,7 +113,7 @@ function renderSub2ApiPlan(result: Record<string, unknown>): RenderedCliResult {
["DATABASE", stringValue(config.databaseMode), "redis", stringValue(config.redisMode)],
["REPLICAS", `${stringValue(config.appReplicas)}/${stringValue(config.redisReplicas)}`, "service", stringValue(target.serviceDns)],
["PUBLIC", boolText(exposure.enabled), "url", stringValue(exposure.publicBaseUrl, "-")],
["EGRESS", boolText(egress.enabled), "sourceRef", stringValue(egress.sourceRef, "-")],
["EGRESS", boolText(egress.enabled), "source", `${stringValue(egress.sourceType, "-")} ${stringValue(egress.sourceFingerprint, "-")}`],
["POLICY", failedPolicy.length === 0 ? "ok" : `failed=${failedPolicy.length}`, "valuesPrinted", "false"],
];
const lines = [