feat: SAC 0.7.4 sidebar system stats block with UI toggle

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
PTah
2026-06-02 09:15:15 +10:00
parent cf5a6f995c
commit 3053058cdf
20 changed files with 1082 additions and 255 deletions
+115
View File
@@ -0,0 +1,115 @@
"""Host and DB metrics for SAC sidebar (best-effort, no heavy queries)."""
from __future__ import annotations
import os
import shutil
import time
from datetime import datetime, timedelta, timezone
from pathlib import Path
from sqlalchemy import func, select, text
from sqlalchemy.orm import Session
from app.models import Event, Problem
def _disk_path() -> str:
explicit = os.environ.get("SAC_STATS_DISK_PATH", "").strip()
if explicit:
return explicit
return str(Path("/").resolve())
def _read_cpu_percent() -> float | None:
try:
import psutil # type: ignore[import-untyped]
return round(float(psutil.cpu_percent(interval=None)), 1)
except Exception:
return None
def _read_memory() -> dict[str, float | int] | None:
try:
import psutil # type: ignore[import-untyped]
vm = psutil.virtual_memory()
return {
"used_mb": int(vm.used / (1024 * 1024)),
"total_mb": int(vm.total / (1024 * 1024)),
"percent": round(float(vm.percent), 1),
}
except Exception:
return None
def _read_disk(path: str) -> dict[str, float | int | str] | None:
try:
usage = shutil.disk_usage(path)
total = int(usage.total)
used = int(usage.used)
if total <= 0:
return None
return {
"path": path,
"used_gb": round(used / (1024**3), 1),
"total_gb": round(total / (1024**3), 1),
"percent": round(100.0 * used / total, 1),
}
except Exception:
return None
def collect_system_stats(db: Session) -> dict:
collected_at = datetime.now(timezone.utc).isoformat()
disk_path = _disk_path()
db_status = "ok"
db_latency_ms: float | None = None
db_connections: int | None = None
t0 = time.perf_counter()
try:
db.execute(text("SELECT 1"))
db_latency_ms = round((time.perf_counter() - t0) * 1000, 1)
try:
db_connections = int(
db.scalar(
text(
"SELECT count(*) FROM pg_stat_activity "
"WHERE datname = current_database() AND pid <> pg_backend_pid()"
)
)
or 0
)
except Exception:
db_connections = None
except Exception:
db_status = "error"
since = datetime.now(timezone.utc) - timedelta(hours=1)
events_last_hour = 0
problems_open = 0
if db_status == "ok":
events_last_hour = int(
db.scalar(select(func.count()).select_from(Event).where(Event.received_at >= since)) or 0
)
problems_open = int(
db.scalar(select(func.count()).select_from(Problem).where(Problem.status == "open")) or 0
)
return {
"collected_at": collected_at,
"cpu_percent": _read_cpu_percent(),
"memory": _read_memory(),
"disk": _read_disk(disk_path),
"database": {
"status": db_status,
"latency_ms": db_latency_ms,
"active_connections": db_connections,
},
"app": {
"events_last_hour": events_last_hour,
"problems_open": problems_open,
},
}
+30
View File
@@ -0,0 +1,30 @@
from dataclasses import dataclass
from sqlalchemy.orm import Session
from app.models.ui_settings import UI_SETTINGS_ROW_ID, UiSettings
@dataclass(frozen=True)
class UiSettingsConfig:
show_sidebar_system_stats: bool
source: str = "db"
def get_effective_ui_settings(db: Session) -> UiSettingsConfig:
row = db.get(UiSettings, UI_SETTINGS_ROW_ID)
if row is None:
return UiSettingsConfig(show_sidebar_system_stats=True, source="default")
return UiSettingsConfig(show_sidebar_system_stats=bool(row.show_sidebar_system_stats), source="db")
def upsert_ui_settings(db: Session, *, show_sidebar_system_stats: bool) -> UiSettingsConfig:
row = db.get(UiSettings, UI_SETTINGS_ROW_ID)
if row is None:
row = UiSettings(id=UI_SETTINGS_ROW_ID, show_sidebar_system_stats=show_sidebar_system_stats)
db.add(row)
else:
row.show_sidebar_system_stats = show_sidebar_system_stats
db.commit()
db.refresh(row)
return get_effective_ui_settings(db)