feat(admin): backend dashboard pour #71

- backend/admin.py : module FastAPI avec 4 endpoints admin-gated
  - GET /api/admin/stats (CPU/RAM/Disk/Uptime via psutil)
  - GET /api/admin/audit (filtres user/action/limit/offset)
  - GET /api/admin/backup-stats (count + size + age par vault)
  - GET /api/admin/stream (Server-Sent Events 5s)
- backend/main.py : routeur monté + middleware gzip bypass pour SSE
- backend/requirements.txt : ajout psutil>=5.9
- tests/test_admin.py : 13 tests (auth + filtres + format SSE), 100% verts

Pas de frontend dans cette PR — page admin.html + admin.js restent à faire.
This commit is contained in:
2026-09-07 08:54:25 -04:00
parent 99c9acccb0
commit b649c6b301
4 changed files with 581 additions and 5 deletions
+13 -5
View File
@@ -662,11 +662,11 @@ class SSESafeGZipMiddleware(GZipMiddleware):
We detect SSE endpoints by path and bypass compression entirely.
"""
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
if scope["type"] == "http" and scope.get("path") == "/api/events":
# Bypass GZip: passthrough directly to the inner app
await self.app(scope, receive, send)
else:
await super().__call__(scope, receive, send)
if scope["type"] == "http" and scope.get("path") in ("/api/events", "/api/admin/stream"):
# Bypass GZip: passthrough directly to the inner app
await self.app(scope, receive, send)
else:
await super().__call__(scope, receive, send)
app.add_middleware(SSESafeGZipMiddleware, minimum_size=1000)
@@ -717,6 +717,14 @@ app.include_router(auth_router)
app.include_router(ai_router)
app.include_router(bookslm_router)
# Admin Dashboard endpoints (system stats, audit logs, backups, stream)
try:
from backend.admin import router as admin_router
app.include_router(admin_router)
logger.info("Admin dashboard router mounted at /api/admin/*")
except ImportError as e:
logger.warning(f"Could not load admin dashboard router: {e}")
# Resolve frontend path relative to this file
FRONTEND_DIR = Path(__file__).resolve().parent.parent / "frontend"