Add usage stats script and /api/admin/stats endpoint
scripts/usage_stats.py: standalone script (PEP 723, runs via uv run) that parses all nginx access.log files, filters bots, maps Referer headers to feature labels, and produces a 3-panel matplotlib figure: daily logins + 7-day rolling mean, hour×weekday API heatmap, and weekly feature usage stacked area. Output saved to /var/bincio/stats/latest.png. Intended for a weekly cron job. bincio/serve/routers/admin.py: GET /api/admin/stats serves the PNG via the existing _require_admin() check — no new auth logic or nginx changes needed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -10,7 +10,7 @@ from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from fastapi import APIRouter, Cookie, HTTPException, Request
|
||||
from fastapi.responses import JSONResponse, StreamingResponse
|
||||
from fastapi.responses import FileResponse, JSONResponse, StreamingResponse
|
||||
|
||||
from bincio.serve import deps, tasks
|
||||
from bincio.serve.models import ResetPasswordCodeResponse
|
||||
@@ -58,6 +58,16 @@ def _wipe_user_activities(user_dir: Path) -> int:
|
||||
return deleted
|
||||
|
||||
|
||||
@router.get("/api/admin/stats")
|
||||
async def admin_stats(bincio_session: str | None = Cookie(default=None)) -> FileResponse:
|
||||
"""Serve the latest usage stats figure. Admin only."""
|
||||
deps._require_admin(bincio_session)
|
||||
path = deps._get_data_dir().parent / "stats" / "latest.png"
|
||||
if not path.exists():
|
||||
raise HTTPException(404, "Stats not yet generated — run scripts/usage_stats.py first")
|
||||
return FileResponse(path, media_type="image/png", headers={"Cache-Control": "no-cache, no-store"})
|
||||
|
||||
|
||||
@router.get("/api/admin/users")
|
||||
async def admin_users(bincio_session: str | None = Cookie(default=None)) -> JSONResponse:
|
||||
deps._require_admin(bincio_session)
|
||||
|
||||
Reference in New Issue
Block a user