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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +125 -132
app.py CHANGED
@@ -1,7 +1,6 @@
1
  from flask import Flask, render_template_string, jsonify, Response
2
  import requests
3
  import os
4
- import json
5
 
6
  app = Flask(__name__)
7
 
@@ -220,148 +219,147 @@ htmlTemplate = f"""
220
  <div id="servers" class="stats-container">
221
  </div>
222
  </div>
223
- <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>
224
 
225
  <script>
226
- const username = '{USERNAME}';
227
 
228
- async function fetchInstances() {{
229
- try {{
230
  const response = await fetch(`/instances`);
231
  const userInstances = await response.json();
232
  return userInstances;
233
- }} catch (error) {{
234
  console.error("获取实例列表失败:", error);
235
  return [];
236
- }}
237
- }}
238
 
239
- class MetricsManager {{
240
- constructor() {{
241
  this.eventSources = new Map();
242
  this.servers = new Map();
243
  this.instanceOwners = new Map();
244
  this.spaceIds = new Map();
245
- this.lastData = new Map(); // Store last received data
246
- }}
247
 
248
- async connect(instanceId, username) {{
249
  if (this.eventSources.has(instanceId)) return;
250
 
251
- try {{
252
- const eventSource = new EventSource(`/metrics/${{username}}/${{instanceId}}`);
253
-
254
- this.spaceIds.set(instanceId, instanceId);
255
- this.instanceOwners.set(instanceId, username);
256
-
257
- eventSource.addEventListener("metric", (event) => {{
258
- try {{
259
- const data = JSON.parse(event.data);
260
- this.lastData.set(instanceId, data); // Store the data
261
- updateServerCard(data, instanceId);
262
- }} catch (error) {{
263
- console.error(`解析数据失败 (${{instanceId}}):`, error);
264
- }}
265
- }});
266
-
267
- eventSource.onerror = (error) => {{
268
- console.error(`EventSource 错误 (${{instanceId}}):`, error);
269
- eventSource.close();
270
- this.eventSources.delete(instanceId);
271
- }};
272
-
273
- this.eventSources.set(instanceId, eventSource);
274
- }} catch (error) {{
275
- console.error(`连接失败 (${{username}}/${{instanceId}}):`, error);
276
- }}
277
- }}
278
-
279
- disconnectAll() {{
280
  this.eventSources.forEach(es => es.close());
281
  this.eventSources.clear();
282
- }}
283
-
284
- getLastData(instanceId) {{
285
- return this.lastData.get(instanceId);
286
- }}
287
- }}
288
 
289
  const metricsManager = new MetricsManager();
290
  const servers = new Map();
 
 
291
 
292
- async function initialize() {{
293
  const instances = await fetchInstances();
294
- instances.forEach(instance => {{
295
  metricsManager.connect(instance.id, instance.owner);
296
- }});
297
- }}
298
-
299
-
300
- function updateServerCard(data, spaceId) {{
301
- const serverId = data.replica;
302
- const serverElement = document.getElementById(`server-${{serverId}}`);
303
- const owner = metricsManager.instanceOwners.get(spaceId);
304
-
305
- if (!serverElement) {{
306
- const card = document.createElement('div');
307
- card.id = `server-${{serverId}}`;
308
- card.className = 'server-card';
309
- card.innerHTML = `
310
- <div class="server-header">
311
- <div class="server-name">
312
- <div class="status-dot status-online"></div>
313
- <svg class="server-flag" width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
314
- <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"/>
315
- </svg>
316
- <div>${{serverId}} (${{owner}}/${{spaceId}})</div>
317
- </div>
318
- </div>
319
- <div class="metric-grid">
320
- <div class="metric-item">
321
- <div class="metric-label">CPU</div>
322
- <div class="progress-bar-container">
323
- <div class="cpu-progress-bar"></div>
324
- </div>
325
- <div class="metric-value cpu-usage">0%</div>
326
- </div>
327
- <div class="metric-item">
328
- <div class="metric-label">内存</div>
329
- <div class="progress-bar-container">
330
- <div class="memory-progress-bar"></div>
331
- </div>
332
- <div class="metric-value memory-usage">0%</div>
333
- </div>
334
- <div class="metric-item">
335
- <div class="metric-label">上传</div>
336
- <div class="metric-value upload">0 KB/s</div>
337
  </div>
338
- <div class="metric-item">
339
- <div class="metric-label">下载</div>
340
- <div class="metric-value download">0 KB/s</div>
 
 
 
341
  </div>
342
- </div>
343
- `;
344
- document.getElementById('servers').appendChild(card);
345
- }}
346
-
347
- const card = document.getElementById(`server-${{serverId}}`);
348
- const cpuUsage = data.cpu_usage_pct;
349
- const memoryUsage = (data.memory_used_bytes / data.memory_total_bytes) * 100;
350
- const uploadBps = data.tx_bps;
351
- const downloadBps = data.rx_bps;
352
-
353
- card.querySelector('.cpu-usage').textContent = `${{cpuUsage.toFixed(2)}}%`;
354
- card.querySelector('.cpu-progress-bar').style.width = `${{cpuUsage}}%`;
355
-
356
- card.querySelector('.memory-usage').textContent = `${{memoryUsage.toFixed(2)}}%`;
357
- card.querySelector('.memory-progress-bar').style.width = `${{memoryUsage}}%`;
358
-
359
- card.querySelector('.upload').textContent = `${{formatBytes(uploadBps)}}/s`;
360
- card.querySelector('.download').textContent = `${{formatBytes(downloadBps)}}/s`;
361
-
362
- servers.set(serverId, Date.now());
363
- updateSummary();
364
- }}
 
 
 
 
 
 
 
 
 
 
 
 
 
365
 
366
  function updateSummary() {
367
  const now = Date.now();
@@ -373,17 +371,15 @@ htmlTemplate = f"""
373
  servers.forEach((lastSeen, serverId) => {
374
  const isOnline = (now - lastSeen) < 10000;
375
  const serverCard = document.getElementById(`server-${serverId}`);
376
-
377
  if (serverCard) {
378
  const statusDot = serverCard.querySelector('.status-dot');
379
  statusDot.className = `status-dot status-${isOnline ? 'online' : 'offline'}`;
380
 
381
  if (isOnline) {
382
- const lastData = metricsManager.getLastData(serverId.split(" ")[0]); // Use the correct instanceId
383
- if(lastData) {
384
- totalUpload += lastData.tx_bps;
385
- totalDownload += lastData.rx_bps;
386
- }
387
  }
388
  }
389
  isOnline ? online++ : offline++;
@@ -396,25 +392,22 @@ htmlTemplate = f"""
396
  document.getElementById('totalDownload').textContent = `${formatBytes(totalDownload)}/s`;
397
  }
398
 
399
-
400
- function formatBytes(bytes) {{
401
  if (bytes === 0) return '0 B';
402
  const k = 1024;
403
  const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
404
  const i = Math.floor(Math.log(bytes) / Math.log(k));
405
  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
406
- }}
407
-
408
 
409
- initialize(); // Initial load
410
 
411
  setInterval(updateSummary, 2000);
412
 
413
- setInterval(async () => {{
414
  metricsManager.disconnectAll();
415
  await initialize();
416
- }}, 300000);
417
-
418
  </script>
419
  </body>
420
  </html>
@@ -464,7 +457,7 @@ def stream_metrics(username, instance_id):
464
  event_type = line.split(":", 1)[1].strip()
465
  elif line.startswith("data:"):
466
  data_lines.append(line.split(":", 1)[1].strip())
467
-
468
  if event_type == "metric":
469
  yield f"event: {event_type}\ndata: {''.join(data_lines)}\n\n"
470
 
 
1
  from flask import Flask, render_template_string, jsonify, Response
2
  import requests
3
  import os
 
4
 
5
  app = Flask(__name__)
6
 
 
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">
306
+ <div class="server-name">
307
+ <div class="status-dot status-online"></div>
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">
315
+ <div class="metric-item">
316
+ <div class="metric-label">CPU</div>
317
+ <div class="progress-bar-container">
318
+ <div class="cpu-progress-bar"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
319
  </div>
320
+ <div class="metric-value cpu-usage">0%</div>
321
+ </div>
322
+ <div class="metric-item">
323
+ <div class="metric-label">内存</div>
324
+ <div class="progress-bar-container">
325
+ <div class="memory-progress-bar"></div>
326
  </div>
327
+ <div class="metric-value memory-usage">0%</div>
328
+ </div>
329
+ <div class="metric-item">
330
+ <div class="metric-label">上传</div>
331
+ <div class="metric-value upload">0 KB/s</div>
332
+ </div>
333
+ <div class="metric-item">
334
+ <div class="metric-label">下载</div>
335
+ <div class="metric-value download">0 KB/s</div>
336
+ </div>
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();
 
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++;
 
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>
 
457
  event_type = line.split(":", 1)[1].strip()
458
  elif line.startswith("data:"):
459
  data_lines.append(line.split(":", 1)[1].strip())
460
+
461
  if event_type == "metric":
462
  yield f"event: {event_type}\ndata: {''.join(data_lines)}\n\n"
463