feat: add system metrics dashboard with resource monitoring

- Add system metrics endpoint (CPU load, memory, swap, network traffic)
- Collect metrics from /proc and /sys (no subprocess required)
- Overhaul dashboard to pull from per-subsystem models
- Remove deprecated /status/all monolithic endpoint
- Improve networkd import to handle optional priority prefix
- Fix CSS duplicate .grid-4 rule and unused dashboard imports
This commit is contained in:
2026-07-15 00:24:43 +00:00
parent c21639b7f1
commit dadabd7954
11 changed files with 438 additions and 107 deletions
+35
View File
@@ -0,0 +1,35 @@
"""System metrics handler.
Returns CPU load, memory usage, and per-interface network traffic stats.
"""
from __future__ import annotations
import logging
from typing import Any
from daemon.iface import GET_SYSTEM_METRICS
from daemon.server import registry
from lib.state import state as state_store
logger = logging.getLogger(__name__)
@registry.register(GET_SYSTEM_METRICS)
def system_metrics(_request: Any, _body: Any) -> dict[str, Any]:
"""Return system-wide metrics.
Reads from pre-collected state (CPU load, memory, network traffic).
Returns:
Dict with load, memory, swap, and traffic data.
"""
sys_state = state_store.get("system")
if sys_state is None:
return {
"load": {"load1": 0.0, "load5": 0.0, "load15": 0.0},
"memory": {"total": 0, "available": 0, "used": 0, "used_pct": 0},
"swap": {"total": 0, "used": 0, "used_pct": 0},
"traffic": {},
}
return sys_state
+1 -1
View File
@@ -155,12 +155,12 @@ GET_LOGS_APP: Endpoint = _ep("GET", "/logs/app")
# ---- Server infra (not going through client) ----
GET_HEALTH: Endpoint = _ep("GET", "/health")
GET_STATUS_ALL: Endpoint = _ep("GET", "/status/all")
POST_STATUS_REFRESH: Endpoint = _ep("POST", "/status/refresh")
GET_WS: Endpoint = _ep("GET", "/ws")
POST_BATCH: Endpoint = _ep("POST", "/batch")
GET_STATUS_PENDING: Endpoint = _ep("GET", "/status/pending")
POST_STATUS_APPLY_ALL: Endpoint = _ep("POST", "/status/apply-all")
GET_SYSTEM_METRICS: Endpoint = _ep("GET", "/system/metrics")
# Collect all endpoint module-level constants for __all__ verification
_all_endpoints = [
+1 -10
View File
@@ -350,7 +350,6 @@ def create_app() -> web.Application:
"""
app = web.Application()
app.router.add_route("GET", "/health", _health)
app.router.add_route("GET", "/status/all", get_status_all)
app.router.add_route("POST", "/status/refresh", refresh_status)
app.router.add_route("POST", "/batch", _handle_batch)
app.router.add_route("GET", "/ws", _handle_ws)
@@ -462,15 +461,6 @@ async def _health(_request: web.Request) -> web.Response:
return ok({"pid": os.getpid(), "socket": str(SOCKET_PATH)})
async def get_status_all(_request: web.Request) -> web.Response:
"""Return the entire state snapshot in one call.
Returns:
JSON response containing state data for all subsystems.
"""
return ok({name: state_store.get(name) for name in state_store.SUBSYSTEMS})
async def refresh_status(_request: web.Request) -> web.Response:
"""Re-collect all state from system.
@@ -517,6 +507,7 @@ def _register_routes() -> None:
network, # noqa: F401
nginx, # noqa: F401
status, # noqa: F401
system, # noqa: F401
wireguard, # noqa: F401
)