mirror of
https://github.com/lush2020/edgetunnel.git
synced 2026-03-24 00:48:39 +08:00
fix: 添加Clash订阅配置文件热补丁,支持UUID和ECH配置,优化Singbox处理逻辑
This commit is contained in:
141
_worker.js
141
_worker.js
@@ -324,6 +324,7 @@ export default {
|
|||||||
订阅内容 = Singbox订阅配置文件热补丁(订阅内容, config_JSON.UUID, config_JSON.Fingerprint, config_JSON.ECH ? await getECH(host) : null);
|
订阅内容 = Singbox订阅配置文件热补丁(订阅内容, config_JSON.UUID, config_JSON.Fingerprint, config_JSON.ECH ? await getECH(host) : null);
|
||||||
responseHeaders["content-type"] = 'application/json; charset=utf-8';
|
responseHeaders["content-type"] = 'application/json; charset=utf-8';
|
||||||
} else if (订阅类型 === 'clash') {
|
} else if (订阅类型 === 'clash') {
|
||||||
|
订阅内容 = Clash订阅配置文件热补丁(订阅内容, config_JSON.UUID, config_JSON.ECH ? await getECH(host) : null);
|
||||||
responseHeaders["content-type"] = 'application/x-yaml; charset=utf-8';
|
responseHeaders["content-type"] = 'application/x-yaml; charset=utf-8';
|
||||||
}
|
}
|
||||||
return new Response(订阅内容, { status: 200, headers: responseHeaders });
|
return new Response(订阅内容, { status: 200, headers: responseHeaders });
|
||||||
@@ -784,6 +785,142 @@ async function httpConnect(targetHost, targetPort, initialData) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
//////////////////////////////////////////////////功能性函数///////////////////////////////////////////////
|
//////////////////////////////////////////////////功能性函数///////////////////////////////////////////////
|
||||||
|
function Clash订阅配置文件热补丁(clash_yaml, uuid = null, ech_config = null) {
|
||||||
|
// 判断uuid字段是否为真
|
||||||
|
if (!uuid) {
|
||||||
|
return clash_yaml;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果ech_config为null,直接返回
|
||||||
|
if (!ech_config) return clash_yaml;
|
||||||
|
|
||||||
|
const lines = clash_yaml.split('\n');
|
||||||
|
const processedLines = [];
|
||||||
|
let i = 0;
|
||||||
|
|
||||||
|
while (i < lines.length) {
|
||||||
|
const line = lines[i];
|
||||||
|
const trimmedLine = line.trim();
|
||||||
|
|
||||||
|
// 处理行格式(Flow):- {name: ..., uuid: ..., ...}
|
||||||
|
if (trimmedLine.startsWith('- {') && (trimmedLine.includes('uuid:') || trimmedLine.includes('password:'))) {
|
||||||
|
let fullNode = line;
|
||||||
|
let braceCount = (line.match(/\{/g) || []).length - (line.match(/\}/g) || []).length;
|
||||||
|
|
||||||
|
// 如果括号不匹配,继续读取下一行
|
||||||
|
while (braceCount > 0 && i + 1 < lines.length) {
|
||||||
|
i++;
|
||||||
|
fullNode += '\n' + lines[i];
|
||||||
|
braceCount += (lines[i].match(/\{/g) || []).length - (lines[i].match(/\}/g) || []).length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取代理类型
|
||||||
|
const typeMatch = fullNode.match(/type:\s*(\w+)/);
|
||||||
|
const proxyType = typeMatch ? typeMatch[1] : 'vless';
|
||||||
|
|
||||||
|
// 根据代理类型确定要查找的字段
|
||||||
|
let credentialField = 'uuid';
|
||||||
|
if (proxyType === 'trojan') {
|
||||||
|
credentialField = 'password';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查对应字段的值是否匹配
|
||||||
|
const credentialPattern = new RegExp(`${credentialField}:\\s*([^,}\\n]+)`);
|
||||||
|
const credentialMatch = fullNode.match(credentialPattern);
|
||||||
|
|
||||||
|
if (credentialMatch && credentialMatch[1].trim() === uuid.trim()) {
|
||||||
|
// 在最后一个}前添加ech-opts
|
||||||
|
fullNode = fullNode.replace(/\}(\s*)$/, `, ech-opts: {enable: true, config: "${ech_config}"}}$1`);
|
||||||
|
}
|
||||||
|
|
||||||
|
processedLines.push(fullNode);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
// 处理块格式(Block):- name: ..., 后续行为属性
|
||||||
|
else if (trimmedLine.startsWith('- name:')) {
|
||||||
|
// 收集完整的代理节点定义
|
||||||
|
let nodeLines = [line];
|
||||||
|
let baseIndent = line.search(/\S/);
|
||||||
|
let topLevelIndent = baseIndent + 2; // 顶级属性的缩进
|
||||||
|
i++;
|
||||||
|
|
||||||
|
// 继续读取这个节点的所有属性
|
||||||
|
while (i < lines.length) {
|
||||||
|
const nextLine = lines[i];
|
||||||
|
const nextTrimmed = nextLine.trim();
|
||||||
|
|
||||||
|
// 如果是空行,包含它但不继续
|
||||||
|
if (!nextTrimmed) {
|
||||||
|
nodeLines.push(nextLine);
|
||||||
|
i++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextIndent = nextLine.search(/\S/);
|
||||||
|
|
||||||
|
// 如果缩进小于等于基础缩进且不是空行,说明节点结束了
|
||||||
|
if (nextIndent <= baseIndent && nextTrimmed.startsWith('- ')) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果缩进更小,节点也结束了
|
||||||
|
if (nextIndent < baseIndent && nextTrimmed) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeLines.push(nextLine);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取代理类型
|
||||||
|
const nodeText = nodeLines.join('\n');
|
||||||
|
const typeMatch = nodeText.match(/type:\s*(\w+)/);
|
||||||
|
const proxyType = typeMatch ? typeMatch[1] : 'vless';
|
||||||
|
|
||||||
|
// 根据代理类型确定要查找的字段
|
||||||
|
let credentialField = 'uuid';
|
||||||
|
if (proxyType === 'trojan') {
|
||||||
|
credentialField = 'password';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查这个节点的对应字段是否匹配
|
||||||
|
const credentialPattern = new RegExp(`${credentialField}:\\s*([^\\n]+)`);
|
||||||
|
const credentialMatch = nodeText.match(credentialPattern);
|
||||||
|
|
||||||
|
if (credentialMatch && credentialMatch[1].trim() === uuid.trim()) {
|
||||||
|
// 找到在哪里插入ech-opts
|
||||||
|
// 策略:在最后一个顶级属性后面插入,或在ws-opts之前插入
|
||||||
|
let insertIndex = -1;
|
||||||
|
|
||||||
|
for (let j = nodeLines.length - 1; j >= 0; j--) {
|
||||||
|
// 跳过空行,找到节点中最后一个非空行(可能是顶级属性或其子项)
|
||||||
|
if (nodeLines[j].trim()) {
|
||||||
|
insertIndex = j;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (insertIndex >= 0) {
|
||||||
|
const indent = ' '.repeat(topLevelIndent);
|
||||||
|
// 在节点末尾(最后一个属性块之后)插入 ech-opts 属性
|
||||||
|
nodeLines.splice(insertIndex + 1, 0,
|
||||||
|
`${indent}ech-opts:`,
|
||||||
|
`${indent} enable: true`,
|
||||||
|
`${indent} config: "${ech_config}"`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
processedLines.push(...nodeLines);
|
||||||
|
} else {
|
||||||
|
processedLines.push(line);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return processedLines.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
function Singbox订阅配置文件热补丁(sb_json_text, uuid = null, fingerprint = "chrome", ech_config = null) {
|
function Singbox订阅配置文件热补丁(sb_json_text, uuid = null, fingerprint = "chrome", ech_config = null) {
|
||||||
try {
|
try {
|
||||||
let config = JSON.parse(sb_json_text);
|
let config = JSON.parse(sb_json_text);
|
||||||
@@ -932,8 +1069,8 @@ function Singbox订阅配置文件热补丁(sb_json_text, uuid = null, fingerpri
|
|||||||
// --- 4. UUID 匹配节点的 TLS 热补丁 (utls & ech) ---
|
// --- 4. UUID 匹配节点的 TLS 热补丁 (utls & ech) ---
|
||||||
if (uuid) {
|
if (uuid) {
|
||||||
config.outbounds.forEach(outbound => {
|
config.outbounds.forEach(outbound => {
|
||||||
// 仅处理包含 uuid 且匹配的节点
|
// 仅处理包含 uuid 或 password 且匹配的节点
|
||||||
if (outbound.uuid && outbound.uuid === uuid) {
|
if ((outbound.uuid && outbound.uuid === uuid) || (outbound.password && outbound.password === uuid)) {
|
||||||
// 确保 tls 对象存在
|
// 确保 tls 对象存在
|
||||||
if (!outbound.tls) {
|
if (!outbound.tls) {
|
||||||
outbound.tls = { enabled: true };
|
outbound.tls = { enabled: true };
|
||||||
|
|||||||
Reference in New Issue
Block a user