feat: Add network testing functionality.

This commit is contained in:
dqzboy
2024-08-30 17:07:02 +08:00
parent a81d90ba31
commit f51655df38
4 changed files with 173 additions and 1 deletions
+1 -1
View File
@@ -20,7 +20,7 @@
[![GitHub license](https://img.shields.io/github/license/dqzboy/Docker-Proxy)](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>
+1
View File
@@ -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"
}
}
+34
View File
@@ -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, () => {
+137
View File
@@ -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) {