monitoring.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. """
  2. Superadmin monitoring endpoints for host metrics and alerts.
  3. """
  4. from datetime import datetime, timedelta, timezone
  5. from fastapi import APIRouter, Depends, Query
  6. from sqlalchemy import select
  7. from sqlalchemy.ext.asyncio import AsyncSession
  8. from app.api.deps import get_current_superadmin, get_db
  9. from app.models.alert import Alert
  10. from app.models.host_metrics import HostMetrics
  11. from app.models.security_event import SecurityEvent
  12. from app.services.alert_service import alert_service
  13. router = APIRouter()
  14. @router.get("/host-metrics/recent")
  15. async def get_recent_host_metrics(
  16. limit: int = Query(default=60, le=1000),
  17. db: AsyncSession = Depends(get_db),
  18. _current_user=Depends(get_current_superadmin),
  19. ):
  20. """Get recent host metrics for dashboard charts (default: last 60 data points)."""
  21. result = await db.execute(
  22. select(HostMetrics)
  23. .order_by(HostMetrics.timestamp.desc())
  24. .limit(limit)
  25. )
  26. metrics = list(result.scalars().all())
  27. # Return in chronological order
  28. return [
  29. {
  30. "timestamp": m.timestamp.isoformat(),
  31. "cpu_percent": m.cpu_percent,
  32. "cpu_count": m.cpu_count,
  33. "memory_total": m.memory_total,
  34. "memory_used": m.memory_used,
  35. "memory_percent": m.memory_percent,
  36. "load_1": m.load_1,
  37. "load_5": m.load_5,
  38. "load_15": m.load_15,
  39. "disk_read_bytes": m.disk_read_bytes,
  40. "disk_write_bytes": m.disk_write_bytes,
  41. "disk_usage_percent": m.disk_usage_percent,
  42. "net_sent_bytes": m.net_sent_bytes,
  43. "net_recv_bytes": m.net_recv_bytes,
  44. }
  45. for m in reversed(metrics)
  46. ]
  47. @router.get("/host-metrics/history")
  48. async def get_host_metrics_history(
  49. start_date: datetime = Query(...),
  50. end_date: datetime = Query(...),
  51. db: AsyncSession = Depends(get_db),
  52. _current_user=Depends(get_current_superadmin),
  53. ):
  54. """Get historical host metrics for specified date range."""
  55. result = await db.execute(
  56. select(HostMetrics)
  57. .where(HostMetrics.timestamp >= start_date)
  58. .where(HostMetrics.timestamp <= end_date)
  59. .order_by(HostMetrics.timestamp.asc())
  60. )
  61. metrics = list(result.scalars().all())
  62. return [
  63. {
  64. "timestamp": m.timestamp.isoformat(),
  65. "cpu_percent": m.cpu_percent,
  66. "cpu_count": m.cpu_count,
  67. "memory_total": m.memory_total,
  68. "memory_used": m.memory_used,
  69. "memory_percent": m.memory_percent,
  70. "load_1": m.load_1,
  71. "load_5": m.load_5,
  72. "load_15": m.load_15,
  73. "disk_read_bytes": m.disk_read_bytes,
  74. "disk_write_bytes": m.disk_write_bytes,
  75. "disk_usage_percent": m.disk_usage_percent,
  76. "net_sent_bytes": m.net_sent_bytes,
  77. "net_recv_bytes": m.net_recv_bytes,
  78. }
  79. for m in metrics
  80. ]
  81. @router.get("/alerts")
  82. async def get_alerts(
  83. dismissed: bool = Query(default=False),
  84. db: AsyncSession = Depends(get_db),
  85. _current_user=Depends(get_current_superadmin),
  86. ):
  87. """Get alerts (by default only active/non-dismissed alerts)."""
  88. query = select(Alert).order_by(Alert.timestamp.desc())
  89. if not dismissed:
  90. query = query.where(Alert.dismissed == False)
  91. result = await db.execute(query)
  92. alerts = list(result.scalars().all())
  93. return [
  94. {
  95. "id": a.id,
  96. "timestamp": a.timestamp.isoformat(),
  97. "alert_type": a.alert_type,
  98. "severity": a.severity,
  99. "title": a.title,
  100. "message": a.message,
  101. "alert_metadata": a.alert_metadata,
  102. "acknowledged": a.acknowledged,
  103. "acknowledged_at": a.acknowledged_at.isoformat() if a.acknowledged_at else None,
  104. "acknowledged_by": a.acknowledged_by,
  105. "dismissed": a.dismissed,
  106. "dismissed_at": a.dismissed_at.isoformat() if a.dismissed_at else None,
  107. "sent_dashboard": a.sent_dashboard,
  108. "sent_telegram": a.sent_telegram,
  109. "sent_email": a.sent_email,
  110. }
  111. for a in alerts
  112. ]
  113. @router.post("/alerts/{alert_id}/acknowledge")
  114. async def acknowledge_alert(
  115. alert_id: int,
  116. db: AsyncSession = Depends(get_db),
  117. current_user=Depends(get_current_superadmin),
  118. ):
  119. """Mark alert as acknowledged."""
  120. await alert_service.acknowledge_alert(db, alert_id, current_user.id)
  121. return {"status": "ok"}
  122. @router.post("/alerts/{alert_id}/dismiss")
  123. async def dismiss_alert(
  124. alert_id: int,
  125. db: AsyncSession = Depends(get_db),
  126. _current_user=Depends(get_current_superadmin),
  127. ):
  128. """Mark alert as dismissed (hide from dashboard)."""
  129. await alert_service.dismiss_alert(db, alert_id)
  130. return {"status": "ok"}
  131. @router.get("/security-events")
  132. async def get_security_events(
  133. resolved: bool = Query(default=False),
  134. limit: int = Query(default=100, le=1000),
  135. db: AsyncSession = Depends(get_db),
  136. _current_user=Depends(get_current_superadmin),
  137. ):
  138. """Get security events (by default only unresolved events)."""
  139. query = select(SecurityEvent).order_by(SecurityEvent.timestamp.desc()).limit(limit)
  140. if not resolved:
  141. query = query.where(SecurityEvent.resolved == False)
  142. result = await db.execute(query)
  143. events = list(result.scalars().all())
  144. return [
  145. {
  146. "id": e.id,
  147. "timestamp": e.timestamp.isoformat(),
  148. "event_type": e.event_type,
  149. "severity": e.severity,
  150. "ip_address": e.ip_address,
  151. "user_agent": e.user_agent,
  152. "endpoint": e.endpoint,
  153. "description": e.description,
  154. "event_metadata": e.event_metadata,
  155. "resolved": e.resolved,
  156. "resolved_at": e.resolved_at.isoformat() if e.resolved_at else None,
  157. "resolved_by": e.resolved_by,
  158. }
  159. for e in events
  160. ]
  161. @router.post("/security-events/{event_id}/resolve")
  162. async def resolve_security_event(
  163. event_id: int,
  164. db: AsyncSession = Depends(get_db),
  165. current_user=Depends(get_current_superadmin),
  166. ):
  167. """Mark security event as resolved."""
  168. result = await db.execute(
  169. select(SecurityEvent).where(SecurityEvent.id == event_id)
  170. )
  171. event = result.scalar_one_or_none()
  172. if event:
  173. event.resolved = True
  174. event.resolved_at = datetime.now(timezone.utc)
  175. event.resolved_by = current_user.id
  176. await db.commit()
  177. return {"status": "ok"}