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:
+13
-5
@@ -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"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user