Update src/api/admin.py
Browse files- src/api/admin.py +35 -14
src/api/admin.py
CHANGED
|
@@ -372,19 +372,24 @@ const CF_TOKEN = "{cf_token}";
|
|
| 372 |
// Two-path analytics fetch. The backend proxy keeps credentials server-side and
|
| 373 |
// is tried first; if the backend cannot reach the Worker (a known limitation on
|
| 374 |
// some hosts) we retry directly from this browser, which can.
|
|
|
|
|
|
|
|
|
|
| 375 |
async function cfFetch(path){{
|
| 376 |
-
|
| 377 |
-
|
| 378 |
-
|
| 379 |
-
|
| 380 |
-
|
| 381 |
-
|
| 382 |
-
|
| 383 |
-
|
|
|
|
|
|
|
| 384 |
const d2 = await r2.json();
|
| 385 |
-
if(!r2.ok) throw new Error('
|
| 386 |
-
if(d2.error) throw new Error(d2.error.message||
|
| 387 |
-
return {{data:d2, via:'
|
| 388 |
}}
|
| 389 |
|
| 390 |
async function loadStats(){{
|
|
@@ -721,7 +726,7 @@ async def _worker_get(path: str, params: Optional[dict] = None):
|
|
| 721 |
if settings.cloudflare_worker_token:
|
| 722 |
headers["Authorization"] = f"Bearer {settings.cloudflare_worker_token}"
|
| 723 |
try:
|
| 724 |
-
async with _mk(timeout=
|
| 725 |
r = await client.get(f"{worker}{path}", params=params or {}, headers=headers)
|
| 726 |
return r.json()
|
| 727 |
except HTTPException:
|
|
@@ -779,7 +784,7 @@ async def admin_worker_health(request: Request):
|
|
| 779 |
"""One probe, its own short timeout, never raises."""
|
| 780 |
entry = {"path": path}
|
| 781 |
try:
|
| 782 |
-
async with _mk(timeout=
|
| 783 |
r = await client.get(f"{worker}{path}",
|
| 784 |
headers=headers if use_auth else {})
|
| 785 |
entry["status"] = r.status_code
|
|
@@ -797,7 +802,23 @@ async def admin_worker_health(request: Request):
|
|
| 797 |
probe("analytics", "/analytics"),
|
| 798 |
probe("schema", "/analytics/schema"),
|
| 799 |
)
|
| 800 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 801 |
|
| 802 |
# Interpretation
|
| 803 |
if "error_type" in ver and "error_type" in out["probes"]["hours"]:
|
|
|
|
| 372 |
// Two-path analytics fetch. The backend proxy keeps credentials server-side and
|
| 373 |
// is tried first; if the backend cannot reach the Worker (a known limitation on
|
| 374 |
// some hosts) we retry directly from this browser, which can.
|
| 375 |
+
// Browser-FIRST: this browser reaches the Worker reliably and fast, whereas the
|
| 376 |
+
// host's own egress to workers.dev is unreliable. The server-side proxy is kept
|
| 377 |
+
// as a fallback for environments where the browser is the restricted one.
|
| 378 |
async function cfFetch(path){{
|
| 379 |
+
if(CF_URL){{
|
| 380 |
+
try{{
|
| 381 |
+
const h = CF_TOKEN ? {{'Authorization':'Bearer '+CF_TOKEN}} : {{}};
|
| 382 |
+
const r = await fetch(CF_URL+path, {{headers:h}});
|
| 383 |
+
const d = await r.json();
|
| 384 |
+
if(r.ok && !d.error) return {{data:d, via:'direct'}};
|
| 385 |
+
if(d.error) throw new Error(d.error.message||'Worker error');
|
| 386 |
+
}}catch(e){{ /* fall through to the backend proxy */ }}
|
| 387 |
+
}}
|
| 388 |
+
const r2 = await fetch('/admin'+path);
|
| 389 |
const d2 = await r2.json();
|
| 390 |
+
if(!r2.ok||d2.detail) throw new Error('HTTP '+r2.status+' '+(d2.detail||''));
|
| 391 |
+
if(d2.error) throw new Error(d2.error.message||JSON.stringify(d2.error));
|
| 392 |
+
return {{data:d2, via:'backend proxy'}};
|
| 393 |
}}
|
| 394 |
|
| 395 |
async function loadStats(){{
|
|
|
|
| 726 |
if settings.cloudflare_worker_token:
|
| 727 |
headers["Authorization"] = f"Bearer {settings.cloudflare_worker_token}"
|
| 728 |
try:
|
| 729 |
+
async with _mk(timeout=8) as client:
|
| 730 |
r = await client.get(f"{worker}{path}", params=params or {}, headers=headers)
|
| 731 |
return r.json()
|
| 732 |
except HTTPException:
|
|
|
|
| 784 |
"""One probe, its own short timeout, never raises."""
|
| 785 |
entry = {"path": path}
|
| 786 |
try:
|
| 787 |
+
async with _mk(timeout=8) as client:
|
| 788 |
r = await client.get(f"{worker}{path}",
|
| 789 |
headers=headers if use_auth else {})
|
| 790 |
entry["status"] = r.status_code
|
|
|
|
| 802 |
probe("analytics", "/analytics"),
|
| 803 |
probe("schema", "/analytics/schema"),
|
| 804 |
)
|
| 805 |
+
# Optional write probe: ?test_write=1 posts one marker row so a POST can be
|
| 806 |
+
# compared against the GETs above. It inserts a row tagged 'healthcheck'.
|
| 807 |
+
if request.query_params.get("test_write") == "1":
|
| 808 |
+
entry = {"path": "/log", "method": "POST"}
|
| 809 |
+
try:
|
| 810 |
+
async with _mk(timeout=10) as client:
|
| 811 |
+
r = await client.post(
|
| 812 |
+
f"{worker}/log", headers=headers,
|
| 813 |
+
json={"question": "healthcheck", "tool": "healthcheck:probe",
|
| 814 |
+
"model": "n/a", "response_time": 0, "result_count": 0},
|
| 815 |
+
)
|
| 816 |
+
entry["status"] = r.status_code
|
| 817 |
+
entry["body"] = r.text[:200]
|
| 818 |
+
except Exception as exc:
|
| 819 |
+
entry["error_type"] = type(exc).__name__
|
| 820 |
+
entry["error"] = repr(exc)[:200] or type(exc).__name__
|
| 821 |
+
out["probes"]["log_write"] = entry
|
| 822 |
|
| 823 |
# Interpretation
|
| 824 |
if "error_type" in ver and "error_type" in out["probes"]["hours"]:
|