Files
hy2-panel/lib/custom-proxy.ts

68 lines
2.4 KiB
TypeScript

import { parseDocument, stringify } from "yaml";
export class CustomProxyValidationError extends Error {}
function isRecord(value: unknown): value is Record<string, unknown> {
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
}
/** Accept a single proxy, a one-item list, or the legacy `proxies:` wrapper. */
export function parseCustomProxyYaml(
source: string,
): Record<string, unknown> & { name: string; type: string } {
if (!source.trim()) throw new CustomProxyValidationError("请填写节点 YAML。");
if (source.length > 128 * 1024)
throw new CustomProxyValidationError("单个节点 YAML 不能超过 128 KiB。");
const doc = parseDocument(source, { prettyErrors: false, uniqueKeys: true });
if (doc.errors.length || doc.warnings.length) {
// Parser messages can contain credentials from the YAML; do not echo them.
throw new CustomProxyValidationError(
"YAML 格式错误,请检查缩进、引号和重复字段。",
);
}
let value: unknown;
try {
value = doc.toJS({ maxAliasCount: 100 });
} catch {
throw new CustomProxyValidationError(
"YAML 引用无效或过多,请使用独立的节点配置。",
);
}
if (isRecord(value) && "proxies" in value) value = value.proxies;
if (Array.isArray(value)) {
if (value.length !== 1)
throw new CustomProxyValidationError(
"每条节点记录只能包含一个 proxy,请将多个节点分别添加。",
);
value = value[0];
}
if (!isRecord(value))
throw new CustomProxyValidationError(
"请填写一个 Clash proxy 对象或单节点列表。",
);
if (typeof value.name !== "string" || !value.name.trim()) {
throw new CustomProxyValidationError(
"节点 YAML 必须包含非空的 name 字段(字符串)。",
);
}
if (typeof value.type !== "string" || !value.type.trim()) {
throw new CustomProxyValidationError(
"节点 YAML 必须包含非空的 type 字段,例如 anytls。",
);
}
return value as Record<string, unknown> & { name: string; type: string };
}
/** Re-serialize only for output; stored source and comments remain untouched. */
export function renderCustomProxyYaml(source: string) {
return stringify([parseCustomProxyYaml(source)], { lineWidth: 0 }).trimEnd();
}
export function extractProxyNameFromYaml(source: string) {
try {
return parseCustomProxyYaml(source).name;
} catch {
return null;
}
}