mirror of
https://github.com/dqzboy/Docker-Proxy.git
synced 2026-06-30 13:15:50 +08:00
feat: Add network testing functionality.
This commit is contained in:
@@ -20,7 +20,7 @@
|
||||
[](https://github.com/dqzboy/Docker-Proxy/blob/main/LICENSE)
|
||||
|
||||
|
||||
📢 <a href="https://t.me/+ghs_XDp1vwxkMGU9" style="font-size: 15px;">Docker Proxy-TG交流群</a>
|
||||
📢 <a href="https://t.me/+ghs_XDp1vwxkMGU9" style="font-size: 15px;">Docker Proxy-交流群</a>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"express": "^4.19.2",
|
||||
"express-session": "^1.18.0",
|
||||
"morgan": "^1.10.0",
|
||||
"validator": "^13.12.0",
|
||||
"ws": "^8.18.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ const app = express();
|
||||
const cors = require('cors');
|
||||
const WebSocket = require('ws');
|
||||
const http = require('http');
|
||||
const { exec } = require('child_process'); // 网络测试
|
||||
const validator = require('validator');
|
||||
|
||||
let docker = null;
|
||||
|
||||
@@ -646,6 +648,38 @@ app.post('/api/docker/delete/:id', requireLogin, async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// 网络测试
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
// 在应用启动时执行
|
||||
const pingPath = execSync('which ping').toString().trim();
|
||||
const traceroutePath = execSync('which traceroute').toString().trim();
|
||||
|
||||
app.post('/api/network-test', requireLogin, (req, res) => {
|
||||
const { domain, testType } = req.body;
|
||||
|
||||
let command;
|
||||
switch (testType) {
|
||||
case 'ping':
|
||||
command = `${pingPath} -c 4 ${domain}`;
|
||||
break;
|
||||
case 'traceroute':
|
||||
command = `${traceroutePath} -m 10 ${domain}`;
|
||||
break;
|
||||
default:
|
||||
return res.status(400).send('无效的测试类型');
|
||||
}
|
||||
|
||||
exec(command, { timeout: 30000 }, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
console.error(`执行出错: ${error}`);
|
||||
return res.status(500).send('测试执行失败');
|
||||
}
|
||||
res.send(stdout || stderr);
|
||||
});
|
||||
});
|
||||
|
||||
// 启动服务器
|
||||
const PORT = process.env.PORT || 3000;
|
||||
server.listen(PORT, () => {
|
||||
|
||||
@@ -470,6 +470,26 @@
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
#network-test .input-group {
|
||||
margin-bottom: 15px;
|
||||
max-width: 400px;
|
||||
}
|
||||
#network-test label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
#network-test input[type="text"],
|
||||
#network-test select {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
border: 1px solid #d1d5da;
|
||||
border-radius: 4px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
#network-test button {
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -486,6 +506,7 @@
|
||||
<li data-section="ad-management">广告管理</li>
|
||||
<li data-section="documentation-management">文档管理</li>
|
||||
<li data-section="password-change">修改密码</li>
|
||||
<li data-section="network-test">网络测试</li>
|
||||
<li data-section="docker-status">Docker 服务状态</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -574,6 +595,35 @@
|
||||
<button type="button" onclick="changePassword()">修改密码</button>
|
||||
</div>
|
||||
|
||||
<!-- 网络测试 -->
|
||||
<div id="network-test" class="content-section">
|
||||
<h1 class="admin-title">网络测试</h1>
|
||||
<div class="input-group">
|
||||
<label for="testDomain">目标域名或IP地址:</label>
|
||||
<input type="text" id="testDomain" placeholder="例如: www.example.com 或 8.8.8.8">
|
||||
<select id="domainSelect">
|
||||
<option value="">选择预定义域名</option>
|
||||
<option value="gcr.io">gcr.io</option>
|
||||
<option value="ghcr.io">ghcr.io</option>
|
||||
<option value="quay.io">quay.io</option>
|
||||
<option value="k8s.gcr.io">k8s.gcr.io</option>
|
||||
<option value="registry.k8s.io">registry.k8s.io</option>
|
||||
<option value="mcr.microsoft.com">mcr.microsoft.com</option>
|
||||
<option value="docker.elastic.com">docker.elastic.com</option>
|
||||
<option value="registry-1.docker.io">registry-1.docker.io</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label for="testType">测试类型:</label>
|
||||
<select id="testType">
|
||||
<option value="ping">Ping</option>
|
||||
<option value="traceroute">Traceroute</option>
|
||||
</select>
|
||||
</div>
|
||||
<button>开始测试</button>
|
||||
<div id="testResults" style="margin-top: 20px; white-space: pre-wrap; font-family: monospace;"></div>
|
||||
</div>
|
||||
|
||||
<!-- Docker服务状态 -->
|
||||
<div id="docker-status" class="content-section">
|
||||
<h1 class="admin-title">Docker 服务状态</h1>
|
||||
@@ -1708,10 +1758,97 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const sidebarItems = document.querySelectorAll('.sidebar li');
|
||||
const contentSections = document.querySelectorAll('.content-section');
|
||||
|
||||
const testDomainInput = document.getElementById('testDomain');
|
||||
|
||||
const domainSelect = document.getElementById('domainSelect');
|
||||
|
||||
// 当选择预定义域名时,更新输入框
|
||||
domainSelect.addEventListener('change', function() {
|
||||
if (this.value) {
|
||||
testDomainInput.value = this.value;
|
||||
}
|
||||
});
|
||||
|
||||
// 当输入框获得焦点时,清空选择框
|
||||
testDomainInput.addEventListener('focus', function() {
|
||||
domainSelect.value = '';
|
||||
});
|
||||
|
||||
// 网络测试函数
|
||||
function runNetworkTest() {
|
||||
const domain = testDomainInput.value.trim();
|
||||
const testType = document.getElementById('testType').value;
|
||||
const resultsDiv = document.getElementById('testResults');
|
||||
|
||||
// 验证输入
|
||||
if (!domain) {
|
||||
alert('请输入目标域名或IP地址');
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证域名格式
|
||||
const domainRegex = /^(?!:\/\/)(?=.{1,255}$)((.{1,63}\.){1,127}(?![0-9]*$)[a-z0-9-]+\.?)$/i;
|
||||
// 验证IPv4格式
|
||||
const ipv4Regex = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
|
||||
// 验证IPv6格式
|
||||
const ipv6Regex = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/;
|
||||
|
||||
// 检查是否为预定义的选项之一
|
||||
const predefinedOptions = [
|
||||
'gcr.io', 'ghcr.io', 'quay.io', 'k8s.gcr.io', 'registry.k8s.io',
|
||||
'mcr.microsoft.com', 'docker.elastic.com', 'registry-1.docker.io'
|
||||
];
|
||||
|
||||
if (!predefinedOptions.includes(domain) &&
|
||||
!domainRegex.test(domain) &&
|
||||
!ipv4Regex.test(domain) &&
|
||||
!ipv6Regex.test(domain)) {
|
||||
alert('请输入有效的域名或IP地址');
|
||||
return;
|
||||
}
|
||||
|
||||
resultsDiv.innerHTML = '测试中,请稍候...';
|
||||
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), 60000); // 60秒超时
|
||||
|
||||
fetch('/api/network-test', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ domain, testType }),
|
||||
signal: controller.signal
|
||||
})
|
||||
.then(response => {
|
||||
clearTimeout(timeoutId);
|
||||
if (!response.ok) {
|
||||
throw new Error('网络测试失败');
|
||||
}
|
||||
return response.text();
|
||||
})
|
||||
.then(result => {
|
||||
resultsDiv.textContent = result;
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('网络测试出错:', error);
|
||||
if (error.name === 'AbortError') {
|
||||
resultsDiv.textContent = '测试超时,请稍后再试';
|
||||
} else {
|
||||
resultsDiv.textContent = '测试失败: ' + error.message;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 绑定测试按钮点击事件
|
||||
document.querySelector('#network-test button').addEventListener('click', runNetworkTest);
|
||||
|
||||
|
||||
let isDockerStatusLoaded = false;
|
||||
|
||||
function showSection(sectionId) {
|
||||
|
||||
Reference in New Issue
Block a user