Browse Source

Fix monitoring API response format and add all metrics fields

Backend fixes:
- Обернул /host-metrics/recent ответ в {metrics: [...]}
- Обернул /alerts ответ в {alerts: [...]}
- Добавил все новые поля в /host-metrics/recent:
  - IOPS (disk_read_iops, disk_write_iops)
  - Throughput (disk_read_mbps, disk_write_mbps, net_in_mbps, net_out_mbps)
  - CPU advanced (cpu_per_core, cpu_steal, context_switches, interrupts)
  - Memory advanced (buffers, cached, available, swap)
  - Network advanced (packets, errors, drops)
  - Process metrics (process_count, thread_count, top processes)
- Добавил limit параметр для /alerts
- Добавил created_at поле для frontend совместимости

Frontend fixes:
- Убрал .reverse() из loadHostMetrics() (API уже возвращает в правильном порядке)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
root 4 weeks ago
parent
commit
9577ba7a1f

+ 73 - 40
backend/app/api/v1/superadmin/monitoring.py

@@ -32,25 +32,54 @@ async def get_recent_host_metrics(
     metrics = list(result.scalars().all())
     metrics = list(result.scalars().all())
 
 
     # Return in chronological order
     # Return in chronological order
-    return [
-        {
-            "timestamp": m.timestamp.isoformat(),
-            "cpu_percent": m.cpu_percent,
-            "cpu_count": m.cpu_count,
-            "memory_total": m.memory_total,
-            "memory_used": m.memory_used,
-            "memory_percent": m.memory_percent,
-            "load_1": m.load_1,
-            "load_5": m.load_5,
-            "load_15": m.load_15,
-            "disk_read_bytes": m.disk_read_bytes,
-            "disk_write_bytes": m.disk_write_bytes,
-            "disk_usage_percent": m.disk_usage_percent,
-            "net_sent_bytes": m.net_sent_bytes,
-            "net_recv_bytes": m.net_recv_bytes,
-        }
-        for m in reversed(metrics)
-    ]
+    return {
+        "metrics": [
+            {
+                "timestamp": m.timestamp.isoformat(),
+                "cpu_percent": m.cpu_percent,
+                "cpu_count": m.cpu_count,
+                "cpu_per_core": m.cpu_per_core,
+                "cpu_steal": m.cpu_steal,
+                "context_switches_per_sec": m.context_switches_per_sec,
+                "interrupts_per_sec": m.interrupts_per_sec,
+                "memory_total": m.memory_total,
+                "memory_used": m.memory_used,
+                "memory_percent": m.memory_percent,
+                "memory_available": m.memory_available,
+                "memory_buffers": m.memory_buffers,
+                "memory_cached": m.memory_cached,
+                "swap_total": m.swap_total,
+                "swap_used": m.swap_used,
+                "swap_percent": m.swap_percent,
+                "load_1": m.load_1,
+                "load_5": m.load_5,
+                "load_15": m.load_15,
+                "disk_read_bytes": m.disk_read_bytes,
+                "disk_write_bytes": m.disk_write_bytes,
+                "disk_read_iops": m.disk_read_iops,
+                "disk_write_iops": m.disk_write_iops,
+                "disk_read_mbps": m.disk_read_mbps,
+                "disk_write_mbps": m.disk_write_mbps,
+                "disk_io_time_ms": m.disk_io_time_ms,
+                "disk_usage_percent": m.disk_usage_percent,
+                "net_sent_bytes": m.net_sent_bytes,
+                "net_recv_bytes": m.net_recv_bytes,
+                "net_in_mbps": m.net_in_mbps,
+                "net_out_mbps": m.net_out_mbps,
+                "net_packets_in_per_sec": m.net_packets_in_per_sec,
+                "net_packets_out_per_sec": m.net_packets_out_per_sec,
+                "net_errors_in": m.net_errors_in,
+                "net_errors_out": m.net_errors_out,
+                "net_drops_in": m.net_drops_in,
+                "net_drops_out": m.net_drops_out,
+                "process_count": m.process_count,
+                "thread_count": m.thread_count,
+                "top_cpu_processes": m.top_cpu_processes,
+                "top_mem_processes": m.top_mem_processes,
+            }
+            for m in reversed(metrics)
+        ]
+    }
 
 
 
 
 @router.get("/host-metrics/history")
 @router.get("/host-metrics/history")
@@ -93,11 +122,12 @@ async def get_host_metrics_history(
 @router.get("/alerts")
 @router.get("/alerts")
 async def get_alerts(
 async def get_alerts(
     dismissed: bool = Query(default=False),
     dismissed: bool = Query(default=False),
+    limit: int = Query(default=100, le=1000),
     db: AsyncSession = Depends(get_db),
     db: AsyncSession = Depends(get_db),
     _current_user=Depends(get_current_superadmin),
     _current_user=Depends(get_current_superadmin),
 ):
 ):
     """Get alerts (by default only active/non-dismissed alerts)."""
     """Get alerts (by default only active/non-dismissed alerts)."""
-    query = select(Alert).order_by(Alert.timestamp.desc())
+    query = select(Alert).order_by(Alert.timestamp.desc()).limit(limit)
 
 
     if not dismissed:
     if not dismissed:
         query = query.where(Alert.dismissed == False)
         query = query.where(Alert.dismissed == False)
@@ -105,26 +135,29 @@ async def get_alerts(
     result = await db.execute(query)
     result = await db.execute(query)
     alerts = list(result.scalars().all())
     alerts = list(result.scalars().all())
 
 
-    return [
-        {
-            "id": a.id,
-            "timestamp": a.timestamp.isoformat(),
-            "alert_type": a.alert_type,
-            "severity": a.severity,
-            "title": a.title,
-            "message": a.message,
-            "alert_metadata": a.alert_metadata,
-            "acknowledged": a.acknowledged,
-            "acknowledged_at": a.acknowledged_at.isoformat() if a.acknowledged_at else None,
-            "acknowledged_by": a.acknowledged_by,
-            "dismissed": a.dismissed,
-            "dismissed_at": a.dismissed_at.isoformat() if a.dismissed_at else None,
-            "sent_dashboard": a.sent_dashboard,
-            "sent_telegram": a.sent_telegram,
-            "sent_email": a.sent_email,
-        }
-        for a in alerts
-    ]
+    return {
+        "alerts": [
+            {
+                "id": a.id,
+                "timestamp": a.timestamp.isoformat(),
+                "created_at": a.timestamp.isoformat(),  # For frontend compatibility
+                "alert_type": a.alert_type,
+                "severity": a.severity,
+                "title": a.title,
+                "message": a.message,
+                "alert_metadata": a.alert_metadata,
+                "acknowledged": a.acknowledged,
+                "acknowledged_at": a.acknowledged_at.isoformat() if a.acknowledged_at else None,
+                "acknowledged_by": a.acknowledged_by,
+                "dismissed": a.dismissed,
+                "dismissed_at": a.dismissed_at.isoformat() if a.dismissed_at else None,
+                "sent_dashboard": a.sent_dashboard,
+                "sent_telegram": a.sent_telegram,
+                "sent_email": a.sent_email,
+            }
+            for a in alerts
+        ]
+    }
 
 
 
 
 @router.post("/alerts/{alert_id}/acknowledge")
 @router.post("/alerts/{alert_id}/acknowledge")

+ 1 - 1
frontend/src/views/superadmin/DashboardView.vue

@@ -263,7 +263,7 @@ async function loadHostMetrics() {
       params: { limit: 30 }
       params: { limit: 30 }
     })
     })
     console.log('Host metrics loaded:', data.metrics.length, 'records')
     console.log('Host metrics loaded:', data.metrics.length, 'records')
-    hostMetrics.value = data.metrics.reverse() // Oldest first
+    hostMetrics.value = data.metrics  // Already in chronological order
     console.log('Calling updateCharts()...')
     console.log('Calling updateCharts()...')
     updateCharts()
     updateCharts()
   } catch (error) {
   } catch (error) {