mirror of
https://github.com/lush2020/edgetunnel.git
synced 2026-03-21 17:12:33 +08:00
新增在线优选IP功能,支持动态加载IP列表和用户界面优化
This commit is contained in:
351
明文源码.js
351
明文源码.js
@@ -2178,6 +2178,17 @@ async function KV(request, env, txt = 'ADD.txt') {
|
|||||||
.back-btn:hover {
|
.back-btn:hover {
|
||||||
background: #555;
|
background: #555;
|
||||||
}
|
}
|
||||||
|
.bestip-btn {
|
||||||
|
background: #2196F3;
|
||||||
|
padding: 6px 15px;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.bestip-btn:hover {
|
||||||
|
background: #1976D2;
|
||||||
|
}
|
||||||
.save-status {
|
.save-status {
|
||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
@@ -2204,6 +2215,7 @@ async function KV(request, env, txt = 'ADD.txt') {
|
|||||||
id="content">${content}</textarea>
|
id="content">${content}</textarea>
|
||||||
<div class="save-container">
|
<div class="save-container">
|
||||||
<button class="back-btn" onclick="goBack()">返回配置页</button>
|
<button class="back-btn" onclick="goBack()">返回配置页</button>
|
||||||
|
<button class="bestip-btn" onclick="goBestIP()">在线优选IP</button>
|
||||||
<button class="save-btn" onclick="saveContent(this)">保存</button>
|
<button class="save-btn" onclick="saveContent(this)">保存</button>
|
||||||
<span class="save-status" id="saveStatus"></span>
|
<span class="save-status" id="saveStatus"></span>
|
||||||
</div>
|
</div>
|
||||||
@@ -2225,6 +2237,12 @@ async function KV(request, env, txt = 'ADD.txt') {
|
|||||||
window.location.href = parentUrl;
|
window.location.href = parentUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function goBestIP() {
|
||||||
|
const currentUrl = window.location.href;
|
||||||
|
const parentUrl = currentUrl.substring(0, currentUrl.lastIndexOf('/'));
|
||||||
|
window.location.href = parentUrl + '/bestip';
|
||||||
|
}
|
||||||
|
|
||||||
function replaceFullwidthColon() {
|
function replaceFullwidthColon() {
|
||||||
const text = textarea.value;
|
const text = textarea.value;
|
||||||
textarea.value = text.replace(/:/g, ':');
|
textarea.value = text.replace(/:/g, ':');
|
||||||
@@ -2549,12 +2567,30 @@ 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 GetCFIPs() {
|
async function GetCFIPs(ipSource = 'official') {
|
||||||
try {
|
try {
|
||||||
// 首先尝试第一个URL
|
let response;
|
||||||
let response = await fetch('https://raw.githubusercontent.com/ipverse/asn-ip/master/as/13335/ipv4-aggregated.txt');
|
if (ipSource === 'as13335') {
|
||||||
if (!response.ok) {
|
// AS13335列表
|
||||||
// 如果失败,尝试第二个URL
|
response = await fetch('https://raw.githubusercontent.com/ipverse/asn-ip/master/as/13335/ipv4-aggregated.txt');
|
||||||
|
} else if (ipSource === 'as209242') {
|
||||||
|
// AS209242列表
|
||||||
|
response = await fetch('https://raw.githubusercontent.com/ipverse/asn-ip/master/as/209242/ipv4-aggregated.txt');
|
||||||
|
} else if (ipSource === 'cm') {
|
||||||
|
// CM整理列表
|
||||||
|
response = await fetch('https://raw.githubusercontent.com/cmliu/cmliu/main/CF-CIDR.txt');
|
||||||
|
} else if (ipSource === 'baipiao') {
|
||||||
|
// 反代IP列表 (直接IP,非CIDR)
|
||||||
|
response = await fetch('https://raw.githubusercontent.com/cmliu/ACL4SSR/main/baipiao.txt');
|
||||||
|
const text = response.ok ? await response.text() : '';
|
||||||
|
// 直接返回IP列表,不进行CIDR生成
|
||||||
|
const ips = text.split('\n')
|
||||||
|
.map(line => line.trim())
|
||||||
|
.filter(line => line && !line.startsWith('#'));
|
||||||
|
console.log(`反代IP列表获取到${ips.length}个IP`);
|
||||||
|
return ips;
|
||||||
|
} else {
|
||||||
|
// CF官方列表 (默认)
|
||||||
response = await fetch('https://www.cloudflare.com/ips-v4/');
|
response = await fetch('https://www.cloudflare.com/ips-v4/');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2772,12 +2808,13 @@ async function bestIP(request, env, txt = 'ADD.txt') {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const cfIPs = await GetCFIPs();
|
// 移除初始IP加载,改为在前端动态加载
|
||||||
|
const cfIPs = []; // 初始为空数组
|
||||||
|
|
||||||
// 判断是否为中国用户
|
// 判断是否为中国用户
|
||||||
const isChina = country === 'CN';
|
const isChina = country === 'CN';
|
||||||
const countryDisplayClass = isChina ? '' : 'proxy-warning';
|
const countryDisplayClass = isChina ? '' : 'proxy-warning';
|
||||||
const countryDisplayText = isChina ? country : `${country} ⚠️`;
|
const countryDisplayText = isChina ? `${country}` : `${country} ⚠️`;
|
||||||
|
|
||||||
const html = `
|
const html = `
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
@@ -2808,6 +2845,19 @@ async function bestIP(request, env, txt = 'ADD.txt') {
|
|||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
margin: 20px 0;
|
margin: 20px 0;
|
||||||
}
|
}
|
||||||
|
.test-info {
|
||||||
|
margin-top: 15px;
|
||||||
|
padding: 12px;
|
||||||
|
background-color: #f3e5f5;
|
||||||
|
border: 1px solid #ce93d8;
|
||||||
|
border-radius: 6px;
|
||||||
|
color: #4a148c;
|
||||||
|
}
|
||||||
|
.test-info p {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
.proxy-warning {
|
.proxy-warning {
|
||||||
color: #d32f2f !important;
|
color: #d32f2f !important;
|
||||||
font-weight: bold !important;
|
font-weight: bold !important;
|
||||||
@@ -2919,6 +2969,22 @@ async function bestIP(request, env, txt = 'ADD.txt') {
|
|||||||
.append-button:not(:disabled):hover {
|
.append-button:not(:disabled):hover {
|
||||||
background-color: #F57C00;
|
background-color: #F57C00;
|
||||||
}
|
}
|
||||||
|
.edit-button {
|
||||||
|
background-color: #9C27B0;
|
||||||
|
color: white;
|
||||||
|
padding: 15px 32px;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
}
|
||||||
|
.edit-button:hover {
|
||||||
|
background-color: #7B1FA2;
|
||||||
|
}
|
||||||
.back-button {
|
.back-button {
|
||||||
background-color: #607D8B;
|
background-color: #607D8B;
|
||||||
color: white;
|
color: white;
|
||||||
@@ -2986,16 +3052,6 @@ async function bestIP(request, env, txt = 'ADD.txt') {
|
|||||||
<body>
|
<body>
|
||||||
<h1>在线优选IP</h1>
|
<h1>在线优选IP</h1>
|
||||||
|
|
||||||
<div class="stats">
|
|
||||||
<h2>统计信息</h2>
|
|
||||||
<p><strong>您的国家:</strong><span class="${countryDisplayClass}">${countryDisplayText}</span></p>
|
|
||||||
<p><strong>获取到的IP总数:</strong>${cfIPs.length} 个</p>
|
|
||||||
<p><strong>测试进度:</strong><span id="progress-text">未开始</span></p>
|
|
||||||
<div class="progress">
|
|
||||||
<div class="progress-bar" id="progress-bar"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
${!isChina ? `
|
${!isChina ? `
|
||||||
<div class="warning-notice">
|
<div class="warning-notice">
|
||||||
<h3>🚨 代理检测警告</h3>
|
<h3>🚨 代理检测警告</h3>
|
||||||
@@ -3009,12 +3065,34 @@ async function bestIP(request, env, txt = 'ADD.txt') {
|
|||||||
<p><strong>建议操作:</strong>请关闭所有代理软件(VPN、科学上网工具等),确保处于直连网络环境后重新访问本页面。</p>
|
<p><strong>建议操作:</strong>请关闭所有代理软件(VPN、科学上网工具等),确保处于直连网络环境后重新访问本页面。</p>
|
||||||
</div>
|
</div>
|
||||||
` : ''}
|
` : ''}
|
||||||
|
|
||||||
|
<div class="stats">
|
||||||
|
<h2>统计信息</h2>
|
||||||
|
<p><strong>您的国家:</strong><span class="${countryDisplayClass}">${countryDisplayText}</span></p>
|
||||||
|
<p><strong>获取到的IP总数:</strong><span id="ip-count">点击开始测试后加载</span></p>
|
||||||
|
<p><strong>测试进度:</strong><span id="progress-text">未开始</span></p>
|
||||||
|
<div class="progress">
|
||||||
|
<div class="progress-bar" id="progress-bar"></div>
|
||||||
|
</div>
|
||||||
|
<div class="test-info">
|
||||||
|
<p><strong>📊 测试说明:</strong>当前优选方式仅进行网络延迟测试,主要评估连接响应速度,并未包含带宽速度测试。延迟测试可快速筛选出响应最快的IP节点,适合日常使用场景的初步优选。</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="test-controls">
|
<div class="test-controls">
|
||||||
<div class="port-selector">
|
<div class="port-selector">
|
||||||
<label for="port-select">选择端口:</label>
|
<label for="ip-source-select">IP库:</label>
|
||||||
|
<select id="ip-source-select">
|
||||||
|
<option value="official">CF官方列表</option>
|
||||||
|
<option value="cm">CM整理列表</option>
|
||||||
|
<option value="as13335">AS13335列表</option>
|
||||||
|
<option value="as209242">AS209242列表</option>
|
||||||
|
<option value="baipiao">反代IP列表</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<label for="port-select" style="margin-left: 20px;">端口:</label>
|
||||||
<select id="port-select">
|
<select id="port-select">
|
||||||
<option value="443" selected>443</option>
|
<option value="443">443</option>
|
||||||
<option value="2053">2053</option>
|
<option value="2053">2053</option>
|
||||||
<option value="2083">2083</option>
|
<option value="2083">2083</option>
|
||||||
<option value="2087">2087</option>
|
<option value="2087">2087</option>
|
||||||
@@ -3026,6 +3104,7 @@ async function bestIP(request, env, txt = 'ADD.txt') {
|
|||||||
<button class="test-button" id="test-btn" onclick="startTest()">开始延迟测试</button>
|
<button class="test-button" id="test-btn" onclick="startTest()">开始延迟测试</button>
|
||||||
<button class="save-button" id="save-btn" onclick="saveIPs()" disabled>覆盖保存优选IP</button>
|
<button class="save-button" id="save-btn" onclick="saveIPs()" disabled>覆盖保存优选IP</button>
|
||||||
<button class="append-button" id="append-btn" onclick="appendIPs()" disabled>追加保存优选IP</button>
|
<button class="append-button" id="append-btn" onclick="appendIPs()" disabled>追加保存优选IP</button>
|
||||||
|
<button class="edit-button" id="edit-btn" onclick="goEdit()">编辑优选列表</button>
|
||||||
<button class="back-button" id="back-btn" onclick="goBack()">返回配置页</button>
|
<button class="back-button" id="back-btn" onclick="goBack()">返回配置页</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="save-warning">
|
<div class="save-warning">
|
||||||
@@ -3036,14 +3115,56 @@ 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-list" id="ip-list">
|
<div class="ip-list" id="ip-list">
|
||||||
${cfIPs.map(ip => `<div class="ip-item">${ip}</div>`).join('')}
|
<div class="ip-item">请选择端口和IP库,然后点击"开始延迟测试"加载IP列表</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const originalIPs = ${JSON.stringify(cfIPs)};
|
let originalIPs = []; // 改为动态加载
|
||||||
let testResults = [];
|
let testResults = [];
|
||||||
let displayedResults = []; // 新增:存储当前显示的结果
|
let displayedResults = []; // 新增:存储当前显示的结果
|
||||||
|
|
||||||
|
// 新增:本地存储管理
|
||||||
|
const StorageKeys = {
|
||||||
|
PORT: 'cf-ip-test-port',
|
||||||
|
IP_SOURCE: 'cf-ip-test-source'
|
||||||
|
};
|
||||||
|
|
||||||
|
// 初始化页面设置
|
||||||
|
function initializeSettings() {
|
||||||
|
const portSelect = document.getElementById('port-select');
|
||||||
|
const ipSourceSelect = document.getElementById('ip-source-select');
|
||||||
|
|
||||||
|
// 从本地存储读取上次的选择
|
||||||
|
const savedPort = localStorage.getItem(StorageKeys.PORT);
|
||||||
|
const savedIPSource = localStorage.getItem(StorageKeys.IP_SOURCE);
|
||||||
|
|
||||||
|
// 恢复端口选择
|
||||||
|
if (savedPort && portSelect.querySelector(\`option[value="\${savedPort}"]\`)) {
|
||||||
|
portSelect.value = savedPort;
|
||||||
|
} else {
|
||||||
|
portSelect.value = '8443'; // 默认值
|
||||||
|
}
|
||||||
|
|
||||||
|
// 恢复IP库选择
|
||||||
|
if (savedIPSource && ipSourceSelect.querySelector(\`option[value="\${savedIPSource}"]\`)) {
|
||||||
|
ipSourceSelect.value = savedIPSource;
|
||||||
|
} else {
|
||||||
|
ipSourceSelect.value = 'official'; // 默认值改为CF官方列表
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加事件监听器保存选择
|
||||||
|
portSelect.addEventListener('change', function() {
|
||||||
|
localStorage.setItem(StorageKeys.PORT, this.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
ipSourceSelect.addEventListener('change', function() {
|
||||||
|
localStorage.setItem(StorageKeys.IP_SOURCE, this.value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 页面加载完成后初始化设置
|
||||||
|
document.addEventListener('DOMContentLoaded', initializeSettings);
|
||||||
|
|
||||||
function showMessage(text, type = 'success') {
|
function showMessage(text, type = 'success') {
|
||||||
const messageDiv = document.getElementById('message');
|
const messageDiv = document.getElementById('message');
|
||||||
messageDiv.textContent = text;
|
messageDiv.textContent = text;
|
||||||
@@ -3069,24 +3190,32 @@ async function bestIP(request, env, txt = 'ADD.txt') {
|
|||||||
const testBtn = document.getElementById('test-btn');
|
const testBtn = document.getElementById('test-btn');
|
||||||
const saveBtn = document.getElementById('save-btn');
|
const saveBtn = document.getElementById('save-btn');
|
||||||
const appendBtn = document.getElementById('append-btn');
|
const appendBtn = document.getElementById('append-btn');
|
||||||
|
const editBtn = document.getElementById('edit-btn');
|
||||||
const backBtn = document.getElementById('back-btn');
|
const backBtn = document.getElementById('back-btn');
|
||||||
const portSelect = document.getElementById('port-select');
|
const portSelect = document.getElementById('port-select');
|
||||||
|
const ipSourceSelect = document.getElementById('ip-source-select');
|
||||||
|
|
||||||
testBtn.disabled = true;
|
testBtn.disabled = true;
|
||||||
saveBtn.disabled = true;
|
saveBtn.disabled = true;
|
||||||
appendBtn.disabled = true;
|
appendBtn.disabled = true;
|
||||||
|
editBtn.disabled = true;
|
||||||
backBtn.disabled = true;
|
backBtn.disabled = true;
|
||||||
portSelect.disabled = true;
|
portSelect.disabled = true;
|
||||||
|
ipSourceSelect.disabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function enableButtons() {
|
function enableButtons() {
|
||||||
const testBtn = document.getElementById('test-btn');
|
const testBtn = document.getElementById('test-btn');
|
||||||
|
const editBtn = document.getElementById('edit-btn');
|
||||||
const backBtn = document.getElementById('back-btn');
|
const backBtn = document.getElementById('back-btn');
|
||||||
const portSelect = document.getElementById('port-select');
|
const portSelect = document.getElementById('port-select');
|
||||||
|
const ipSourceSelect = document.getElementById('ip-source-select');
|
||||||
|
|
||||||
testBtn.disabled = false;
|
testBtn.disabled = false;
|
||||||
|
editBtn.disabled = false;
|
||||||
backBtn.disabled = false;
|
backBtn.disabled = false;
|
||||||
portSelect.disabled = false;
|
portSelect.disabled = false;
|
||||||
|
ipSourceSelect.disabled = false;
|
||||||
updateButtonStates();
|
updateButtonStates();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3170,6 +3299,12 @@ async function bestIP(request, env, txt = 'ADD.txt') {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function goEdit() {
|
||||||
|
const currentUrl = window.location.href;
|
||||||
|
const parentUrl = currentUrl.substring(0, currentUrl.lastIndexOf('/'));
|
||||||
|
window.location.href = parentUrl + '/edit';
|
||||||
|
}
|
||||||
|
|
||||||
function goBack() {
|
function goBack() {
|
||||||
const currentUrl = window.location.href;
|
const currentUrl = window.location.href;
|
||||||
const parentUrl = currentUrl.substring(0, currentUrl.lastIndexOf('/'));
|
const parentUrl = currentUrl.substring(0, currentUrl.lastIndexOf('/'));
|
||||||
@@ -3179,24 +3314,28 @@ async function bestIP(request, env, txt = 'ADD.txt') {
|
|||||||
async function testIP(ip, port) {
|
async function testIP(ip, port) {
|
||||||
const timeout = 999;
|
const timeout = 999;
|
||||||
|
|
||||||
|
// 解析IP格式
|
||||||
|
const parsedIP = parseIPFormat(ip, port);
|
||||||
|
if (!parsedIP) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// 第一次测试
|
// 第一次测试
|
||||||
const firstResult = await singleTest(ip, port, timeout);
|
const firstResult = await singleTest(parsedIP.host, parsedIP.port, timeout);
|
||||||
if (!firstResult) {
|
if (!firstResult) {
|
||||||
return null; // 第一次测试失败,直接返回
|
return null; // 第一次测试失败,直接返回
|
||||||
}
|
}
|
||||||
|
|
||||||
// 第一次测试成功,再进行2次测试
|
// 第一次测试成功,再进行第二次测试
|
||||||
console.log(\`IP \${ip}:\${port} 第一次测试成功: \${firstResult.latency}ms,进行额外测试...\`);
|
console.log(\`IP \${parsedIP.host}:\${parsedIP.port} 第一次测试成功: \${firstResult.latency}ms,进行第二次测试...\`);
|
||||||
|
|
||||||
const results = [firstResult];
|
const results = [firstResult];
|
||||||
|
|
||||||
// 进行第二次和第三次测试
|
// 进行第二次测试
|
||||||
for (let i = 2; i <= 3; i++) {
|
const secondResult = await singleTest(parsedIP.host, parsedIP.port, timeout);
|
||||||
const result = await singleTest(ip, port, timeout);
|
if (secondResult) {
|
||||||
if (result) {
|
results.push(secondResult);
|
||||||
results.push(result);
|
console.log(\`IP \${parsedIP.host}:\${parsedIP.port} 第二次测试: \${secondResult.latency}ms\`);
|
||||||
console.log(\`IP \${ip}:\${port} 第\${i}次测试: \${result.latency}ms\`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 取最低延迟
|
// 取最低延迟
|
||||||
@@ -3204,21 +3343,64 @@ async function bestIP(request, env, txt = 'ADD.txt') {
|
|||||||
current.latency < best.latency ? current : best
|
current.latency < best.latency ? current : best
|
||||||
);
|
);
|
||||||
|
|
||||||
// 将延迟除以2并向下取整(因为是往返时间)
|
const displayLatency = Math.floor(bestResult.latency);
|
||||||
const displayLatency = Math.floor(bestResult.latency / 2);
|
|
||||||
|
|
||||||
console.log(\`IP \${ip}:\${port} 最终结果: \${displayLatency}ms (原始: \${bestResult.latency}ms, 共\${results.length}次有效测试)\`);
|
console.log(\`IP \${parsedIP.host}:\${parsedIP.port} 最终结果: \${displayLatency}ms (原始: \${bestResult.latency}ms, 共\${results.length}次有效测试)\`);
|
||||||
|
|
||||||
|
// 生成显示格式
|
||||||
|
const comment = parsedIP.comment || 'CF优选IP';
|
||||||
|
const display = \`\${parsedIP.host}:\${parsedIP.port}#\${comment} \${displayLatency}ms\`;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
ip: ip,
|
ip: parsedIP.host,
|
||||||
port: port,
|
port: parsedIP.port,
|
||||||
latency: displayLatency,
|
latency: displayLatency,
|
||||||
originalLatency: bestResult.latency,
|
originalLatency: bestResult.latency,
|
||||||
testCount: results.length,
|
testCount: results.length,
|
||||||
display: \`\${ip}:\${port}#CF优选IP \${displayLatency}ms\`
|
comment: comment,
|
||||||
|
display: display
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 新增:解析IP格式的函数
|
||||||
|
function parseIPFormat(ipString, defaultPort) {
|
||||||
|
try {
|
||||||
|
let host, port, comment;
|
||||||
|
|
||||||
|
// 先处理注释部分(#之后的内容)
|
||||||
|
let mainPart = ipString;
|
||||||
|
if (ipString.includes('#')) {
|
||||||
|
const parts = ipString.split('#');
|
||||||
|
mainPart = parts[0];
|
||||||
|
comment = parts[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理端口部分
|
||||||
|
if (mainPart.includes(':')) {
|
||||||
|
const parts = mainPart.split(':');
|
||||||
|
host = parts[0];
|
||||||
|
port = parseInt(parts[1]);
|
||||||
|
} else {
|
||||||
|
host = mainPart;
|
||||||
|
port = parseInt(defaultPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证IP格式
|
||||||
|
if (!host || !port || isNaN(port)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
host: host.trim(),
|
||||||
|
port: port,
|
||||||
|
comment: comment ? comment.trim() : null
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error('解析IP格式失败:', ipString, error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function singleTest(ip, port, timeout) {
|
async function singleTest(ip, port, timeout) {
|
||||||
const startTime = Date.now();
|
const startTime = Date.now();
|
||||||
|
|
||||||
@@ -3299,26 +3481,81 @@ async function bestIP(request, env, txt = 'ADD.txt') {
|
|||||||
async function startTest() {
|
async function startTest() {
|
||||||
const testBtn = document.getElementById('test-btn');
|
const testBtn = document.getElementById('test-btn');
|
||||||
const portSelect = document.getElementById('port-select');
|
const portSelect = document.getElementById('port-select');
|
||||||
|
const ipSourceSelect = document.getElementById('ip-source-select');
|
||||||
const progressBar = document.getElementById('progress-bar');
|
const progressBar = document.getElementById('progress-bar');
|
||||||
const progressText = document.getElementById('progress-text');
|
const progressText = document.getElementById('progress-text');
|
||||||
const ipList = document.getElementById('ip-list');
|
const ipList = document.getElementById('ip-list');
|
||||||
const resultCount = document.getElementById('result-count');
|
const resultCount = document.getElementById('result-count');
|
||||||
|
const ipCount = document.getElementById('ip-count');
|
||||||
|
|
||||||
const selectedPort = portSelect.value;
|
const selectedPort = portSelect.value;
|
||||||
|
const selectedIPSource = ipSourceSelect.value;
|
||||||
|
|
||||||
|
// 保存当前选择到本地存储
|
||||||
|
localStorage.setItem(StorageKeys.PORT, selectedPort);
|
||||||
|
localStorage.setItem(StorageKeys.IP_SOURCE, selectedIPSource);
|
||||||
|
|
||||||
testBtn.disabled = true;
|
testBtn.disabled = true;
|
||||||
testBtn.textContent = '测试中...';
|
testBtn.textContent = '加载IP列表...';
|
||||||
portSelect.disabled = true;
|
portSelect.disabled = true;
|
||||||
|
ipSourceSelect.disabled = true;
|
||||||
testResults = [];
|
testResults = [];
|
||||||
displayedResults = []; // 重置显示结果
|
displayedResults = []; // 重置显示结果
|
||||||
ipList.innerHTML = '<div class="ip-item">测试中,请稍候...</div>';
|
ipList.innerHTML = '<div class="ip-item">正在加载IP列表,请稍候...</div>';
|
||||||
updateButtonStates(); // 更新按钮状态
|
updateButtonStates(); // 更新按钮状态
|
||||||
|
|
||||||
// 重置进度条
|
// 重置进度条
|
||||||
progressBar.style.width = '0%';
|
progressBar.style.width = '0%';
|
||||||
|
|
||||||
|
// 根据IP库类型显示对应的加载信息
|
||||||
|
let ipSourceName = '';
|
||||||
|
switch(selectedIPSource) {
|
||||||
|
case 'official':
|
||||||
|
ipSourceName = 'CF官方';
|
||||||
|
break;
|
||||||
|
case 'cm':
|
||||||
|
ipSourceName = 'CM整理';
|
||||||
|
break;
|
||||||
|
case 'as13335':
|
||||||
|
ipSourceName = 'AS13335';
|
||||||
|
break;
|
||||||
|
case 'as209242':
|
||||||
|
ipSourceName = 'AS209242';
|
||||||
|
break;
|
||||||
|
case 'baipiao':
|
||||||
|
ipSourceName = '反代IP';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ipSourceName = '未知';
|
||||||
|
}
|
||||||
|
|
||||||
|
progressText.textContent = \`正在加载 \${ipSourceName} IP列表...\`;
|
||||||
|
|
||||||
|
// 加载IP列表
|
||||||
|
originalIPs = await loadIPs(selectedIPSource);
|
||||||
|
|
||||||
|
if (originalIPs.length === 0) {
|
||||||
|
ipList.innerHTML = '<div class="ip-item">加载IP列表失败,请重试</div>';
|
||||||
|
ipCount.textContent = '0 个';
|
||||||
|
testBtn.disabled = false;
|
||||||
|
testBtn.textContent = '开始延迟测试';
|
||||||
|
portSelect.disabled = false;
|
||||||
|
ipSourceSelect.disabled = false;
|
||||||
|
progressText.textContent = '加载失败';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新IP数量显示
|
||||||
|
ipCount.textContent = \`\${originalIPs.length} 个\`;
|
||||||
|
|
||||||
|
// 显示加载的IP列表
|
||||||
|
ipList.innerHTML = originalIPs.map(ip => \`<div class="ip-item">\${ip}</div>\`).join('');
|
||||||
|
|
||||||
|
// 开始测试
|
||||||
|
testBtn.textContent = '测试中...';
|
||||||
progressText.textContent = \`开始测试端口 \${selectedPort}...\`;
|
progressText.textContent = \`开始测试端口 \${selectedPort}...\`;
|
||||||
|
|
||||||
// 使用32个并发线程测试
|
// 使用16个并发线程测试
|
||||||
const results = await testIPsWithConcurrency(originalIPs, selectedPort, 16);
|
const results = await testIPsWithConcurrency(originalIPs, selectedPort, 16);
|
||||||
|
|
||||||
// 按延迟排序
|
// 按延迟排序
|
||||||
@@ -3330,7 +3567,27 @@ async function bestIP(request, env, txt = 'ADD.txt') {
|
|||||||
testBtn.disabled = false;
|
testBtn.disabled = false;
|
||||||
testBtn.textContent = '重新测试';
|
testBtn.textContent = '重新测试';
|
||||||
portSelect.disabled = false;
|
portSelect.disabled = false;
|
||||||
progressText.textContent = \`完成 - 有效IP: \${testResults.length}/\${originalIPs.length} (端口: \${selectedPort})\`;
|
ipSourceSelect.disabled = false;
|
||||||
|
progressText.textContent = \`完成 - 有效IP: \${testResults.length}/\${originalIPs.length} (端口: \${selectedPort}, IP库: \${ipSourceName})\`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增:加载IP列表的函数
|
||||||
|
async function loadIPs(ipSource) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(\`/?loadIPs=\${ipSource}\`, {
|
||||||
|
method: 'GET'
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Failed to load IPs');
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
return data.ips || [];
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载IP列表失败:', error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function displayResults() {
|
function displayResults() {
|
||||||
@@ -3368,6 +3625,18 @@ async function bestIP(request, env, txt = 'ADD.txt') {
|
|||||||
</html>
|
</html>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
// 处理加载IP的请求
|
||||||
|
if (url.searchParams.get('loadIPs')) {
|
||||||
|
const ipSource = url.searchParams.get('loadIPs');
|
||||||
|
const ips = await GetCFIPs(ipSource);
|
||||||
|
|
||||||
|
return new Response(JSON.stringify({ ips }), {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return new Response(html, {
|
return new Response(html, {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'text/html; charset=UTF-8',
|
'Content-Type': 'text/html; charset=UTF-8',
|
||||||
|
|||||||
Reference in New Issue
Block a user