From a61e7dd0e790a26073f3a2f71d5d780c7e75a93b Mon Sep 17 00:00:00 2001 From: Josue Zamudio Date: Mon, 31 Aug 2026 15:23:36 +0000 Subject: [PATCH] GET /api/cdn: probe status endpoint (public, read-only) Latest state + kbps + ~1h history (20 samples). curl it to check the CDN without digging through server.log. --- server/app.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/server/app.py b/server/app.py index 25de3ad..495c295 100644 --- a/server/app.py +++ b/server/app.py @@ -25,6 +25,9 @@ from . import db, upstream, users ROOT = Path(__file__).parent.parent app = FastAPI(title="ani-web") +# latest probe result + last 20 samples (~1h at 3-min spacing), for GET /api/cdn +_CDN = {"state": "unknown", "kbps": 0.0, "ts": 0.0, "hist": []} + def _cdn_probe(): """Background probe: pull 64KB from the CDN every 3 min and log state @@ -56,18 +59,27 @@ def _cdn_probe(): if now != state: print(f"[probe] CDN {now} ({kbps:.0f} KB/s)", flush=True) state = now + _CDN.update(state=now, kbps=round(kbps), ts=time.time()) + _CDN["hist"].append({"t": time.time(), "kbps": round(kbps)}) + del _CDN["hist"][:-20] time.sleep(180) threading.Thread(target=_cdn_probe, daemon=True).start() + +@app.get("/api/cdn") +async def cdn_status(): + """CDN health (read-only, public): latest probe + ~1h of history.""" + return _CDN + # ---------- auth: multi-user, signed cookies ---------- users.migrate_legacy() # file-based legacy import; db.init() absorbs its output db.init() _SIGNER = TimestampSigner(users.get_secret()) _SESSION = "aniweb_session" _SESSION_AGE = 30 * 86400 -_PUBLIC = {("/login", "GET"), ("/login", "POST")} +_PUBLIC = {("/login", "GET"), ("/login", "POST"), ("/api/cdn", "GET")} _SECURE = os.environ.get("ANIWEB_SECURE") == "1" # set when served over HTTPS # ponytail: in-memory lockout per username, single process — 5 fails -> 15 min