| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 | <!DOCTYPE html><html lang="zh-CN"><head>    <meta charset="UTF-8">    <meta name="viewport" content="width=device-width, initial-scale=1.0">    <title>{{.title}}</title>    <style>        body {            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;            margin: 0;            padding: 20px;            background-color: #f5f5f5;        }        .container {            max-width: 1200px;            margin: 0 auto;            background: white;            border-radius: 8px;            box-shadow: 0 2px 10px rgba(0,0,0,0.1);            overflow: hidden;        }        .header {            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);            color: white;            padding: 20px;            text-align: center;        }        .content {            padding: 20px;        }        .stats {            display: grid;            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));            gap: 20px;            margin-bottom: 30px;        }        .stat-card {            background: #f8f9fa;            padding: 20px;            border-radius: 8px;            text-align: center;            border-left: 4px solid #667eea;        }        .stat-number {            font-size: 2em;            font-weight: bold;            color: #667eea;        }        .stat-label {            color: #666;            margin-top: 5px;        }        .section {            margin-bottom: 30px;        }        .section h3 {            color: #333;            border-bottom: 2px solid #667eea;            padding-bottom: 10px;        }        .btn {            background: #667eea;            color: white;            border: none;            padding: 10px 20px;            border-radius: 5px;            cursor: pointer;            font-size: 14px;        }        .btn:hover {            background: #5a6fd8;        }        .table {            width: 100%;            border-collapse: collapse;            margin-top: 10px;        }        .table th, .table td {            padding: 12px;            text-align: left;            border-bottom: 1px solid #ddd;        }        .table th {            background-color: #f8f9fa;            font-weight: 600;        }        .status-success {            color: #28a745;        }        .status-error {            color: #dc3545;        }    </style></head><body>    <div class="container">        <div class="header">            <h1>{{.title}}</h1>            <p>代理节点测速管理系统</p>        </div>                <div class="content">            <div class="stats">                <div class="stat-card">                    <div class="stat-number" id="totalNodes">-</div>                    <div class="stat-label">总节点数</div>                </div>                <div class="stat-card">                    <div class="stat-number" id="activeNodes">-</div>                    <div class="stat-label">活跃节点</div>                </div>                <div class="stat-card">                    <div class="stat-number" id="avgLatency">-</div>                    <div class="stat-label">平均延迟(ms)</div>                </div>                <div class="stat-card">                    <div class="stat-number" id="successRate">-</div>                    <div class="stat-label">成功率(%)</div>                </div>            </div>            <div class="section">                <h3>系统状态</h3>                <button class="btn" onclick="triggerSpeedTest()">立即测速</button>                <button class="btn" onclick="refreshData()">刷新数据</button>                <div id="systemStatus">加载中...</div>            </div>            <div class="section">                <h3>最近测试结果</h3>                <div id="recentResults">加载中...</div>            </div>        </div>    </div>    <script>        // 页面加载时获取数据        document.addEventListener('DOMContentLoaded', function() {            refreshData();        });        // 刷新数据        function refreshData() {            fetchSystemStatus();            fetchRecentResults();        }        // 获取系统状态        function fetchSystemStatus() {            fetch('/api/status')                .then(response => response.json())                .then(data => {                    document.getElementById('totalNodes').textContent = data.total_nodes || 0;                    document.getElementById('activeNodes').textContent = data.active_nodes || 0;                    document.getElementById('avgLatency').textContent = data.avg_latency || '-';                    document.getElementById('successRate').textContent = data.success_rate || '-';                                        const statusDiv = document.getElementById('systemStatus');                    statusDiv.innerHTML = `                        <p><strong>调度器状态:</strong> ${data.scheduler_running ? '运行中' : '已停止'}</p>                        <p><strong>最后更新:</strong> ${new Date().toLocaleString()}</p>                    `;                })                .catch(error => {                    console.error('获取系统状态失败:', error);                    document.getElementById('systemStatus').innerHTML = '<p class="status-error">获取系统状态失败</p>';                });        }        // 获取最近测试结果        function fetchRecentResults() {            fetch('/api/speed-test?limit=10')                .then(response => response.json())                .then(data => {                    const resultsDiv = document.getElementById('recentResults');                    if (data.data && data.data.length > 0) {                        let html = '<table class="table">';                        html += '<thead><tr><th>节点名称</th><th>延迟(ms)</th><th>IP地址</th><th>位置</th><th>状态</th><th>测试时间</th></tr></thead>';                        html += '<tbody>';                                                data.data.forEach(result => {                            const status = result.is_success ?                                 '<span class="status-success">成功</span>' :                                 '<span class="status-error">失败</span>';                                                        html += `<tr>                                <td>${result.node ? result.node.name : '未知'}</td>                                <td>${result.latency || '-'}</td>                                <td>${result.ip_address || '-'}</td>                                <td>${result.location || '-'}</td>                                <td>${status}</td>                                <td>${new Date(result.test_time).toLocaleString()}</td>                            </tr>`;                        });                                                html += '</tbody></table>';                        resultsDiv.innerHTML = html;                    } else {                        resultsDiv.innerHTML = '<p>暂无测试结果</p>';                    }                })                .catch(error => {                    console.error('获取测试结果失败:', error);                    document.getElementById('recentResults').innerHTML = '<p class="status-error">获取测试结果失败</p>';                });        }        // 触发测速        function triggerSpeedTest() {            fetch('/api/speed-test/trigger', {                method: 'POST'            })            .then(response => response.json())            .then(data => {                alert('测速任务已触发');                setTimeout(refreshData, 2000); // 2秒后刷新数据            })            .catch(error => {                console.error('触发测速失败:', error);                alert('触发测速失败');            });        }    </script></body></html> 
 |