Update src/api/admin.py
Browse files- src/api/admin.py +68 -0
src/api/admin.py
CHANGED
|
@@ -715,3 +715,71 @@ async def admin_analytics_recent(request: Request):
|
|
| 715 |
_require_session(request)
|
| 716 |
return await _worker_get("/analytics/recent")
|
| 717 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 715 |
_require_session(request)
|
| 716 |
return await _worker_get("/analytics/recent")
|
| 717 |
|
| 718 |
+
@router.get("/worker-health")
|
| 719 |
+
async def admin_worker_health(request: Request):
|
| 720 |
+
"""Diagnose the backend -> Worker -> D1 chain and say plainly what is wrong.
|
| 721 |
+
|
| 722 |
+
Checks, in order: is CLOUDFLARE_WORKER_URL set; is the Worker reachable and
|
| 723 |
+
running v3.8; is CLOUDFLARE_WORKER_TOKEN set; does it authenticate against
|
| 724 |
+
the Worker's analytics surface. Returns a verdict plus the raw status codes.
|
| 725 |
+
"""
|
| 726 |
+
_require_session(request)
|
| 727 |
+
import httpx as _hx
|
| 728 |
+
from src.config import get_settings as _gs, LIBBEE_VERSION as _ver
|
| 729 |
+
settings = _gs()
|
| 730 |
+
worker = (settings.cloudflare_worker_url or "").rstrip("/")
|
| 731 |
+
token = settings.cloudflare_worker_token or ""
|
| 732 |
+
out = {
|
| 733 |
+
"backend_version": _ver,
|
| 734 |
+
"worker_url_configured": bool(worker),
|
| 735 |
+
"worker_token_configured": bool(token),
|
| 736 |
+
"checks": {},
|
| 737 |
+
"verdict": "",
|
| 738 |
+
}
|
| 739 |
+
if not worker:
|
| 740 |
+
out["verdict"] = "CLOUDFLARE_WORKER_URL is not set in the Space secrets."
|
| 741 |
+
return out
|
| 742 |
+
headers = {"Authorization": f"Bearer {token}"} if token else {}
|
| 743 |
+
try:
|
| 744 |
+
async with _hx.AsyncClient(timeout=8) as client:
|
| 745 |
+
rv = await client.get(f"{worker}/version")
|
| 746 |
+
out["checks"]["version_status"] = rv.status_code
|
| 747 |
+
try:
|
| 748 |
+
out["checks"]["worker_version"] = (rv.json() or {}).get("version")
|
| 749 |
+
except Exception:
|
| 750 |
+
out["checks"]["worker_version"] = None
|
| 751 |
+
ra = await client.get(f"{worker}/analytics", headers=headers)
|
| 752 |
+
out["checks"]["analytics_status"] = ra.status_code
|
| 753 |
+
if ra.status_code == 200:
|
| 754 |
+
try:
|
| 755 |
+
out["checks"]["total_queries_logged"] = (ra.json() or {}).get("total")
|
| 756 |
+
except Exception:
|
| 757 |
+
pass
|
| 758 |
+
except Exception as exc:
|
| 759 |
+
out["verdict"] = f"Worker unreachable: {exc}"
|
| 760 |
+
return out
|
| 761 |
+
|
| 762 |
+
wv = out["checks"].get("worker_version")
|
| 763 |
+
a = out["checks"].get("analytics_status")
|
| 764 |
+
if wv != "3.8.0":
|
| 765 |
+
out["verdict"] = (
|
| 766 |
+
"Worker is not running v3.8.0 (no /version response). Deploy "
|
| 767 |
+
"cloudflare-worker-v3.8.js in the Cloudflare dashboard."
|
| 768 |
+
)
|
| 769 |
+
elif a == 401 and not token:
|
| 770 |
+
out["verdict"] = (
|
| 771 |
+
"Worker requires a token but CLOUDFLARE_WORKER_TOKEN is not set in "
|
| 772 |
+
"the Space secrets. Add it with the same value as the Worker's "
|
| 773 |
+
"ANALYTICS_TOKEN, then restart the Space."
|
| 774 |
+
)
|
| 775 |
+
elif a == 401:
|
| 776 |
+
out["verdict"] = (
|
| 777 |
+
"CLOUDFLARE_WORKER_TOKEN does not match the Worker's ANALYTICS_TOKEN. "
|
| 778 |
+
"Re-copy the value into both places."
|
| 779 |
+
)
|
| 780 |
+
elif a == 200:
|
| 781 |
+
out["verdict"] = "OK - backend, Worker and D1 analytics are connected."
|
| 782 |
+
else:
|
| 783 |
+
out["verdict"] = f"Unexpected analytics status {a}."
|
| 784 |
+
return out
|
| 785 |
+
|