diff --git a/_worker.js b/_worker.js
index 80145be..c4d7f85 100644
--- a/_worker.js
+++ b/_worker.js
@@ -1,1697 +1,362 @@
-// Cloudflare Worker - 简化版优选工具
-// 仅保留优选域名、优选IP、GitHub、上报和节点生成功能
-// 修复记录:已修正 VMess 协议下节点名称包含中文导致 Error 1101 的问题
+// Cloudflare Workers 优选工具
+// GitHub: https://github.com/byJoey/yx-auto
-// 默认配置
-let customPreferredIPs = [];
-let customPreferredDomains = [];
-let epd = true; // 启用优选域名
-let epi = true; // 启用优选IP
-let egi = true; // 启用GitHub优选
-let ev = true; // 启用VLESS协议
-let et = false; // 启用Trojan协议
-let vm = false; // 启用VMess协议
-let scu = 'https://url.v1.mk/sub'; // 订阅转换地址
-// ECH (Encrypted Client Hello)
-let enableECH = false;
-let customDNS = 'https://dns.joeyblog.eu.org/joeyblog';
-let customECHDomain = 'cloudflare-ech.com';
+const HTML_CONTENT = `
+
+
+
+ /g;
- const cellRegex = /| (.+?)<\/td>[\s\S]*? | ([\d.:a-fA-F]+)<\/td>[\s\S]*? | (.+?)<\/td>/;
-
- let match;
- while ((match = rowRegex.exec(html)) !== null) {
- const rowHtml = match[0];
- const cellMatch = rowHtml.match(cellRegex);
- if (cellMatch && cellMatch[1] && cellMatch[2]) {
- const colo = cellMatch[3] ? cellMatch[3].trim().replace(/<.*?>/g, '') : '';
- results.push({
- isp: cellMatch[1].trim().replace(/<.*?>/g, ''),
- ip: cellMatch[2].trim(),
- colo: colo
- });
- }
- }
- return results;
+ const url = `https://api.wetest.vip/api/bestcf/${ipVersion}/${isp}`;
+ const response = await fetch(url);
+ const data = await response.json();
+ return data.ips || [];
} catch (error) {
+ console.error('获取优选IP失败:', error);
return [];
}
}
-// 整理成数组
-async function 整理成数组(内容) {
- var 替换后的内容 = 内容.replace(/[ "'\r\n]+/g, ',').replace(/,+/g, ',');
- if (替换后的内容.charAt(0) == ',') 替换后的内容 = 替换后的内容.slice(1);
- if (替换后的内容.charAt(替换后的内容.length - 1) == ',') 替换后的内容 = 替换后的内容.slice(0, 替换后的内容.length - 1);
- const 地址数组 = 替换后的内容.split(',');
- return 地址数组;
-}
-
-// 请求优选API
-async function 请求优选API(urls, 默认端口 = '443', 超时时间 = 3000) {
- if (!urls?.length) return [];
- const results = new Set();
- await Promise.allSettled(urls.map(async (url) => {
- try {
- const controller = new AbortController();
- const timeoutId = setTimeout(() => controller.abort(), 超时时间);
- const response = await fetch(url, { signal: controller.signal });
- clearTimeout(timeoutId);
- let text = '';
- try {
- const buffer = await response.arrayBuffer();
- const contentType = (response.headers.get('content-type') || '').toLowerCase();
- const charset = contentType.match(/charset=([^\s;]+)/i)?.[1]?.toLowerCase() || '';
-
- // 根据 Content-Type 响应头判断编码优先级
- let decoders = ['utf-8', 'gb2312']; // 默认优先 UTF-8
- if (charset.includes('gb') || charset.includes('gbk') || charset.includes('gb2312')) {
- decoders = ['gb2312', 'utf-8']; // 如果明确指定 GB 系编码,优先尝试 GB2312
- }
-
- // 尝试多种编码解码
- let decodeSuccess = false;
- for (const decoder of decoders) {
- try {
- const decoded = new TextDecoder(decoder).decode(buffer);
- // 验证解码结果的有效性
- if (decoded && decoded.length > 0 && !decoded.includes('\ufffd')) {
- text = decoded;
- decodeSuccess = true;
- break;
- } else if (decoded && decoded.length > 0) {
- // 如果有替换字符 (U+FFFD),说明编码不匹配,继续尝试下一个编码
- continue;
- }
- } catch (e) {
- // 该编码解码失败,尝试下一个
- continue;
- }
- }
-
- // 如果所有编码都失败或无效,尝试 response.text()
- if (!decodeSuccess) {
- text = await response.text();
- }
-
- // 如果返回的是空或无效数据,返回
- if (!text || text.trim().length === 0) {
- return;
- }
- } catch (e) {
- console.error('Failed to decode response:', e);
- return;
- }
- const lines = text.trim().split('\n').map(l => l.trim()).filter(l => l);
- const isCSV = lines.length > 1 && lines[0].includes(',');
- const IPV6_PATTERN = /^[^\[\]]*:[^\[\]]*:[^\[\]]/;
- if (!isCSV) {
- lines.forEach(line => {
- const hashIndex = line.indexOf('#');
- const [hostPart, remark] = hashIndex > -1 ? [line.substring(0, hashIndex), line.substring(hashIndex)] : [line, ''];
- let hasPort = false;
- if (hostPart.startsWith('[')) {
- hasPort = /\]:(\d+)$/.test(hostPart);
- } else {
- const colonIndex = hostPart.lastIndexOf(':');
- hasPort = colonIndex > -1 && /^\d+$/.test(hostPart.substring(colonIndex + 1));
- }
- const port = new URL(url).searchParams.get('port') || 默认端口;
- results.add(hasPort ? line : `${hostPart}:${port}${remark}`);
- });
- } else {
- const headers = lines[0].split(',').map(h => h.trim());
- const dataLines = lines.slice(1);
- if (headers.includes('IP地址') && headers.includes('端口') && headers.includes('数据中心')) {
- const ipIdx = headers.indexOf('IP地址'), portIdx = headers.indexOf('端口');
- const remarkIdx = headers.indexOf('国家') > -1 ? headers.indexOf('国家') :
- headers.indexOf('城市') > -1 ? headers.indexOf('城市') : headers.indexOf('数据中心');
- const tlsIdx = headers.indexOf('TLS');
- dataLines.forEach(line => {
- const cols = line.split(',').map(c => c.trim());
- if (tlsIdx !== -1 && cols[tlsIdx]?.toLowerCase() !== 'true') return;
- const wrappedIP = IPV6_PATTERN.test(cols[ipIdx]) ? `[${cols[ipIdx]}]` : cols[ipIdx];
- results.add(`${wrappedIP}:${cols[portIdx]}#${cols[remarkIdx]}`);
- });
- } else if (headers.some(h => h.includes('IP')) && headers.some(h => h.includes('延迟')) && headers.some(h => h.includes('下载速度'))) {
- const ipIdx = headers.findIndex(h => h.includes('IP'));
- const delayIdx = headers.findIndex(h => h.includes('延迟'));
- const speedIdx = headers.findIndex(h => h.includes('下载速度'));
- const port = new URL(url).searchParams.get('port') || 默认端口;
- dataLines.forEach(line => {
- const cols = line.split(',').map(c => c.trim());
- const wrappedIP = IPV6_PATTERN.test(cols[ipIdx]) ? `[${cols[ipIdx]}]` : cols[ipIdx];
- results.add(`${wrappedIP}:${port}#CF优选 ${cols[delayIdx]}ms ${cols[speedIdx]}MB/s`);
- });
- }
- }
- } catch (e) { }
- }));
- return Array.from(results);
-}
-
-// 从GitHub获取优选IP(保留原有功能,同时支持优选API)
-async function fetchAndParseNewIPs(piu) {
- const url = piu || defaultIPURL;
+// 从 GitHub 获取IP列表
+async function fetchGitHubIPs(url) {
try {
const response = await fetch(url);
- if (!response.ok) return [];
const text = await response.text();
- const results = [];
- const lines = text.trim().replace(/\r/g, "").split('\n');
- const regex = /^([^:]+):(\d+)#(.*)$/;
-
- for (const line of lines) {
- const trimmedLine = line.trim();
- if (!trimmedLine) continue;
- const match = trimmedLine.match(regex);
- if (match) {
- results.push({
- ip: match[1],
- port: parseInt(match[2], 10),
- name: match[3].trim() || match[1]
- });
- }
- }
- return results;
+ return text.split('\n').filter(line => line.trim());
} catch (error) {
+ console.error('从GitHub获取IP失败:', error);
return [];
}
}
-// 生成VLESS链接
-function generateLinksFromSource(list, user, workerDomain, disableNonTLS = false, customPath = '/', echConfig = null) {
- const CF_HTTP_PORTS = [80, 8080, 8880, 2052, 2082, 2086, 2095];
- const CF_HTTPS_PORTS = [443, 2053, 2083, 2087, 2096, 8443];
- const defaultHttpsPorts = [443];
- const defaultHttpPorts = disableNonTLS ? [] : [80];
- const links = [];
- const wsPath = customPath || '/';
- const proto = 'vless';
-
- list.forEach(item => {
- let nodeNameBase = item.isp ? item.isp.replace(/\s/g, '_') : (item.name || item.domain || item.ip);
- if (item.colo && item.colo.trim()) {
- nodeNameBase = `${nodeNameBase}-${item.colo.trim()}`;
- }
- const safeIP = item.ip.includes(':') ? `[${item.ip}]` : item.ip;
-
- let portsToGenerate = [];
-
- if (item.port) {
- const port = item.port;
- if (CF_HTTPS_PORTS.includes(port)) {
- portsToGenerate.push({ port: port, tls: true });
- } else if (CF_HTTP_PORTS.includes(port)) {
- portsToGenerate.push({ port: port, tls: false });
- } else {
- portsToGenerate.push({ port: port, tls: true });
- }
- } else {
- defaultHttpsPorts.forEach(port => {
- portsToGenerate.push({ port: port, tls: true });
- });
- defaultHttpPorts.forEach(port => {
- portsToGenerate.push({ port: port, tls: false });
- });
- }
-
- portsToGenerate.forEach(({ port, tls }) => {
- if (tls) {
- const wsNodeName = `${nodeNameBase}-${port}-WS-TLS`;
- const wsParams = new URLSearchParams({
- encryption: 'none',
- security: 'tls',
- sni: workerDomain,
- fp: 'chrome',
- type: 'ws',
- host: workerDomain,
- path: wsPath
- });
- if (echConfig) {
- wsParams.set('alpn', 'h3,h2,http/1.1');
- wsParams.set('ech', echConfig);
- }
- links.push(`${proto}://${user}@${safeIP}:${port}?${wsParams.toString()}#${encodeURIComponent(wsNodeName)}`);
- } else {
- const wsNodeName = `${nodeNameBase}-${port}-WS`;
- const wsParams = new URLSearchParams({
- encryption: 'none',
- security: 'none',
- type: 'ws',
- host: workerDomain,
- path: wsPath
- });
- links.push(`${proto}://${user}@${safeIP}:${port}?${wsParams.toString()}#${encodeURIComponent(wsNodeName)}`);
- }
- });
- });
- return links;
-}
-
-// 生成Trojan链接
-async function generateTrojanLinksFromSource(list, user, workerDomain, disableNonTLS = false, customPath = '/', echConfig = null) {
- const CF_HTTP_PORTS = [80, 8080, 8880, 2052, 2082, 2086, 2095];
- const CF_HTTPS_PORTS = [443, 2053, 2083, 2087, 2096, 8443];
- const defaultHttpsPorts = [443];
- const defaultHttpPorts = disableNonTLS ? [] : [80];
- const links = [];
- const wsPath = customPath || '/';
- const password = user; // Trojan使用UUID作为密码
-
- list.forEach(item => {
- let nodeNameBase = item.isp ? item.isp.replace(/\s/g, '_') : (item.name || item.domain || item.ip);
- if (item.colo && item.colo.trim()) {
- nodeNameBase = `${nodeNameBase}-${item.colo.trim()}`;
- }
- const safeIP = item.ip.includes(':') ? `[${item.ip}]` : item.ip;
-
- let portsToGenerate = [];
-
- if (item.port) {
- const port = item.port;
- if (CF_HTTPS_PORTS.includes(port)) {
- portsToGenerate.push({ port: port, tls: true });
- } else if (CF_HTTP_PORTS.includes(port)) {
- if (!disableNonTLS) {
- portsToGenerate.push({ port: port, tls: false });
- }
- } else {
- portsToGenerate.push({ port: port, tls: true });
- }
- } else {
- defaultHttpsPorts.forEach(port => {
- portsToGenerate.push({ port: port, tls: true });
- });
- defaultHttpPorts.forEach(port => {
- portsToGenerate.push({ port: port, tls: false });
- });
- }
-
- portsToGenerate.forEach(({ port, tls }) => {
- if (tls) {
- const wsNodeName = `${nodeNameBase}-${port}-Trojan-WS-TLS`;
- const wsParams = new URLSearchParams({
- security: 'tls',
- sni: workerDomain,
- fp: 'chrome',
- type: 'ws',
- host: workerDomain,
- path: wsPath
- });
- if (echConfig) {
- wsParams.set('alpn', 'h3,h2,http/1.1');
- wsParams.set('ech', echConfig);
- }
- links.push(`trojan://${password}@${safeIP}:${port}?${wsParams.toString()}#${encodeURIComponent(wsNodeName)}`);
- } else {
- const wsNodeName = `${nodeNameBase}-${port}-Trojan-WS`;
- const wsParams = new URLSearchParams({
- security: 'none',
- type: 'ws',
- host: workerDomain,
- path: wsPath
- });
- links.push(`trojan://${password}@${safeIP}:${port}?${wsParams.toString()}#${encodeURIComponent(wsNodeName)}`);
- }
- });
- });
- return links;
+// 生成节点配置
+function generateNode(protocol, address, port, uuid, domain, index) {
+ const name = `${protocol.toUpperCase()}-${address}-${index}`;
+
+ if (protocol === 'vless') {
+ return `vless://${uuid}@${address}:${port}?encryption=none&security=tls&sni=${domain}&type=ws&host=${domain}&path=%2F#${encodeURIComponent(name)}`;
+ } else if (protocol === 'trojan') {
+ return `trojan://${uuid}@${address}:${port}?security=tls&sni=${domain}&type=ws&host=${domain}&path=%2F#${encodeURIComponent(name)}`;
+ } else if (protocol === 'vmess') {
+ const vmessConfig = {
+ v: "2",
+ ps: name,
+ add: address,
+ port: port,
+ id: uuid,
+ aid: "0",
+ net: "ws",
+ type: "none",
+ host: domain,
+ path: "/",
+ tls: "tls",
+ sni: domain
+ };
+ return 'vmess://' + btoa(JSON.stringify(vmessConfig));
+ }
}
-// 生成VMess链接 (已修复中文名导致1101报错的问题)
-function generateVMessLinksFromSource(list, user, workerDomain, disableNonTLS = false, customPath = '/', echConfig = null) {
- const CF_HTTP_PORTS = [80, 8080, 8880, 2052, 2082, 2086, 2095];
- const CF_HTTPS_PORTS = [443, 2053, 2083, 2087, 2096, 8443];
- const defaultHttpsPorts = [443];
- const defaultHttpPorts = disableNonTLS ? [] : [80];
- const links = [];
- const wsPath = customPath || '/';
-
- list.forEach(item => {
- let nodeNameBase = item.isp ? item.isp.replace(/\s/g, '_') : (item.name || item.domain || item.ip);
- if (item.colo && item.colo.trim()) {
- nodeNameBase = `${nodeNameBase}-${item.colo.trim()}`;
- }
- const safeIP = item.ip.includes(':') ? `[${item.ip}]` : item.ip;
-
- let portsToGenerate = [];
-
- if (item.port) {
- const port = item.port;
- if (CF_HTTPS_PORTS.includes(port)) {
- portsToGenerate.push({ port: port, tls: true });
- } else if (CF_HTTP_PORTS.includes(port)) {
- if (!disableNonTLS) {
- portsToGenerate.push({ port: port, tls: false });
- }
- } else {
- portsToGenerate.push({ port: port, tls: true });
- }
- } else {
- defaultHttpsPorts.forEach(port => {
- portsToGenerate.push({ port: port, tls: true });
- });
- defaultHttpPorts.forEach(port => {
- portsToGenerate.push({ port: port, tls: false });
- });
- }
-
- portsToGenerate.forEach(({ port, tls }) => {
- const vmessConfig = {
- v: "2",
- ps: tls ? `${nodeNameBase}-${port}-VMess-WS-TLS` : `${nodeNameBase}-${port}-VMess-WS`,
- add: safeIP,
- port: port.toString(),
- id: user,
- aid: "0",
- scy: "auto",
- net: "ws",
- type: "none",
- host: workerDomain,
- path: wsPath,
- tls: tls ? "tls" : "none"
- };
- if (tls) {
- vmessConfig.sni = workerDomain;
- vmessConfig.fp = "chrome";
+// 生成 Clash 配置
+function generateClashConfig(nodes, domain) {
+ const proxies = nodes.map((node, index) => {
+ const url = new URL(node);
+ const protocol = url.protocol.replace(':', '');
+ const params = new URLSearchParams(url.search);
+
+ return {
+ name: decodeURIComponent(url.hash.substring(1)),
+ type: protocol,
+ server: url.hostname,
+ port: parseInt(url.port) || 443,
+ uuid: url.username,
+ tls: true,
+ 'skip-cert-verify': false,
+ network: 'ws',
+ 'ws-opts': {
+ path: params.get('path') || '/',
+ headers: { Host: domain }
}
-
- // 核心修复:处理中文编码,防止 btoa 报错
- const jsonStr = JSON.stringify(vmessConfig);
- const vmessBase64 = btoa(encodeURIComponent(jsonStr).replace(/%([0-9A-F]{2})/g,
- function toSolidBytes(match, p1) {
- return String.fromCharCode('0x' + p1);
- }));
-
- links.push(`vmess://${vmessBase64}`);
- });
+ };
});
- return links;
-}
-
-// 从GitHub IP生成链接(VLESS)
-function generateLinksFromNewIPs(list, user, workerDomain, customPath = '/', echConfig = null) {
- const CF_HTTP_PORTS = [80, 8080, 8880, 2052, 2082, 2086, 2095];
- const CF_HTTPS_PORTS = [443, 2053, 2083, 2087, 2096, 8443];
- const links = [];
- const wsPath = customPath || '/';
- const proto = 'vless';
- const echSuffix = echConfig ? `&alpn=h3%2Ch2%2Chttp%2F1.1&ech=${encodeURIComponent(echConfig)}` : '';
- list.forEach(item => {
- const nodeName = item.name.replace(/\s/g, '_');
- const port = item.port;
-
- if (CF_HTTPS_PORTS.includes(port)) {
- const wsNodeName = `${nodeName}-${port}-WS-TLS`;
- const link = `${proto}://${user}@${item.ip}:${port}?encryption=none&security=tls&sni=${workerDomain}&fp=chrome&type=ws&host=${workerDomain}&path=${wsPath}${echSuffix}#${encodeURIComponent(wsNodeName)}`;
- links.push(link);
- } else if (CF_HTTP_PORTS.includes(port)) {
- const wsNodeName = `${nodeName}-${port}-WS`;
- const link = `${proto}://${user}@${item.ip}:${port}?encryption=none&security=none&type=ws&host=${workerDomain}&path=${wsPath}#${encodeURIComponent(wsNodeName)}`;
- links.push(link);
- } else {
- const wsNodeName = `${nodeName}-${port}-WS-TLS`;
- const link = `${proto}://${user}@${item.ip}:${port}?encryption=none&security=tls&sni=${workerDomain}&fp=chrome&type=ws&host=${workerDomain}&path=${wsPath}${echSuffix}#${encodeURIComponent(wsNodeName)}`;
- links.push(link);
- }
- });
- return links;
+ return {
+ proxies: proxies,
+ 'proxy-groups': [{
+ name: '自动选择',
+ type: 'url-test',
+ proxies: proxies.map(p => p.name),
+ url: 'http://www.gstatic.com/generate_204',
+ interval: 300
+ }]
+ };
}
-// 生成订阅内容
-async function handleSubscriptionRequest(request, user, customDomain, piu, ipv4Enabled, ipv6Enabled, ispMobile, ispUnicom, ispTelecom, evEnabled, etEnabled, vmEnabled, disableNonTLS, customPath, echConfig = null) {
+// 处理订阅请求
+async function handleSubscription(request, uuid) {
const url = new URL(request.url);
- const finalLinks = [];
- const workerDomain = url.hostname; // workerDomain始终是请求的hostname
- const nodeDomain = customDomain || url.hostname; // 用户输入的域名用于生成节点时的host/sni
- const target = url.searchParams.get('target') || 'base64';
- const wsPath = customPath || '/';
-
- async function addNodesFromList(list) {
- // 确保至少有一个协议被启用
- const hasProtocol = evEnabled || etEnabled || vmEnabled;
- const useVL = hasProtocol ? evEnabled : true; // 如果没有选择任何协议,默认使用VLESS
-
- if (useVL) {
- finalLinks.push(...generateLinksFromSource(list, user, nodeDomain, disableNonTLS, wsPath, echConfig));
- }
- if (etEnabled) {
- finalLinks.push(...await generateTrojanLinksFromSource(list, user, nodeDomain, disableNonTLS, wsPath, echConfig));
- }
- if (vmEnabled) {
- finalLinks.push(...generateVMessLinksFromSource(list, user, nodeDomain, disableNonTLS, wsPath, echConfig));
- }
- }
-
- // 原生地址
- const nativeList = [{ ip: workerDomain, isp: '原生地址' }];
- await addNodesFromList(nativeList);
-
- // 优选域名
- if (epd) {
- const domainList = directDomains.map(d => ({ ip: d.domain, isp: d.name || d.domain }));
- await addNodesFromList(domainList);
- }
-
- // 优选IP
- if (epi) {
- try {
- const dynamicIPList = await fetchDynamicIPs(ipv4Enabled, ipv6Enabled, ispMobile, ispUnicom, ispTelecom);
- if (dynamicIPList.length > 0) {
- await addNodesFromList(dynamicIPList);
- }
- } catch (error) {
- console.error('获取动态IP失败:', error);
- }
- }
-
- // GitHub优选 / 优选API
- if (egi) {
- try {
- // 检查是否是优选API URL(以https://开头)
- if (piu && piu.toLowerCase().startsWith('https://')) {
- // 从优选API获取IP列表
- const 优选API的IP = await 请求优选API([piu]);
- if (优选API的IP && 优选API的IP.length > 0) {
- // 解析IP字符串格式:IP:端口#备注
- const IP列表 = 优选API的IP.map(原始地址 => {
- // 统一正则: 匹配 域名/IPv4/IPv6地址 + 可选端口 + 可选备注
- const regex = /^(\[[\da-fA-F:]+\]|[\d.]+|[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?)*)(?::(\d+))?(?:#(.+))?$/;
- const match = 原始地址.match(regex);
-
- if (match) {
- const 节点地址 = match[1].replace(/[\[\]]/g, ''); // 移除IPv6的方括号
- const 节点端口 = match[2] || 443;
- const 节点备注 = match[3] || 节点地址;
- return {
- ip: 节点地址,
- port: parseInt(节点端口),
- name: 节点备注
- };
- }
- return null;
- }).filter(item => item !== null);
-
- if (IP列表.length > 0) {
- const hasProtocol = evEnabled || etEnabled || vmEnabled;
- const useVL = hasProtocol ? evEnabled : true;
-
- if (useVL) {
- finalLinks.push(...generateLinksFromNewIPs(IP列表, user, nodeDomain, wsPath, echConfig));
- }
- }
- }
- } else if (piu && piu.includes('\n')) {
- // 支持多行文本,包含混合格式(优选API URL + IP列表)
- const 完整优选列表 = await 整理成数组(piu);
- const 优选API = [], 优选IP = [], 其他节点 = [];
-
- for (const 元素 of 完整优选列表) {
- if (元素.toLowerCase().startsWith('https://')) {
- 优选API.push(元素);
- } else if (元素.toLowerCase().includes('://')) {
- 其他节点.push(元素);
- } else {
- 优选IP.push(元素);
- }
- }
-
- // 从优选API获取IP
- if (优选API.length > 0) {
- const 优选API的IP = await 请求优选API(优选API);
- 优选IP.push(...优选API的IP);
- }
-
- // 解析所有IP并生成节点
- if (优选IP.length > 0) {
- const IP列表 = 优选IP.map(原始地址 => {
- const regex = /^(\[[\da-fA-F:]+\]|[\d.]+|[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?)*)(?::(\d+))?(?:#(.+))?$/;
- const match = 原始地址.match(regex);
-
- if (match) {
- const 节点地址 = match[1].replace(/[\[\]]/g, '');
- const 节点端口 = match[2] || 443;
- const 节点备注 = match[3] || 节点地址;
- return {
- ip: 节点地址,
- port: parseInt(节点端口),
- name: 节点备注
- };
- }
- return null;
- }).filter(item => item !== null);
-
- if (IP列表.length > 0) {
- const hasProtocol = evEnabled || etEnabled || vmEnabled;
- const useVL = hasProtocol ? evEnabled : true;
-
- if (useVL) {
- finalLinks.push(...generateLinksFromNewIPs(IP列表, user, nodeDomain, wsPath, echConfig));
- }
- }
- }
- } else {
- // 原有的GitHub优选逻辑(单URL)
- const newIPList = await fetchAndParseNewIPs(piu);
- if (newIPList.length > 0) {
- const hasProtocol = evEnabled || etEnabled || vmEnabled;
- const useVL = hasProtocol ? evEnabled : true;
-
- if (useVL) {
- finalLinks.push(...generateLinksFromNewIPs(newIPList, user, nodeDomain, wsPath, echConfig));
- }
- }
- }
- } catch (error) {
- console.error('获取优选IP失败:', error);
- }
- }
-
- if (finalLinks.length === 0) {
- const errorRemark = "所有节点获取失败";
- const errorLink = `vless://00000000-0000-0000-0000-000000000000@127.0.0.1:80?encryption=none&security=none&type=ws&host=error.com&path=%2F#${encodeURIComponent(errorRemark)}`;
- finalLinks.push(errorLink);
- }
-
- let subscriptionContent;
- let contentType = 'text/plain; charset=utf-8';
+ const params = url.searchParams;
- switch (target.toLowerCase()) {
- case 'clash':
- case 'clashr':
- subscriptionContent = generateClashConfig(finalLinks);
- contentType = 'text/yaml; charset=utf-8';
- break;
- case 'surge':
- case 'surge2':
- case 'surge3':
- case 'surge4':
- subscriptionContent = generateSurgeConfig(finalLinks);
- break;
- case 'quantumult':
- case 'quanx':
- subscriptionContent = generateQuantumultConfig(finalLinks);
- break;
- default:
- subscriptionContent = btoa(finalLinks.join('\n'));
+ // 获取参数
+ const domain = params.get('domain');
+ if (!domain) {
+ return new Response('缺少 domain 参数', { status: 400 });
}
- return new Response(subscriptionContent, {
- headers: {
- 'Content-Type': contentType,
- 'Cache-Control': 'no-store, no-cache, must-revalidate, max-age=0',
- },
- });
-}
-
-// 生成Clash配置(简化版,返回YAML格式)
-function generateClashConfig(links) {
- let yaml = 'port: 7890\n';
- yaml += 'socks-port: 7891\n';
- yaml += 'allow-lan: false\n';
- yaml += 'mode: rule\n';
- yaml += 'log-level: info\n\n';
- yaml += 'proxies:\n';
+ const enableVLESS = params.get('ev') === 'yes';
+ const enableTrojan = params.get('et') === 'yes';
+ const enableVMess = params.get('mess') === 'yes';
+ const enableDefaultDomains = params.get('epd') === 'yes';
+ const enableOptimalIP = params.get('epi') === 'yes';
+ const enableGitHubIP = params.get('egi') === 'yes';
+ const customIPUrl = params.get('piu');
+ const target = params.get('target') || 'base64';
- const proxyNames = [];
- links.forEach((link, index) => {
- const name = decodeURIComponent(link.split('#')[1] || `节点${index + 1}`);
- proxyNames.push(name);
- const server = link.match(/@([^:]+):(\d+)/)?.[1] || '';
- const port = link.match(/@[^:]+:(\d+)/)?.[1] || '443';
- const uuid = link.match(/vless:\/\/([^@]+)@/)?.[1] || '';
- const tls = link.includes('security=tls');
- const path = link.match(/path=([^]+)/)?.[1] || '/';
- const host = link.match(/host=([^]+)/)?.[1] || '';
- const sni = link.match(/sni=([^]+)/)?.[1] || '';
- const echParam = link.match(/[?&]ech=([^]+)/)?.[1];
- const echDomain = echParam ? decodeURIComponent(echParam).split('+')[0] : '';
-
- yaml += ` - name: ${name}\n`;
- yaml += ` type: vless\n`;
- yaml += ` server: ${server}\n`;
- yaml += ` port: ${port}\n`;
- yaml += ` uuid: ${uuid}\n`;
- yaml += ` tls: ${tls}\n`;
- yaml += ` network: ws\n`;
- yaml += ` ws-opts:\n`;
- yaml += ` path: ${path}\n`;
- yaml += ` headers:\n`;
- yaml += ` Host: ${host}\n`;
- if (sni) {
- yaml += ` servername: ${sni}\n`;
- }
- if (echDomain) {
- yaml += ` ech-opts:\n`;
- yaml += ` enable: true\n`;
- yaml += ` query-server-name: ${echDomain}\n`;
- }
- });
+ const enableIPv4 = params.get('ipv4') !== 'no';
+ const enableIPv6 = params.get('ipv6') !== 'no';
+ const enableMobile = params.get('ispMobile') !== 'no';
+ const enableUnicom = params.get('ispUnicom') !== 'no';
+ const enableTelecom = params.get('ispTelecom') !== 'no';
- yaml += '\nproxy-groups:\n';
- yaml += ' - name: PROXY\n';
- yaml += ' type: select\n';
- yaml += ` proxies: [${proxyNames.map(n => `'${n}'`).join(', ')}]\n`;
- yaml += '\nrules:\n';
- yaml += ' - DOMAIN-SUFFIX,local,DIRECT\n';
- yaml += ' - IP-CIDR,127.0.0.0/8,DIRECT\n';
- yaml += ' - GEOIP,CN,DIRECT\n';
- yaml += ' - MATCH,PROXY\n';
+ // 收集所有地址
+ let addresses = [];
- return yaml;
-}
-
-// 生成Surge配置
-function generateSurgeConfig(links) {
- let config = '[Proxy]\n';
- links.forEach(link => {
- const name = decodeURIComponent(link.split('#')[1] || '节点');
- config += `${name} = vless, ${link.match(/@([^:]+):(\d+)/)?.[1] || ''}, ${link.match(/@[^:]+:(\d+)/)?.[1] || '443'}, username=${link.match(/vless:\/\/([^@]+)@/)?.[1] || ''}, tls=${link.includes('security=tls')}, ws=true, ws-path=${link.match(/path=([^]+)/)?.[1] || '/'}, ws-headers=Host:${link.match(/host=([^]+)/)?.[1] || ''}\n`;
- });
- config += '\n[Proxy Group]\nPROXY = select, ' + links.map((_, i) => decodeURIComponent(links[i].split('#')[1] || `节点${i + 1}`)).join(', ') + '\n';
- return config;
-}
-
-// 生成Quantumult配置
-function generateQuantumultConfig(links) {
- return btoa(links.join('\n'));
-}
-
-// 生成iOS 26风格的主页
-function generateHomePage(scuValue) {
- const scu = scuValue || 'https://url.v1.mk/sub';
- return `
-
-
-
-
-
-
- 服务器优选工具
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 自定义WebSocket路径,例如:/v2ray 或 /
-
-
-
-
-
-
-
-
-
-
-
- 自定义优选IP列表来源URL,留空则使用默认地址
-
-
-
-
-
-
-
-
-
-
-
-
- 仅TLS节点
- 启用后只生成带TLS的节点,不生成非TLS节点(如80端口)
-
-
-
-
-
-
- ECH (Encrypted Client Hello)
- 启用后节点链接将携带 ECH 参数,需客户端支持;开启时自动仅TLS
-
-
-
-
-
-
- 用于 ECH 配置查询的 DoH 地址
-
-
-
-
-
-
-
+ }
-
-
-`;
+ }
+
+ // 根据目标格式返回
+ if (target === 'clash') {
+ const config = generateClashConfig(nodes, domain);
+ return new Response(JSON.stringify(config, null, 2), {
+ headers: { 'Content-Type': 'application/json' }
+ });
+ } else {
+ // Base64 格式
+ const content = nodes.join('\n');
+ const base64Content = btoa(unescape(encodeURIComponent(content)));
+ return new Response(base64Content, {
+ headers: { 'Content-Type': 'text/plain' }
+ });
+ }
}
-// 主处理函数
export default {
- async fetch(request, env, ctx) {
+ async fetch(request) {
const url = new URL(request.url);
- const path = url.pathname;
-
- // 主页
- if (path === '/' || path === '') {
- const scuValue = env?.scu || scu;
- return new Response(generateHomePage(scuValue), {
- headers: { 'Content-Type': 'text/html; charset=utf-8' }
- });
- }
- // 测试优选API API: /test-optimize-api?url=xxx&port=443
- if (path === '/test-optimize-api') {
- if (request.method === 'OPTIONS') {
- return new Response(null, {
- headers: {
- 'Access-Control-Allow-Origin': '*',
- 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
- 'Access-Control-Allow-Headers': 'Content-Type'
- }
- });
- }
+ // 代理 3xui.xlihf.top 到服务器 8080 端口
+ if (url.hostname === '3xui.xlihf.top') {
+ const targetUrl = `http://192.227.232.131:8080${url.pathname}${url.search}`;
- const apiUrl = url.searchParams.get('url');
- const port = url.searchParams.get('port') || '443';
- const timeout = parseInt(url.searchParams.get('timeout') || '3000');
-
- if (!apiUrl) {
- return new Response(JSON.stringify({
- success: false,
- error: '缺少url参数'
- }), {
- status: 400,
- headers: {
- 'Content-Type': 'application/json; charset=utf-8',
- 'Access-Control-Allow-Origin': '*'
- }
- });
- }
+ // 创建新的请求头
+ const headers = new Headers(request.headers);
+ headers.set('Host', '3xui.xlihf.top');
+ headers.set('X-Forwarded-For', request.headers.get('CF-Connecting-IP') || '');
+ headers.set('X-Real-IP', request.headers.get('CF-Connecting-IP') || '');
try {
- const results = await 请求优选API([apiUrl], port, timeout);
- return new Response(JSON.stringify({
- success: true,
- results: results,
- total: results.length,
- message: `成功获取 ${results.length} 个优选IP`
- }, null, 2), {
- headers: {
- 'Content-Type': 'application/json; charset=utf-8',
- 'Access-Control-Allow-Origin': '*'
- }
+ const response = await fetch(targetUrl, {
+ method: request.method,
+ headers: headers,
+ body: request.body,
+ redirect: 'follow'
});
- } catch (error) {
- return new Response(JSON.stringify({
- success: false,
- error: error.message
- }), {
- status: 500,
- headers: {
- 'Content-Type': 'application/json; charset=utf-8',
- 'Access-Control-Allow-Origin': '*'
- }
+
+ // 返回响应
+ return new Response(response.body, {
+ status: response.status,
+ statusText: response.statusText,
+ headers: response.headers
});
+ } catch (error) {
+ return new Response('代理错误: ' + error.message, { status: 502 });
}
}
- // 订阅请求格式: /{UUID或Password}/sub?domain=xxx&epd=yes&epi=yes&egi=yes
- const pathMatch = path.match(/^\/([^\/]+)\/sub$/);
- if (pathMatch) {
- const uuid = pathMatch[1];
-
- const domain = url.searchParams.get('domain');
- if (!domain) {
- return new Response('缺少域名参数', { status: 400 });
- }
-
- // 从URL参数获取配置
- epd = url.searchParams.get('epd') !== 'no';
- epi = url.searchParams.get('epi') !== 'no';
- egi = url.searchParams.get('egi') !== 'no';
- const piu = url.searchParams.get('piu') || defaultIPURL;
-
- // 协议选择
- const evEnabled = url.searchParams.get('ev') === 'yes' || (url.searchParams.get('ev') === null && ev);
- const etEnabled = url.searchParams.get('et') === 'yes';
- const vmEnabled = url.searchParams.get('mess') === 'yes';
-
- // IPv4/IPv6选择
- const ipv4Enabled = url.searchParams.get('ipv4') !== 'no';
- const ipv6Enabled = url.searchParams.get('ipv6') !== 'no';
-
- // 运营商选择
- const ispMobile = url.searchParams.get('ispMobile') !== 'no';
- const ispUnicom = url.searchParams.get('ispUnicom') !== 'no';
- const ispTelecom = url.searchParams.get('ispTelecom') !== 'no';
-
- // TLS控制(ECH 开启时强制仅 TLS)
- let disableNonTLS = url.searchParams.get('dkby') === 'yes';
- const echParam = url.searchParams.get('ech');
- const echEnabled = echParam === 'yes' || (echParam === null && enableECH);
- if (echEnabled) disableNonTLS = true;
- const customDNSParam = url.searchParams.get('customDNS') || customDNS;
- const customECHDomainParam = url.searchParams.get('customECHDomain') || customECHDomain;
- const echConfig = echEnabled ? `${customECHDomainParam}+${customDNSParam}` : null;
-
- // 自定义路径
- const customPath = url.searchParams.get('path') || '/';
-
- return await handleSubscriptionRequest(request, uuid, domain, piu, ipv4Enabled, ipv6Enabled, ispMobile, ispUnicom, ispTelecom, evEnabled, etEnabled, vmEnabled, disableNonTLS, customPath, echConfig);
+ const path = url.pathname;
+
+ // 首页
+ if (path === '/' || path === '') {
+ return new Response(HTML_CONTENT, {
+ headers: { 'Content-Type': 'text/html;charset=UTF-8' }
+ });
+ }
+
+ // 订阅路径: /{uuid}/sub
+ const match = path.match(/^\/([^\/]+)\/sub$/);
+ if (match) {
+ const uuid = match[1];
+ return handleSubscription(request, uuid);
}
return new Response('Not Found', { status: 404 });
|