mirror of
https://github.com/lush2020/edgetunnel.git
synced 2026-03-24 00:48:39 +08:00
feat: 新增CF位置信息加载和地区筛选功能,优化IP测试逻辑
This commit is contained in:
@@ -280,3 +280,4 @@
|
|||||||
- [ACL4SSR](https://github.com/ACL4SSR/ACL4SSR/tree/master/Clash/config)
|
- [ACL4SSR](https://github.com/ACL4SSR/ACL4SSR/tree/master/Clash/config)
|
||||||
- [股神](https://t.me/CF_NAT/38889)
|
- [股神](https://t.me/CF_NAT/38889)
|
||||||
- [Workers/Pages Metrics](https://t.me/zhetengsha/3382)
|
- [Workers/Pages Metrics](https://t.me/zhetengsha/3382)
|
||||||
|
- [白嫖哥](https://t.me/bestcfipas)
|
||||||
410
_worker.js
410
_worker.js
@@ -2592,7 +2592,33 @@ async function resolveToIPv6(target) {
|
|||||||
async function bestIP(request, env, txt = 'ADD.txt') {
|
async function bestIP(request, env, txt = 'ADD.txt') {
|
||||||
const country = request.cf?.country || 'CN';
|
const country = request.cf?.country || 'CN';
|
||||||
const url = new URL(request.url);
|
const url = new URL(request.url);
|
||||||
|
async function getNipDomain() {
|
||||||
|
try {
|
||||||
|
const response = await fetch(atob('aHR0cHM6Ly9jbG91ZGZsYXJlLWRucy5jb20vZG5zLXF1ZXJ5P25hbWU9bmlwLjA5MDIyNy54eXomdHlwZT1UWFQ='), {
|
||||||
|
headers: {
|
||||||
|
'Accept': 'application/dns-json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
const data = await response.json();
|
||||||
|
if (data.Status === 0 && data.Answer && data.Answer.length > 0) {
|
||||||
|
// TXT记录的值通常包含在引号中,需要去除引号
|
||||||
|
const txtRecord = data.Answer[0].data;
|
||||||
|
// 去除首尾的引号
|
||||||
|
const domain = txtRecord.replace(/^"(.*)"$/, '$1');
|
||||||
|
console.log('通过DoH解析获取到域名: ' + domain);
|
||||||
|
return domain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.warn('DoH解析失败,使用默认域名');
|
||||||
|
return atob('bmlwLmxmcmVlLm9yZw==');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('DoH解析出错:', error);
|
||||||
|
return atob('aXAuMDkwMjI3Lnh5eg==');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const nipDomain = await getNipDomain();
|
||||||
async function GetCFIPs(ipSource = 'official', targetPort = '443') {
|
async function GetCFIPs(ipSource = 'official', targetPort = '443') {
|
||||||
try {
|
try {
|
||||||
let response;
|
let response;
|
||||||
@@ -2635,11 +2661,11 @@ async function bestIP(request, env, txt = 'ADD.txt') {
|
|||||||
|
|
||||||
console.log(`反代IP列表解析完成,端口${targetPort}匹配到${validIps.length}个有效IP`);
|
console.log(`反代IP列表解析完成,端口${targetPort}匹配到${validIps.length}个有效IP`);
|
||||||
|
|
||||||
// 如果超过1000个IP,随机选择1000个
|
// 如果超过512个IP,随机选择512个
|
||||||
if (validIps.length > 1000) {
|
if (validIps.length > 512) {
|
||||||
const shuffled = [...validIps].sort(() => 0.5 - Math.random());
|
const shuffled = [...validIps].sort(() => 0.5 - Math.random());
|
||||||
const selectedIps = shuffled.slice(0, 1000);
|
const selectedIps = shuffled.slice(0, 512);
|
||||||
console.log(`IP数量超过1000个,随机选择了${selectedIps.length}个IP`);
|
console.log(`IP数量超过512个,随机选择了${selectedIps.length}个IP`);
|
||||||
return selectedIps;
|
return selectedIps;
|
||||||
} else {
|
} else {
|
||||||
return validIps;
|
return validIps;
|
||||||
@@ -2667,7 +2693,7 @@ async function bestIP(request, env, txt = 'ADD.txt') {
|
|||||||
const cidrs = text.split('\n').filter(line => line.trim() && !line.startsWith('#'));
|
const cidrs = text.split('\n').filter(line => line.trim() && !line.startsWith('#'));
|
||||||
|
|
||||||
const ips = new Set(); // 使用Set去重
|
const ips = new Set(); // 使用Set去重
|
||||||
const targetCount = 1000;
|
const targetCount = 512;
|
||||||
let round = 1;
|
let round = 1;
|
||||||
|
|
||||||
// 不断轮次生成IP直到达到目标数量
|
// 不断轮次生成IP直到达到目标数量
|
||||||
@@ -2773,9 +2799,9 @@ async function bestIP(request, env, txt = 'ADD.txt') {
|
|||||||
|
|
||||||
// 构建返回格式
|
// 构建返回格式
|
||||||
if (comment) {
|
if (comment) {
|
||||||
return `${ip}:${port}#${comment}`;
|
return ip + ':' + port + '#' + comment;
|
||||||
} else {
|
} else {
|
||||||
return `${ip}:${port}`;
|
return ip + ':' + port;
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -3274,6 +3300,28 @@ async function bestIP(request, env, txt = 'ADD.txt') {
|
|||||||
color: #e65100;
|
color: #e65100;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
.region-buttons {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
.region-btn {
|
||||||
|
padding: 6px 12px;
|
||||||
|
background-color: #e0e0e0;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
.region-btn:hover {
|
||||||
|
background-color: #d5d5d5;
|
||||||
|
}
|
||||||
|
.region-btn.active {
|
||||||
|
background-color: #2196F3;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -3325,7 +3373,6 @@ async function bestIP(request, env, txt = 'ADD.txt') {
|
|||||||
<option value="official">CF官方列表</option>
|
<option value="official">CF官方列表</option>
|
||||||
<option value="cm">CM整理列表</option>
|
<option value="cm">CM整理列表</option>
|
||||||
<option value="as13335">AS13335列表</option>
|
<option value="as13335">AS13335列表</option>
|
||||||
<option value="as35916">AS35916列表</option>
|
|
||||||
<option value="as209242">AS209242列表</option>
|
<option value="as209242">AS209242列表</option>
|
||||||
<option value="as24429">AS24429列表(Alibaba)</option>
|
<option value="as24429">AS24429列表(Alibaba)</option>
|
||||||
<option value="as199524">AS199524列表(G-Core)</option>
|
<option value="as199524">AS199524列表(G-Core)</option>
|
||||||
@@ -3360,6 +3407,7 @@ async function bestIP(request, env, txt = 'ADD.txt') {
|
|||||||
|
|
||||||
<h2>IP列表 <span id="result-count"></span></h2>
|
<h2>IP列表 <span id="result-count"></span></h2>
|
||||||
<div class="ip-display-info" id="ip-display-info"></div>
|
<div class="ip-display-info" id="ip-display-info"></div>
|
||||||
|
<div id="region-filter" style="margin: 15px 0; display: none;"></div>
|
||||||
<div class="ip-list" id="ip-list">
|
<div class="ip-list" id="ip-list">
|
||||||
<div class="ip-item">请选择端口和IP库,然后点击"开始延迟测试"加载IP列表</div>
|
<div class="ip-item">请选择端口和IP库,然后点击"开始延迟测试"加载IP列表</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -3373,6 +3421,7 @@ async function bestIP(request, env, txt = 'ADD.txt') {
|
|||||||
let displayedResults = []; // 新增:存储当前显示的结果
|
let displayedResults = []; // 新增:存储当前显示的结果
|
||||||
let showingAll = false; // 新增:标记是否显示全部内容
|
let showingAll = false; // 新增:标记是否显示全部内容
|
||||||
let currentDisplayType = 'loading'; // 新增:当前显示类型 'loading' | 'results'
|
let currentDisplayType = 'loading'; // 新增:当前显示类型 'loading' | 'results'
|
||||||
|
let cloudflareLocations = {}; // 新增:存储Cloudflare位置信息
|
||||||
|
|
||||||
// 新增:本地存储管理
|
// 新增:本地存储管理
|
||||||
const StorageKeys = {
|
const StorageKeys = {
|
||||||
@@ -3380,6 +3429,27 @@ async function bestIP(request, env, txt = 'ADD.txt') {
|
|||||||
IP_SOURCE: 'cf-ip-test-source'
|
IP_SOURCE: 'cf-ip-test-source'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 新增:加载Cloudflare位置信息
|
||||||
|
async function loadCloudflareLocations() {
|
||||||
|
try {
|
||||||
|
const response = await fetch('https://speed.cloudflare.com/locations');
|
||||||
|
if (response.ok) {
|
||||||
|
const locations = await response.json();
|
||||||
|
// 转换为以iata为key的对象,便于快速查找
|
||||||
|
cloudflareLocations = {};
|
||||||
|
locations.forEach(location => {
|
||||||
|
cloudflareLocations[location.iata] = location;
|
||||||
|
});
|
||||||
|
console.log('Cloudflare位置信息加载成功:', Object.keys(cloudflareLocations).length, '个位置');
|
||||||
|
} else {
|
||||||
|
console.warn('无法加载Cloudflare位置信息,将使用原始colo值');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载Cloudflare位置信息失败:', error);
|
||||||
|
console.warn('将使用原始colo值');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 初始化页面设置
|
// 初始化页面设置
|
||||||
function initializeSettings() {
|
function initializeSettings() {
|
||||||
const portSelect = document.getElementById('port-select');
|
const portSelect = document.getElementById('port-select');
|
||||||
@@ -3414,7 +3484,12 @@ async function bestIP(request, env, txt = 'ADD.txt') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 页面加载完成后初始化设置
|
// 页面加载完成后初始化设置
|
||||||
document.addEventListener('DOMContentLoaded', initializeSettings);
|
document.addEventListener('DOMContentLoaded', async function() {
|
||||||
|
// 先加载Cloudflare位置信息
|
||||||
|
await loadCloudflareLocations();
|
||||||
|
// 然后初始化页面设置
|
||||||
|
initializeSettings();
|
||||||
|
});
|
||||||
|
|
||||||
// 新增:切换显示更多/更少
|
// 新增:切换显示更多/更少
|
||||||
function toggleShowMore() {
|
function toggleShowMore() {
|
||||||
@@ -3524,7 +3599,17 @@ async function bestIP(request, env, txt = 'ADD.txt') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function saveIPs() {
|
async function saveIPs() {
|
||||||
if (displayedResults.length === 0) {
|
// 使用当前显示的结果而不是全部结果
|
||||||
|
let ipsToSave = [];
|
||||||
|
if (document.getElementById('region-filter') && document.getElementById('region-filter').style.display !== 'none') {
|
||||||
|
// 如果地区筛选器可见,使用筛选后的结果
|
||||||
|
ipsToSave = displayedResults;
|
||||||
|
} else {
|
||||||
|
// 否则使用全部测试结果
|
||||||
|
ipsToSave = testResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ipsToSave.length === 0) {
|
||||||
showMessage('没有可保存的IP结果', 'error');
|
showMessage('没有可保存的IP结果', 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -3538,8 +3623,8 @@ async function bestIP(request, env, txt = 'ADD.txt') {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// 只保存前16个最优IP
|
// 只保存前16个最优IP
|
||||||
const saveCount = Math.min(displayedResults.length, 16);
|
const saveCount = Math.min(ipsToSave.length, 16);
|
||||||
const ips = displayedResults.slice(0, saveCount).map(result => result.display);
|
const ips = ipsToSave.slice(0, saveCount).map(result => result.display);
|
||||||
|
|
||||||
const response = await fetch('?action=save', {
|
const response = await fetch('?action=save', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -3552,7 +3637,7 @@ async function bestIP(request, env, txt = 'ADD.txt') {
|
|||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
showMessage(\`\${data.message}(已保存前\${saveCount}个最优IP)\`, 'success');
|
showMessage(data.message + '(已保存前' + saveCount + '个最优IP)', 'success');
|
||||||
} else {
|
} else {
|
||||||
showMessage(data.error || '保存失败', 'error');
|
showMessage(data.error || '保存失败', 'error');
|
||||||
}
|
}
|
||||||
@@ -3566,7 +3651,17 @@ async function bestIP(request, env, txt = 'ADD.txt') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function appendIPs() {
|
async function appendIPs() {
|
||||||
if (displayedResults.length === 0) {
|
// 使用当前显示的结果而不是全部结果
|
||||||
|
let ipsToAppend = [];
|
||||||
|
if (document.getElementById('region-filter') && document.getElementById('region-filter').style.display !== 'none') {
|
||||||
|
// 如果地区筛选器可见,使用筛选后的结果
|
||||||
|
ipsToAppend = displayedResults;
|
||||||
|
} else {
|
||||||
|
// 否则使用全部测试结果
|
||||||
|
ipsToAppend = testResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ipsToAppend.length === 0) {
|
||||||
showMessage('没有可追加的IP结果', 'error');
|
showMessage('没有可追加的IP结果', 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -3580,8 +3675,8 @@ async function bestIP(request, env, txt = 'ADD.txt') {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// 只追加前16个最优IP
|
// 只追加前16个最优IP
|
||||||
const saveCount = Math.min(displayedResults.length, 16);
|
const saveCount = Math.min(ipsToAppend.length, 16);
|
||||||
const ips = displayedResults.slice(0, saveCount).map(result => result.display);
|
const ips = ipsToAppend.slice(0, saveCount).map(result => result.display);
|
||||||
|
|
||||||
const response = await fetch('?action=append', {
|
const response = await fetch('?action=append', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -3594,7 +3689,7 @@ async function bestIP(request, env, txt = 'ADD.txt') {
|
|||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
showMessage(\`\${data.message}(已追加前\${saveCount}个最优IP)\`, 'success');
|
showMessage(data.message + '(已追加前' + saveCount + '个最优IP)', 'success');
|
||||||
} else {
|
} else {
|
||||||
showMessage(data.error || '追加失败', 'error');
|
showMessage(data.error || '追加失败', 'error');
|
||||||
}
|
}
|
||||||
@@ -3620,7 +3715,7 @@ async function bestIP(request, env, txt = 'ADD.txt') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function testIP(ip, port) {
|
async function testIP(ip, port) {
|
||||||
const timeout = 999;
|
const timeout = 5000; // 增加超时时间到5秒
|
||||||
|
|
||||||
// 解析IP格式
|
// 解析IP格式
|
||||||
const parsedIP = parseIPFormat(ip, port);
|
const parsedIP = parseIPFormat(ip, port);
|
||||||
@@ -3628,46 +3723,40 @@ async function bestIP(request, env, txt = 'ADD.txt') {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 第一次测试
|
// 进行测试,最多重试3次
|
||||||
const firstResult = await singleTest(parsedIP.host, parsedIP.port, timeout);
|
let lastError = null;
|
||||||
if (!firstResult) {
|
for (let attempt = 1; attempt <= 3; attempt++) {
|
||||||
return null; // 第一次测试失败,直接返回
|
const result = await singleTest(parsedIP.host, parsedIP.port, timeout);
|
||||||
}
|
if (result) {
|
||||||
|
console.log(\`IP \${parsedIP.host}:\${parsedIP.port} 第\${attempt}次测试成功: \${result.latency}ms, colo: \${result.colo}, 类型: \${result.type}\`);
|
||||||
|
|
||||||
// 第一次测试成功,再进行第二次测试
|
// 根据colo字段获取国家代码
|
||||||
console.log(\`IP \${parsedIP.host}:\${parsedIP.port} 第一次测试成功: \${firstResult.latency}ms,进行第二次测试...\`);
|
const locationCode = cloudflareLocations[result.colo] ? cloudflareLocations[result.colo].cca2 : result.colo;
|
||||||
|
|
||||||
const results = [firstResult];
|
|
||||||
|
|
||||||
// 进行第二次测试
|
|
||||||
const secondResult = await singleTest(parsedIP.host, parsedIP.port, timeout);
|
|
||||||
if (secondResult) {
|
|
||||||
results.push(secondResult);
|
|
||||||
console.log(\`IP \${parsedIP.host}:\${parsedIP.port} 第二次测试: \${secondResult.latency}ms\`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 取最低延迟
|
|
||||||
const bestResult = results.reduce((best, current) =>
|
|
||||||
current.latency < best.latency ? current : best
|
|
||||||
);
|
|
||||||
|
|
||||||
const displayLatency = Math.floor(bestResult.latency / 2);
|
|
||||||
|
|
||||||
console.log(\`IP \${parsedIP.host}:\${parsedIP.port} 最终结果: \${displayLatency}ms (原始: \${bestResult.latency}ms, 共\${results.length}次有效测试)\`);
|
|
||||||
|
|
||||||
// 生成显示格式
|
// 生成显示格式
|
||||||
const comment = parsedIP.comment || 'CF优选IP';
|
const typeText = result.type === 'official' ? '官方优选' : '反代优选';
|
||||||
const display = \`\${parsedIP.host}:\${parsedIP.port}#\${comment} \${displayLatency}ms\`;
|
const display = \`\${parsedIP.host}:\${parsedIP.port}#\${locationCode} \${typeText} \${result.latency}ms\`;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
ip: parsedIP.host,
|
ip: parsedIP.host,
|
||||||
port: parsedIP.port,
|
port: parsedIP.port,
|
||||||
latency: displayLatency,
|
latency: result.latency,
|
||||||
originalLatency: bestResult.latency,
|
colo: result.colo,
|
||||||
testCount: results.length,
|
type: result.type,
|
||||||
comment: comment,
|
locationCode: locationCode,
|
||||||
|
comment: \`\${locationCode} \${typeText}\`,
|
||||||
display: display
|
display: display
|
||||||
};
|
};
|
||||||
|
} else {
|
||||||
|
console.log(\`IP \${parsedIP.host}:\${parsedIP.port} 第\${attempt}次测试失败\`);
|
||||||
|
if (attempt < 3) {
|
||||||
|
// 短暂延迟后重试
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 200));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null; // 所有尝试都失败
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增:解析IP格式的函数
|
// 新增:解析IP格式的函数
|
||||||
@@ -3710,38 +3799,107 @@ async function bestIP(request, env, txt = 'ADD.txt') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function singleTest(ip, port, timeout) {
|
async function singleTest(ip, port, timeout) {
|
||||||
const startTime = Date.now();
|
// 先进行预请求以缓存DNS解析结果
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
||||||
|
const parts = ip.split('.').map(part => {
|
||||||
|
const hex = parseInt(part, 10).toString(16);
|
||||||
|
return hex.length === 1 ? '0' + hex : hex; // 补零
|
||||||
|
});
|
||||||
|
const nip = parts.join('');
|
||||||
|
|
||||||
const response = await fetch(\`https://\${ip}:\${port}/cdn-cgi/trace\`, {
|
// 预请求,不计入延迟时间
|
||||||
|
await fetch('https://' + nip + '.${nipDomain}:' + port + '/cdn-cgi/trace', {
|
||||||
signal: controller.signal,
|
signal: controller.signal,
|
||||||
mode: 'cors'
|
mode: 'cors'
|
||||||
});
|
});
|
||||||
|
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
// 如果请求成功了,说明这个IP不是我们要的
|
} catch (preRequestError) {
|
||||||
|
// 预请求失败可以忽略,继续进行正式测试
|
||||||
|
console.log('预请求失败 (' + ip + ':' + port + '):', preRequestError.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 正式延迟测试
|
||||||
|
const startTime = Date.now();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const controller = new AbortController();
|
||||||
|
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
||||||
|
const parts = ip.split('.').map(part => {
|
||||||
|
const hex = parseInt(part, 10).toString(16);
|
||||||
|
return hex.length === 1 ? '0' + hex : hex; // 补零
|
||||||
|
});
|
||||||
|
const nip = parts.join('');
|
||||||
|
const response = await fetch('https://' + nip + '.${nipDomain}:' + port + '/cdn-cgi/trace', {
|
||||||
|
signal: controller.signal,
|
||||||
|
mode: 'cors'
|
||||||
|
});
|
||||||
|
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
|
||||||
|
// 检查响应状态
|
||||||
|
if (response.status === 200) {
|
||||||
|
const latency = Date.now() - startTime;
|
||||||
|
const responseText = await response.text();
|
||||||
|
|
||||||
|
// 解析trace响应
|
||||||
|
const traceData = parseTraceResponse(responseText);
|
||||||
|
|
||||||
|
if (traceData && traceData.ip && traceData.colo) {
|
||||||
|
// 判断IP类型
|
||||||
|
const responseIP = traceData.ip;
|
||||||
|
let ipType = 'official'; // 默认官方IP
|
||||||
|
|
||||||
|
// 检查是否是IPv6(包含冒号)或者IP相等
|
||||||
|
if (responseIP.includes(':') || responseIP === ip) {
|
||||||
|
ipType = 'proxy'; // 反代IP
|
||||||
|
}
|
||||||
|
// 如果responseIP与ip不相等且不是IPv6,则是官方IP
|
||||||
|
|
||||||
|
return {
|
||||||
|
ip: ip,
|
||||||
|
port: port,
|
||||||
|
latency: latency,
|
||||||
|
colo: traceData.colo,
|
||||||
|
type: ipType,
|
||||||
|
responseIP: responseIP
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const latency = Date.now() - startTime;
|
const latency = Date.now() - startTime;
|
||||||
|
|
||||||
// 检查是否是真正的超时(接近设定的timeout时间)
|
// 检查是否是真正的超时(接近设定的timeout时间)
|
||||||
if (latency >= timeout - 50) {
|
if (latency >= timeout - 100) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否是 Failed to fetch 错误(通常是SSL/证书错误)
|
return null;
|
||||||
if (error.name === 'TypeError' && error.message.includes('Failed to fetch')) {
|
}
|
||||||
return {
|
|
||||||
ip: ip,
|
|
||||||
port: port,
|
|
||||||
latency: latency
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 新增:解析trace响应的函数
|
||||||
|
function parseTraceResponse(responseText) {
|
||||||
|
try {
|
||||||
|
const lines = responseText.split('\\n');
|
||||||
|
const data = {};
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
const trimmedLine = line.trim();
|
||||||
|
if (trimmedLine && trimmedLine.includes('=')) {
|
||||||
|
const [key, value] = trimmedLine.split('=', 2);
|
||||||
|
data[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('解析trace响应失败:', error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3833,11 +3991,8 @@ async function bestIP(request, env, txt = 'ADD.txt') {
|
|||||||
case 'as13335':
|
case 'as13335':
|
||||||
ipSourceName = 'CF全段';
|
ipSourceName = 'CF全段';
|
||||||
break;
|
break;
|
||||||
case 'as35916':
|
|
||||||
ipSourceName = 'CF非官方1';
|
|
||||||
break;
|
|
||||||
case 'as209242':
|
case 'as209242':
|
||||||
ipSourceName = 'CF非官方2';
|
ipSourceName = 'CF非官方';
|
||||||
break;
|
break;
|
||||||
case 'as24429':
|
case 'as24429':
|
||||||
ipSourceName = 'Alibaba';
|
ipSourceName = 'Alibaba';
|
||||||
@@ -3852,7 +4007,7 @@ async function bestIP(request, env, txt = 'ADD.txt') {
|
|||||||
ipSourceName = '未知';
|
ipSourceName = '未知';
|
||||||
}
|
}
|
||||||
|
|
||||||
progressText.textContent = \`正在加载 \${ipSourceName} IP列表...\`;
|
progressText.textContent = '正在加载 ' + ipSourceName + ' IP列表...';
|
||||||
|
|
||||||
// 加载IP列表
|
// 加载IP列表
|
||||||
originalIPs = await loadIPs(selectedIPSource, selectedPort);
|
originalIPs = await loadIPs(selectedIPSource, selectedPort);
|
||||||
@@ -3869,21 +4024,21 @@ async function bestIP(request, env, txt = 'ADD.txt') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 更新IP数量显示
|
// 更新IP数量显示
|
||||||
ipCount.textContent = \`\${originalIPs.length} 个\`;
|
ipCount.textContent = originalIPs.length + ' 个';
|
||||||
|
|
||||||
// 显示加载的IP列表(默认显示前16个)
|
// 显示加载的IP列表(默认显示前16个)
|
||||||
displayLoadedIPs();
|
displayLoadedIPs();
|
||||||
|
|
||||||
// 开始测试
|
// 开始测试
|
||||||
testBtn.textContent = '测试中...';
|
testBtn.textContent = '测试中...';
|
||||||
progressText.textContent = \`开始测试端口 \${selectedPort}...\`;
|
progressText.textContent = '开始测试端口 ' + selectedPort + '...';
|
||||||
currentDisplayType = 'testing'; // 切换到测试状态
|
currentDisplayType = 'testing'; // 切换到测试状态
|
||||||
|
|
||||||
// 在测试开始时隐藏显示更多按钮
|
// 在测试开始时隐藏显示更多按钮
|
||||||
showMoreSection.style.display = 'none';
|
showMoreSection.style.display = 'none';
|
||||||
|
|
||||||
// 使用16个并发线程测试
|
// 使用更高的并发数(从16增加到32)来加快测试速度
|
||||||
const results = await testIPsWithConcurrency(originalIPs, selectedPort, 16);
|
const results = await testIPsWithConcurrency(originalIPs, selectedPort, 32);
|
||||||
|
|
||||||
// 按延迟排序
|
// 按延迟排序
|
||||||
testResults = results.sort((a, b) => a.latency - b.latency);
|
testResults = results.sort((a, b) => a.latency - b.latency);
|
||||||
@@ -3893,11 +4048,14 @@ async function bestIP(request, env, txt = 'ADD.txt') {
|
|||||||
showingAll = false; // 重置显示状态
|
showingAll = false; // 重置显示状态
|
||||||
displayResults();
|
displayResults();
|
||||||
|
|
||||||
|
// 创建地区筛选器
|
||||||
|
createRegionFilter();
|
||||||
|
|
||||||
testBtn.disabled = false;
|
testBtn.disabled = false;
|
||||||
testBtn.textContent = '重新测试';
|
testBtn.textContent = '重新测试';
|
||||||
portSelect.disabled = false;
|
portSelect.disabled = false;
|
||||||
ipSourceSelect.disabled = false;
|
ipSourceSelect.disabled = false;
|
||||||
progressText.textContent = \`完成 - 有效IP: \${testResults.length}/\${originalIPs.length} (端口: \${selectedPort}, IP库: \${ipSourceName})\`;
|
progressText.textContent = '完成 - 有效IP: ' + testResults.length + '/' + originalIPs.length + ' (端口: ' + selectedPort + ', IP库: ' + ipSourceName + ')';
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增:加载IP列表的函数
|
// 新增:加载IP列表的函数
|
||||||
@@ -3942,12 +4100,12 @@ async function bestIP(request, env, txt = 'ADD.txt') {
|
|||||||
|
|
||||||
// 更新结果计数显示
|
// 更新结果计数显示
|
||||||
if (testResults.length <= 16) {
|
if (testResults.length <= 16) {
|
||||||
resultCount.textContent = \`(共测试出 \${testResults.length} 个有效IP)\`;
|
resultCount.textContent = '(共测试出 ' + testResults.length + ' 个有效IP)';
|
||||||
ipDisplayInfo.textContent = \`显示全部 \${testResults.length} 个测试结果\`;
|
ipDisplayInfo.textContent = '显示全部 ' + testResults.length + ' 个测试结果';
|
||||||
showMoreSection.style.display = 'none';
|
showMoreSection.style.display = 'none';
|
||||||
} else {
|
} else {
|
||||||
resultCount.textContent = \`(共测试出 \${testResults.length} 个有效IP)\`;
|
resultCount.textContent = '(共测试出 ' + testResults.length + ' 个有效IP)';
|
||||||
ipDisplayInfo.textContent = \`显示前 \${maxDisplayCount} 个测试结果,共 \${testResults.length} 个有效IP\`;
|
ipDisplayInfo.textContent = '显示前 ' + maxDisplayCount + ' 个测试结果,共 ' + testResults.length + ' 个有效IP';
|
||||||
showMoreSection.style.display = 'block';
|
showMoreSection.style.display = 'block';
|
||||||
showMoreBtn.textContent = showingAll ? '显示更少' : '显示更多';
|
showMoreBtn.textContent = showingAll ? '显示更少' : '显示更多';
|
||||||
showMoreBtn.disabled = false; // 确保在结果显示时启用按钮
|
showMoreBtn.disabled = false; // 确保在结果显示时启用按钮
|
||||||
@@ -3958,7 +4116,107 @@ async function bestIP(request, env, txt = 'ADD.txt') {
|
|||||||
if (result.latency > 200) className = 'bad-latency';
|
if (result.latency > 200) className = 'bad-latency';
|
||||||
else if (result.latency > 100) className = 'medium-latency';
|
else if (result.latency > 100) className = 'medium-latency';
|
||||||
|
|
||||||
return \`<div class="ip-item \${className}">\${result.display}</div>\`;
|
return '<div class="ip-item ' + className + '">' + result.display + '</div>';
|
||||||
|
}).join('');
|
||||||
|
|
||||||
|
ipList.innerHTML = resultsHTML;
|
||||||
|
updateButtonStates();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增:创建地区筛选器
|
||||||
|
function createRegionFilter() {
|
||||||
|
// 获取所有唯一的地区代码(使用cca2代码)
|
||||||
|
const uniqueRegions = [...new Set(testResults.map(result => result.locationCode))];
|
||||||
|
uniqueRegions.sort(); // 按字母顺序排序
|
||||||
|
|
||||||
|
const filterContainer = document.getElementById('region-filter');
|
||||||
|
if (!filterContainer) return;
|
||||||
|
|
||||||
|
if (uniqueRegions.length === 0) {
|
||||||
|
filterContainer.style.display = 'none';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建筛选按钮
|
||||||
|
let filterHTML = '<h3>地区筛选:</h3><div class="region-buttons">';
|
||||||
|
filterHTML += '<button class="region-btn active" data-region="all">全部 (' + testResults.length + ')</button>';
|
||||||
|
|
||||||
|
uniqueRegions.forEach(region => {
|
||||||
|
const count = testResults.filter(r => r.locationCode === region).length;
|
||||||
|
filterHTML += '<button class="region-btn" data-region="' + region + '">' + region + ' (' + count + ')</button>';
|
||||||
|
});
|
||||||
|
|
||||||
|
filterHTML += '</div>';
|
||||||
|
filterContainer.innerHTML = filterHTML;
|
||||||
|
filterContainer.style.display = 'block';
|
||||||
|
|
||||||
|
// 添加点击事件
|
||||||
|
document.querySelectorAll('.region-btn').forEach(button => {
|
||||||
|
button.addEventListener('click', function() {
|
||||||
|
// 更新活动按钮
|
||||||
|
document.querySelectorAll('.region-btn').forEach(btn => {
|
||||||
|
btn.classList.remove('active');
|
||||||
|
});
|
||||||
|
this.classList.add('active');
|
||||||
|
|
||||||
|
// 筛选结果
|
||||||
|
const selectedRegion = this.getAttribute('data-region');
|
||||||
|
if (selectedRegion === 'all') {
|
||||||
|
displayedResults = [...testResults];
|
||||||
|
} else {
|
||||||
|
displayedResults = testResults.filter(result => result.locationCode === selectedRegion);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置显示状态
|
||||||
|
showingAll = false;
|
||||||
|
displayFilteredResults();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增:显示筛选后的结果
|
||||||
|
function displayFilteredResults() {
|
||||||
|
const ipList = document.getElementById('ip-list');
|
||||||
|
const resultCount = document.getElementById('result-count');
|
||||||
|
const showMoreSection = document.getElementById('show-more-section');
|
||||||
|
const showMoreBtn = document.getElementById('show-more-btn');
|
||||||
|
const ipDisplayInfo = document.getElementById('ip-display-info');
|
||||||
|
|
||||||
|
if (displayedResults.length === 0) {
|
||||||
|
ipList.innerHTML = '<div class="ip-item">未找到有效的IP</div>';
|
||||||
|
resultCount.textContent = '';
|
||||||
|
ipDisplayInfo.textContent = '';
|
||||||
|
showMoreSection.style.display = 'none';
|
||||||
|
updateButtonStates();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确定显示数量
|
||||||
|
const maxDisplayCount = showingAll ? displayedResults.length : Math.min(displayedResults.length, 16);
|
||||||
|
const currentResults = displayedResults.slice(0, maxDisplayCount);
|
||||||
|
|
||||||
|
// 更新结果计数显示
|
||||||
|
const totalCount = testResults.length;
|
||||||
|
const filteredCount = displayedResults.length;
|
||||||
|
|
||||||
|
if (filteredCount <= 16) {
|
||||||
|
resultCount.textContent = '(共测试出 ' + totalCount + ' 个有效IP,筛选出 ' + filteredCount + ' 个)';
|
||||||
|
ipDisplayInfo.textContent = '显示全部 ' + filteredCount + ' 个筛选结果';
|
||||||
|
showMoreSection.style.display = 'none';
|
||||||
|
} else {
|
||||||
|
resultCount.textContent = '(共测试出 ' + totalCount + ' 个有效IP,筛选出 ' + filteredCount + ' 个)';
|
||||||
|
ipDisplayInfo.textContent = '显示前 ' + maxDisplayCount + ' 个筛选结果,共 ' + filteredCount + ' 个';
|
||||||
|
showMoreSection.style.display = 'block';
|
||||||
|
showMoreBtn.textContent = showingAll ? '显示更少' : '显示更多';
|
||||||
|
showMoreBtn.disabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const resultsHTML = currentResults.map(result => {
|
||||||
|
let className = 'good-latency';
|
||||||
|
if (result.latency > 200) className = 'bad-latency';
|
||||||
|
else if (result.latency > 100) className = 'medium-latency';
|
||||||
|
|
||||||
|
return '<div class="ip-item ' + className + '">' + result.display + '</div>';
|
||||||
}).join('');
|
}).join('');
|
||||||
|
|
||||||
ipList.innerHTML = resultsHTML;
|
ipList.innerHTML = resultsHTML;
|
||||||
|
|||||||
Reference in New Issue
Block a user