yangtb24 commited on
Commit
a188021
·
verified ·
1 Parent(s): a4faace

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +82 -84
app.py CHANGED
@@ -219,87 +219,86 @@ htmlTemplate = f"""
219
  <div id="servers" class="stats-container">
220
  </div>
221
  </div>
222
- <script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/js/all.min.js" integrity="sha512-yFjZbTYRCJodnuyGlsKamNE/LlEaEA/3uWCGారి7eIq7jWqVl3J8jL/kof/tfu9Xqzh/y/VM5sJd/tq5iEew==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
223
 
224
  <script>
225
  const username = '{{username}}';
226
 
227
- async function fetchInstances() {
228
- try {
229
  const response = await fetch(`/instances`);
230
  const userInstances = await response.json();
231
  return userInstances;
232
- } catch (error) {
233
  console.error("获取实例列表失败:", error);
234
  return [];
235
- }
236
- }
237
 
238
- class MetricsManager {
239
- constructor() {
240
  this.eventSources = new Map();
241
  this.servers = new Map();
242
  this.instanceOwners = new Map();
243
  this.spaceIds = new Map();
244
- }
245
 
246
- async connect(instanceId, username) {
247
- if (this.eventSources.has(instanceId)) return;
248
 
249
- try {
250
- const eventSource = new EventSource(`/metrics/${username}/${instanceId}`);
251
 
252
- this.spaceIds.set(instanceId, instanceId);
253
- this.instanceOwners.set(instanceId, username);
254
 
255
- eventSource.addEventListener("metric", (event) => {
256
- try {
257
- const data = JSON.parse(event.data);
258
- // console.log("Received data:", data);
259
- updateServerCard(data, instanceId);
260
- } catch (error) {
261
- console.error(`解析数据失败 (${instanceId}):`, error);
262
- }
263
- });
264
 
265
- eventSource.onerror = (error) => {
266
- console.error(`EventSource 错误 (${instanceId}):`, error);
267
  eventSource.close();
268
- this.eventSources.delete(instanceId); // 出错后删除连接
269
- };
270
 
271
- this.eventSources.set(instanceId, eventSource);
272
- } catch (error) {
273
- console.error(`连接失败 (${username}/${instanceId}):`, error);
274
- }
275
- }
276
 
277
- disconnectAll() {
278
  this.eventSources.forEach(es => es.close());
279
  this.eventSources.clear();
280
- }
281
- }
282
 
283
  const metricsManager = new MetricsManager();
284
  const servers = new Map();
285
- // 全局变量,存储每个空间的原始上传和下载数据(单位:字节/秒)
286
- const rawMetrics = new Map();
287
 
288
- async function initialize() {
289
  const instances = await fetchInstances();
290
- instances.forEach(instance => {
291
  metricsManager.connect(instance.id, instance.owner);
292
- });
293
- }
294
 
295
- function updateServerCard(data, spaceId) {
296
- const serverId = data.replica;
297
- const serverElement = document.getElementById(`server-${serverId}`);
298
- const owner = metricsManager.instanceOwners.get(spaceId);
299
 
300
- if (!serverElement) {
 
 
 
 
 
301
  const card = document.createElement('div');
302
- card.id = `server-${serverId}`;
303
  card.className = 'server-card';
304
  card.innerHTML = `
305
  <div class="server-header">
@@ -308,7 +307,7 @@ htmlTemplate = f"""
308
  <svg class="server-flag" width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
309
  <path d="M21 3H3C1.9 3 1 3.9 1 5v3c0 1.1.9 2 2 10h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-1 5H4V6h16v2zm1 4H3c-1.1 0-2 .9-2 2v3c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2v-3c0-1.1-.9-2-2-2zm-1 5H4v-2h16v2zm1 4H3c-1.1 0-2 .9-2 2v3c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2v-3c0-1.1-.9-2-2-2zm-1 5H4v-2h16v2z"/>
310
  </svg>
311
- <div>${serverId} (${owner}/${spaceId})</div>
312
  </div>
313
  </div>
314
  <div class="metric-grid">
@@ -337,77 +336,76 @@ htmlTemplate = f"""
337
  </div>
338
  `;
339
  document.getElementById('servers').appendChild(card);
340
- }
341
 
342
- const card = document.getElementById(`server-${serverId}`);
343
  const cpuUsage = data.cpu_usage_pct;
344
  const memoryUsage = (data.memory_used_bytes / data.memory_total_bytes) * 100;
345
  const uploadBps = data.tx_bps;
346
  const downloadBps = data.rx_bps;
347
 
348
- card.querySelector('.cpu-usage').textContent = `${cpuUsage.toFixed(2)}%`;
349
- card.querySelector('.cpu-progress-bar').style.width = `${cpuUsage}%`;
350
 
351
- card.querySelector('.memory-usage').textContent = `${memoryUsage.toFixed(2)}%`;
352
- card.querySelector('.memory-progress-bar').style.width = `${memoryUsage}%`;
353
 
354
- card.querySelector('.upload').textContent = `${formatBytes(uploadBps)}/s`;
355
- card.querySelector('.download').textContent = `${formatBytes(downloadBps)}/s`;
356
 
357
- // 更新最后一次收到 metrics 的时间戳
358
  servers.set(serverId, Date.now());
359
- // 保存原始上传/下载数据,后续用于累加计算总上传/总下载(单位:字节/秒)
360
- rawMetrics.set(serverId, {tx: uploadBps, rx: downloadBps});
361
  updateSummary();
362
- }
363
 
364
- function updateSummary() {
365
  const now = Date.now();
366
  let online = 0;
367
  let offline = 0;
368
  let totalUpload = 0;
369
  let totalDownload = 0;
370
 
371
- servers.forEach((lastSeen, serverId) => {
372
  const isOnline = (now - lastSeen) < 10000;
373
- const serverCard = document.getElementById(`server-${serverId}`);
374
- if (serverCard) {
375
  const statusDot = serverCard.querySelector('.status-dot');
376
- statusDot.className = `status-dot status-${isOnline ? 'online' : 'offline'}`;
377
-
378
- if (isOnline) {
379
- // rawMetrics 中获取原始的上传与下载(字节/s),确保单位一致
380
- const metrics = rawMetrics.get(serverId) || {tx: 0, rx: 0};
381
- totalUpload += metrics.tx;
382
- totalDownload += metrics.rx;
383
- }
384
- }
385
  isOnline ? online++ : offline++;
386
- });
387
 
388
  document.getElementById('totalServers').textContent = servers.size;
389
  document.getElementById('onlineServers').textContent = online;
390
  document.getElementById('offlineServers').textContent = offline;
391
- document.getElementById('totalUpload').textContent = `${formatBytes(totalUpload)}/s`;
392
- document.getElementById('totalDownload').textContent = `${formatBytes(totalDownload)}/s`;
393
- }
394
 
395
- function formatBytes(bytes) {
396
  if (bytes === 0) return '0 B';
397
  const k = 1024;
398
  const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
399
  const i = Math.floor(Math.log(bytes) / Math.log(k));
400
  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
401
- }
 
402
 
403
- initialize(); // 初始加载
404
 
405
  setInterval(updateSummary, 2000);
406
 
407
- setInterval(async () => {
408
  metricsManager.disconnectAll();
409
  await initialize();
410
- }, 300000);
 
411
  </script>
412
  </body>
413
  </html>
@@ -471,4 +469,4 @@ def stream_metrics(username, instance_id):
471
  return Response(generate(), mimetype='text/event-stream')
472
 
473
  if __name__ == '__main__':
474
- app.run(debug=True, host='0.0.0.0', port=7860)
 
219
  <div id="servers" class="stats-container">
220
  </div>
221
  </div>
222
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/js/all.min.js" integrity="sha512-yFjZbTYRCJodnuyGlsKamNE/LlEaEA/3uWCGാരി7eIq7jWqVl3J8jL/kof/tfu9Xqzh/y/VM5sJd/tq5iEew==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
223
 
224
  <script>
225
  const username = '{{username}}';
226
 
227
+ async function fetchInstances() {{
228
+ try {{
229
  const response = await fetch(`/instances`);
230
  const userInstances = await response.json();
231
  return userInstances;
232
+ }} catch (error) {{
233
  console.error("获取实例列表失败:", error);
234
  return [];
235
+ }}
236
+ }}
237
 
238
+ class MetricsManager {{
239
+ constructor() {{
240
  this.eventSources = new Map();
241
  this.servers = new Map();
242
  this.instanceOwners = new Map();
243
  this.spaceIds = new Map();
244
+ }}
245
 
246
+ async connect(instanceId, username) {{
247
+ if (this.eventSources.has(instanceId)) return;
248
 
249
+ try {{
250
+ const eventSource = new EventSource(`/metrics/${{username}}/${{instanceId}}`);
251
 
252
+ this.spaceIds.set(instanceId, instanceId);
253
+ this.instanceOwners.set(instanceId, username);
254
 
255
+ eventSource.addEventListener("metric", (event) => {{
256
+ try {{
257
+ const data = JSON.parse(event.data);
258
+ // console.log("Received data:", data); // Debugging line
259
+ updateServerCard(data, instanceId);
260
+ }} catch (error) {{
261
+ console.error(`解析数据失败 (${{instanceId}}):`, error);
262
+ }}
263
+ }});
264
 
265
+ eventSource.onerror = (error) => {{
266
+ console.error(`EventSource 错误 (${{instanceId}}):`, error);
267
  eventSource.close();
268
+ this.eventSources.delete(instanceId); // Remove on error
269
+ }};
270
 
271
+ this.eventSources.set(instanceId, eventSource);
272
+ }} catch (error) {{
273
+ console.error(`连接失败 (${{username}}/${{instanceId}}):`, error);
274
+ }}
275
+ }}
276
 
277
+ disconnectAll() {{
278
  this.eventSources.forEach(es => es.close());
279
  this.eventSources.clear();
280
+ }}
281
+ }}
282
 
283
  const metricsManager = new MetricsManager();
284
  const servers = new Map();
 
 
285
 
286
+ async function initialize() {{
287
  const instances = await fetchInstances();
288
+ instances.forEach(instance => {{
289
  metricsManager.connect(instance.id, instance.owner);
290
+ }});
291
+ }}
292
 
 
 
 
 
293
 
294
+ function updateServerCard(data, spaceId) {{
295
+ const serverId = data.replica;
296
+ const serverElement = document.getElementById(`server-${{serverId}}`);
297
+ const owner = metricsManager.instanceOwners.get(spaceId);
298
+
299
+ if (!serverElement) {{
300
  const card = document.createElement('div');
301
+ card.id = `server-${{serverId}}`;
302
  card.className = 'server-card';
303
  card.innerHTML = `
304
  <div class="server-header">
 
307
  <svg class="server-flag" width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
308
  <path d="M21 3H3C1.9 3 1 3.9 1 5v3c0 1.1.9 2 2 10h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-1 5H4V6h16v2zm1 4H3c-1.1 0-2 .9-2 2v3c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2v-3c0-1.1-.9-2-2-2zm-1 5H4v-2h16v2zm1 4H3c-1.1 0-2 .9-2 2v3c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2v-3c0-1.1-.9-2-2-2zm-1 5H4v-2h16v2z"/>
309
  </svg>
310
+ <div>${{serverId}} (${{owner}}/${{spaceId}})</div>
311
  </div>
312
  </div>
313
  <div class="metric-grid">
 
336
  </div>
337
  `;
338
  document.getElementById('servers').appendChild(card);
339
+ }}
340
 
341
+ const card = document.getElementById(`server-${{serverId}}`);
342
  const cpuUsage = data.cpu_usage_pct;
343
  const memoryUsage = (data.memory_used_bytes / data.memory_total_bytes) * 100;
344
  const uploadBps = data.tx_bps;
345
  const downloadBps = data.rx_bps;
346
 
347
+ card.querySelector('.cpu-usage').textContent = `${{cpuUsage.toFixed(2)}}%`;
348
+ card.querySelector('.cpu-progress-bar').style.width = `${{cpuUsage}}%`;
349
 
350
+ card.querySelector('.memory-usage').textContent = `${{memoryUsage.toFixed(2)}}%`;
351
+ card.querySelector('.memory-progress-bar').style.width = `${{memoryUsage}}%`;
352
 
353
+ card.querySelector('.upload').textContent = `${{formatBytes(uploadBps)}}/s`;
354
+ card.querySelector('.download').textContent = `${{formatBytes(downloadBps)}}/s`;
355
 
 
356
  servers.set(serverId, Date.now());
 
 
357
  updateSummary();
358
+ }}
359
 
360
+ function updateSummary() {{
361
  const now = Date.now();
362
  let online = 0;
363
  let offline = 0;
364
  let totalUpload = 0;
365
  let totalDownload = 0;
366
 
367
+ servers.forEach((lastSeen, serverId) => {{
368
  const isOnline = (now - lastSeen) < 10000;
369
+ const serverCard = document.getElementById(`server-${{serverId}}`);
370
+ if (serverCard) {{
371
  const statusDot = serverCard.querySelector('.status-dot');
372
+ statusDot.className = `status-dot status-${{isOnline ? 'online' : 'offline'}}`;
373
+
374
+ if (isOnline) {{
375
+ const uploadText = serverCard.querySelector('.upload').textContent;
376
+ const downloadText = serverCard.querySelector('.download').textContent;
377
+ totalUpload += parseFloat(uploadText) || 0;
378
+ totalDownload += parseFloat(downloadText) || 0;
379
+ }}
380
+ }}
381
  isOnline ? online++ : offline++;
382
+ }});
383
 
384
  document.getElementById('totalServers').textContent = servers.size;
385
  document.getElementById('onlineServers').textContent = online;
386
  document.getElementById('offlineServers').textContent = offline;
387
+ document.getElementById('totalUpload').textContent = `${{formatBytes(totalUpload)}}/s`;
388
+ document.getElementById('totalDownload').textContent = `${{formatBytes(totalDownload)}}/s`;
389
+ }}
390
 
391
+ function formatBytes(bytes) {{
392
  if (bytes === 0) return '0 B';
393
  const k = 1024;
394
  const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
395
  const i = Math.floor(Math.log(bytes) / Math.log(k));
396
  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
397
+ }}
398
+
399
 
400
+ initialize(); // Initial load
401
 
402
  setInterval(updateSummary, 2000);
403
 
404
+ setInterval(async () => {{
405
  metricsManager.disconnectAll();
406
  await initialize();
407
+ }}, 300000);
408
+
409
  </script>
410
  </body>
411
  </html>
 
469
  return Response(generate(), mimetype='text/event-stream')
470
 
471
  if __name__ == '__main__':
472
+ app.run(debug=True, host='0.0.0.0', port=7860)