index.html 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>{{.title}}</title>
  7. <style>
  8. body {
  9. font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
  10. margin: 0;
  11. padding: 20px;
  12. background-color: #f5f5f5;
  13. }
  14. .container {
  15. max-width: 1200px;
  16. margin: 0 auto;
  17. background: white;
  18. border-radius: 8px;
  19. box-shadow: 0 2px 10px rgba(0,0,0,0.1);
  20. overflow: hidden;
  21. }
  22. .header {
  23. background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  24. color: white;
  25. padding: 20px;
  26. text-align: center;
  27. }
  28. .content {
  29. padding: 20px;
  30. }
  31. .stats {
  32. display: grid;
  33. grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  34. gap: 20px;
  35. margin-bottom: 30px;
  36. }
  37. .stat-card {
  38. background: #f8f9fa;
  39. padding: 20px;
  40. border-radius: 8px;
  41. text-align: center;
  42. border-left: 4px solid #667eea;
  43. }
  44. .stat-number {
  45. font-size: 2em;
  46. font-weight: bold;
  47. color: #667eea;
  48. }
  49. .stat-label {
  50. color: #666;
  51. margin-top: 5px;
  52. }
  53. .section {
  54. margin-bottom: 30px;
  55. }
  56. .section h3 {
  57. color: #333;
  58. border-bottom: 2px solid #667eea;
  59. padding-bottom: 10px;
  60. }
  61. .btn {
  62. background: #667eea;
  63. color: white;
  64. border: none;
  65. padding: 10px 20px;
  66. border-radius: 5px;
  67. cursor: pointer;
  68. font-size: 14px;
  69. }
  70. .btn:hover {
  71. background: #5a6fd8;
  72. }
  73. .table {
  74. width: 100%;
  75. border-collapse: collapse;
  76. margin-top: 10px;
  77. }
  78. .table th, .table td {
  79. padding: 12px;
  80. text-align: left;
  81. border-bottom: 1px solid #ddd;
  82. }
  83. .table th {
  84. background-color: #f8f9fa;
  85. font-weight: 600;
  86. }
  87. .status-success {
  88. color: #28a745;
  89. }
  90. .status-error {
  91. color: #dc3545;
  92. }
  93. </style>
  94. </head>
  95. <body>
  96. <div class="container">
  97. <div class="header">
  98. <h1>{{.title}}</h1>
  99. <p>代理节点测速管理系统</p>
  100. </div>
  101. <div class="content">
  102. <div class="stats">
  103. <div class="stat-card">
  104. <div class="stat-number" id="totalNodes">-</div>
  105. <div class="stat-label">总节点数</div>
  106. </div>
  107. <div class="stat-card">
  108. <div class="stat-number" id="activeNodes">-</div>
  109. <div class="stat-label">活跃节点</div>
  110. </div>
  111. <div class="stat-card">
  112. <div class="stat-number" id="avgLatency">-</div>
  113. <div class="stat-label">平均延迟(ms)</div>
  114. </div>
  115. <div class="stat-card">
  116. <div class="stat-number" id="successRate">-</div>
  117. <div class="stat-label">成功率(%)</div>
  118. </div>
  119. </div>
  120. <div class="section">
  121. <h3>系统状态</h3>
  122. <button class="btn" onclick="triggerSpeedTest()">立即测速</button>
  123. <button class="btn" onclick="refreshData()">刷新数据</button>
  124. <div id="systemStatus">加载中...</div>
  125. </div>
  126. <div class="section">
  127. <h3>最近测试结果</h3>
  128. <div id="recentResults">加载中...</div>
  129. </div>
  130. </div>
  131. </div>
  132. <script>
  133. // 页面加载时获取数据
  134. document.addEventListener('DOMContentLoaded', function() {
  135. refreshData();
  136. });
  137. // 刷新数据
  138. function refreshData() {
  139. fetchSystemStatus();
  140. fetchRecentResults();
  141. }
  142. // 获取系统状态
  143. function fetchSystemStatus() {
  144. fetch('/api/status')
  145. .then(response => response.json())
  146. .then(data => {
  147. document.getElementById('totalNodes').textContent = data.total_nodes || 0;
  148. document.getElementById('activeNodes').textContent = data.active_nodes || 0;
  149. document.getElementById('avgLatency').textContent = data.avg_latency || '-';
  150. document.getElementById('successRate').textContent = data.success_rate || '-';
  151. const statusDiv = document.getElementById('systemStatus');
  152. statusDiv.innerHTML = `
  153. <p><strong>调度器状态:</strong> ${data.scheduler_running ? '运行中' : '已停止'}</p>
  154. <p><strong>最后更新:</strong> ${new Date().toLocaleString()}</p>
  155. `;
  156. })
  157. .catch(error => {
  158. console.error('获取系统状态失败:', error);
  159. document.getElementById('systemStatus').innerHTML = '<p class="status-error">获取系统状态失败</p>';
  160. });
  161. }
  162. // 获取最近测试结果
  163. function fetchRecentResults() {
  164. fetch('/api/speed-test?limit=10')
  165. .then(response => response.json())
  166. .then(data => {
  167. const resultsDiv = document.getElementById('recentResults');
  168. if (data.data && data.data.length > 0) {
  169. let html = '<table class="table">';
  170. html += '<thead><tr><th>节点名称</th><th>延迟(ms)</th><th>IP地址</th><th>位置</th><th>状态</th><th>测试时间</th></tr></thead>';
  171. html += '<tbody>';
  172. data.data.forEach(result => {
  173. const status = result.is_success ?
  174. '<span class="status-success">成功</span>' :
  175. '<span class="status-error">失败</span>';
  176. html += `<tr>
  177. <td>${result.node ? result.node.name : '未知'}</td>
  178. <td>${result.latency || '-'}</td>
  179. <td>${result.ip_address || '-'}</td>
  180. <td>${result.location || '-'}</td>
  181. <td>${status}</td>
  182. <td>${new Date(result.test_time).toLocaleString()}</td>
  183. </tr>`;
  184. });
  185. html += '</tbody></table>';
  186. resultsDiv.innerHTML = html;
  187. } else {
  188. resultsDiv.innerHTML = '<p>暂无测试结果</p>';
  189. }
  190. })
  191. .catch(error => {
  192. console.error('获取测试结果失败:', error);
  193. document.getElementById('recentResults').innerHTML = '<p class="status-error">获取测试结果失败</p>';
  194. });
  195. }
  196. // 触发测速
  197. function triggerSpeedTest() {
  198. fetch('/api/speed-test/trigger', {
  199. method: 'POST'
  200. })
  201. .then(response => response.json())
  202. .then(data => {
  203. alert('测速任务已触发');
  204. setTimeout(refreshData, 2000); // 2秒后刷新数据
  205. })
  206. .catch(error => {
  207. console.error('触发测速失败:', error);
  208. alert('触发测速失败');
  209. });
  210. }
  211. </script>
  212. </body>
  213. </html>