i0110 commited on
Commit
2ede23e
·
verified ·
1 Parent(s): 6e2ff83

Update public/index.html

Browse files
Files changed (1) hide show
  1. public/index.html +115 -48
public/index.html CHANGED
@@ -546,6 +546,17 @@
546
  height: 250px; /* 小屏幕图表高度略减 */
547
  }
548
  }
 
 
 
 
 
 
 
 
 
 
 
549
  </style>
550
  </head>
551
  <body>
@@ -934,53 +945,84 @@
934
  }
935
  }
936
 
937
- class MetricsManager {
938
  constructor() {
939
- this.eventSources = new Map();
940
- this.instanceOwners = new Map();
 
941
  }
942
 
943
- async connect(instanceId, username) {
944
- if (this.eventSources.has(instanceId)) {
945
- return;
946
  }
947
 
948
- try {
949
- const eventSource = new EventSource(
950
- `/api/proxy/live-metrics/${username}/${instanceId.split('/')[1]}`
951
- );
 
 
 
 
 
 
 
 
 
 
952
 
953
- this.instanceOwners.set(instanceId, username);
 
 
 
 
 
 
954
 
955
- eventSource.addEventListener("metric", (event) => {
956
- try {
957
- const data = JSON.parse(event.data);
958
- updateServerCard(data, instanceId);
959
- } catch (error) {
960
- console.error(`解析数据失败 (${instanceId}):`, error);
961
- }
962
- });
963
-
964
- eventSource.onerror = (error) => {
965
- eventSource.close();
966
- this.eventSources.delete(instanceId);
967
- updateServerCard(null, instanceId, false);
968
- };
969
-
970
- this.eventSources.set(instanceId, eventSource);
971
- } catch (error) {
972
- console.error(`连接失败 (${username}/${instanceId}):`, error);
973
- updateServerCard(null, instanceId, false);
974
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
975
  }
976
 
977
- disconnectAll() {
978
- this.eventSources.forEach(es => es.close());
979
- this.eventSources.clear();
 
 
 
980
  }
981
  }
982
 
983
- const metricsManager = new MetricsManager();
984
  const instanceMap = new Map();
985
  const serverStatus = new Map();
986
  let allInstances = []; // 存储所有实例数据,用于过滤和排序
@@ -991,18 +1033,21 @@
991
  const instances = await fetchInstances();
992
  allInstances = instances;
993
  renderInstances(allInstances);
994
- instances.forEach(instance => {
995
- if (instance.status.toLowerCase() === 'running') {
996
- metricsManager.connect(instance.repo_id, instance.owner);
997
- }
998
- });
 
 
 
999
  updateSummary();
1000
  updateActionButtons(isLoggedIn);
1001
  }
1002
 
1003
  // 手动刷新数据函数
1004
  async function refreshData() {
1005
- metricsManager.disconnectAll();
1006
  // 销毁所有图表实例
1007
  chartInstances.forEach(chart => {
1008
  if (chart) {
@@ -1205,7 +1250,13 @@
1205
  </svg>
1206
  <div>${instance.name} (${instance.repo_id})</div>
1207
  </div>
1208
- <button class="chart-toggle-button" onclick="toggleChart('${instanceId}')">查看图表</button>
 
 
 
 
 
 
1209
  </div>
1210
  <div class="metric-grid">
1211
  <div class="metric-item">
@@ -1446,7 +1497,7 @@
1446
  setInterval(updateSummary, 5000);
1447
 
1448
  setInterval(async () => {
1449
- metricsManager.disconnectAll();
1450
  await initialize();
1451
  }, 300000);
1452
 
@@ -1484,7 +1535,7 @@
1484
  // 重新渲染过滤和排序后的实例
1485
  instanceMap.clear();
1486
  serverStatus.clear();
1487
- metricsManager.disconnectAll();
1488
  // 销毁现有图表实例
1489
  chartInstances.forEach(chart => {
1490
  if (chart) {
@@ -1493,14 +1544,30 @@
1493
  });
1494
  chartInstances.clear();
1495
  renderInstances(filteredInstances);
1496
- filteredInstances.forEach(instance => {
1497
- if (instance.status.toLowerCase() === 'running') {
1498
- metricsManager.connect(instance.repo_id, instance.owner);
1499
- }
1500
- });
 
 
 
1501
  updateSummary();
1502
  updateActionButtons(isLoggedIn);
1503
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
1504
  </script>
1505
  </body>
1506
  </html>
 
546
  height: 250px; /* 小屏幕图表高度略减 */
547
  }
548
  }
549
+ /* 监控复选框样式 */
550
+ input[type="checkbox"] {
551
+ accent-color: #4CAF50; /* 选中时颜色 */
552
+ width: 16px;
553
+ height: 16px;
554
+ cursor: pointer;
555
+ }
556
+ label {
557
+ color: var(--text-color);
558
+ user-select: none;
559
+ }
560
  </style>
561
  </head>
562
  <body>
 
945
  }
946
  }
947
 
948
+ class MetricsStreamManager {
949
  constructor() {
950
+ this.eventSource = null;
951
+ this.clientId = null;
952
+ this.subscribedInstances = new Set();
953
  }
954
 
955
+ connect(subscribedInstances = []) {
956
+ if (this.eventSource) {
957
+ this.eventSource.close();
958
  }
959
 
960
+ this.subscribedInstances = new Set(subscribedInstances);
961
+ const instancesParam = Array.from(this.subscribedInstances).join(',');
962
+ const url = `/api/proxy/live-metrics-stream?instances=${encodeURIComponent(instancesParam)}`;
963
+ this.eventSource = new EventSource(url);
964
+
965
+ this.eventSource.addEventListener("metric", (event) => {
966
+ try {
967
+ const data = JSON.parse(event.data);
968
+ const { repoId, metrics } = data;
969
+ updateServerCard(metrics, repoId);
970
+ } catch (error) {
971
+ console.error(`解析监控数据失败:`, error);
972
+ }
973
+ });
974
 
975
+ this.eventSource.onerror = (error) => {
976
+ console.error(`SSE 连接错误:`, error);
977
+ this.eventSource.close();
978
+ this.eventSource = null;
979
+ // 可选:尝试重新连接
980
+ setTimeout(() => this.connect(Array.from(this.subscribedInstances)), 5000);
981
+ };
982
 
983
+ console.log(`SSE 连接已建立,订阅实例: ${instancesParam || '无'}`);
984
+ }
985
+
986
+ updateSubscriptions(newInstances) {
987
+ if (!this.clientId) {
988
+ this.clientId = localStorage.getItem('sseClientId') || crypto.randomUUID();
989
+ localStorage.setItem('sseClientId', this.clientId);
 
 
 
 
 
 
 
 
 
 
 
 
990
  }
991
+
992
+ this.subscribedInstances = new Set(newInstances);
993
+ fetch('/api/proxy/update-subscriptions', {
994
+ method: 'POST',
995
+ headers: {
996
+ 'Content-Type': 'application/json',
997
+ },
998
+ body: JSON.stringify({
999
+ clientId: this.clientId,
1000
+ instances: Array.from(this.subscribedInstances)
1001
+ })
1002
+ })
1003
+ .then(response => response.json())
1004
+ .then(data => {
1005
+ if (data.success) {
1006
+ console.log(`订阅更新成功: ${Array.from(this.subscribedInstances).join(', ') || '无'}`);
1007
+ } else {
1008
+ console.error(`订阅更新失败:`, data.error);
1009
+ }
1010
+ })
1011
+ .catch(error => {
1012
+ console.error(`更新订阅请求失败:`, error);
1013
+ });
1014
  }
1015
 
1016
+ disconnect() {
1017
+ if (this.eventSource) {
1018
+ this.eventSource.close();
1019
+ this.eventSource = null;
1020
+ console.log(`SSE 连接已断开`);
1021
+ }
1022
  }
1023
  }
1024
 
1025
+ const metricsStreamManager = new MetricsStreamManager();
1026
  const instanceMap = new Map();
1027
  const serverStatus = new Map();
1028
  let allInstances = []; // 存储所有实例数据,用于过滤和排序
 
1033
  const instances = await fetchInstances();
1034
  allInstances = instances;
1035
  renderInstances(allInstances);
1036
+
1037
+ // 默认只监控状态为 running 的实例
1038
+ const runningInstances = instances
1039
+ .filter(instance => instance.status.toLowerCase() === 'running')
1040
+ .map(instance => instance.repo_id);
1041
+
1042
+ metricsStreamManager.connect(runningInstances);
1043
+
1044
  updateSummary();
1045
  updateActionButtons(isLoggedIn);
1046
  }
1047
 
1048
  // 手动刷新数据函数
1049
  async function refreshData() {
1050
+ metricsStreamManager.disconnect();
1051
  // 销毁所有图表实例
1052
  chartInstances.forEach(chart => {
1053
  if (chart) {
 
1250
  </svg>
1251
  <div>${instance.name} (${instance.repo_id})</div>
1252
  </div>
1253
+ <div>
1254
+ <label style="display: inline-flex; align-items: center; gap: 5px; font-size: 13px; cursor: pointer;">
1255
+ 监控
1256
+ <input type="checkbox" id="monitor-${instanceId}" onchange="toggleMonitorInstance('${instanceId}', this.checked)" ${instance.status.toLowerCase() === 'running' ? 'checked' : ''}>
1257
+ </label>
1258
+ <button class="chart-toggle-button" onclick="toggleChart('${instanceId}')">查看图表</button>
1259
+ </div>
1260
  </div>
1261
  <div class="metric-grid">
1262
  <div class="metric-item">
 
1497
  setInterval(updateSummary, 5000);
1498
 
1499
  setInterval(async () => {
1500
+ metricsStreamManager.disconnect();
1501
  await initialize();
1502
  }, 300000);
1503
 
 
1535
  // 重新渲染过滤和排序后的实例
1536
  instanceMap.clear();
1537
  serverStatus.clear();
1538
+ metricsStreamManager.disconnect();
1539
  // 销毁现有图表实例
1540
  chartInstances.forEach(chart => {
1541
  if (chart) {
 
1544
  });
1545
  chartInstances.clear();
1546
  renderInstances(filteredInstances);
1547
+
1548
+ // 重新计算当前订阅的实例,只保留仍可见的实例
1549
+ const visibleInstanceIds = new Set(filteredInstances.map(instance => instance.repo_id));
1550
+ const updatedSubscriptions = Array.from(metricsStreamManager.subscribedInstances)
1551
+ .filter(instanceId => visibleInstanceIds.has(instanceId));
1552
+ metricsStreamManager.subscribedInstances = new Set(updatedSubscriptions);
1553
+ metricsStreamManager.connect(updatedSubscriptions); // 重新连接
1554
+
1555
  updateSummary();
1556
  updateActionButtons(isLoggedIn);
1557
  }
1558
+
1559
+ // 新增函数:切换实例监控状态
1560
+ function toggleMonitorInstance(instanceId, checked) {
1561
+ const checkbox = document.getElementById(`monitor-${instanceId}`);
1562
+ if (!checkbox) return;
1563
+
1564
+ if (checked) {
1565
+ metricsStreamManager.subscribedInstances.add(instanceId);
1566
+ } else {
1567
+ metricsStreamManager.subscribedInstances.delete(instanceId);
1568
+ }
1569
+ metricsStreamManager.updateSubscriptions(Array.from(metricsStreamManager.subscribedInstances));
1570
+ }
1571
  </script>
1572
  </body>
1573
  </html>