Spaces:
Sleeping
Sleeping
Corrected logs extraction
Browse files
main.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
| 1 |
import os, asyncio, httpx
|
| 2 |
-
from urllib.parse import urlparse
|
| 3 |
from fastapi import FastAPI, Request, HTTPException, UploadFile, File, Form
|
| 4 |
from fastapi.responses import JSONResponse, Response, StreamingResponse
|
| 5 |
from fastapi.middleware.cors import CORSMiddleware
|
|
@@ -491,86 +490,16 @@ def _require_admin_token(request: Request) -> None:
|
|
| 491 |
raise HTTPException(status_code=401, detail="Invalid or missing X-Admin-Pass.")
|
| 492 |
|
| 493 |
|
| 494 |
-
|
| 495 |
-
|
| 496 |
-
|
| 497 |
-
|
| 498 |
-
|
| 499 |
-
|
| 500 |
-
base = host.replace(".hf.space", "").split(".")[0]
|
| 501 |
-
parts = base.split("-")
|
| 502 |
-
if len(parts) >= 4:
|
| 503 |
-
return f"{parts[0]}-{parts[1]}/{parts[2]}-{parts[3]}"
|
| 504 |
-
if len(parts) >= 2:
|
| 505 |
-
return f"{parts[0]}/{parts[1]}"
|
| 506 |
-
except Exception:
|
| 507 |
-
pass
|
| 508 |
-
return None
|
| 509 |
-
|
| 510 |
-
|
| 511 |
-
def _resolve_space_repo(space: str) -> str:
|
| 512 |
-
"""Resolve space key to HF repo (org/space). space must be 'upstream'|'demo'|'self'. Uses existing URLs, no names in code."""
|
| 513 |
-
# Optional env overrides for repo (e.g. HF_LOGS_REPO_UPSTREAM=org/space_name)
|
| 514 |
-
repo = os.getenv(f"HF_LOGS_REPO_{space.upper()}") if space else ""
|
| 515 |
-
if repo:
|
| 516 |
-
return repo.strip()
|
| 517 |
-
url_map = {"upstream": UPSTREAM_URL, "demo": DEMO_FORWARD_URL, "self": SELF_URL}
|
| 518 |
-
if not space or space not in url_map:
|
| 519 |
-
raise HTTPException(
|
| 520 |
-
status_code=400,
|
| 521 |
-
detail="Invalid or missing 'space'. Use: upstream, demo, or self.",
|
| 522 |
-
)
|
| 523 |
-
url = url_map[space]
|
| 524 |
-
if not url and space == "demo":
|
| 525 |
-
raise HTTPException(status_code=400, detail="Demo not configured (DEMO_FORWARD_URL).")
|
| 526 |
-
if not url:
|
| 527 |
-
raise HTTPException(status_code=400, detail="Missing URL for this space key.")
|
| 528 |
-
repo = _hf_space_url_to_repo(url)
|
| 529 |
-
if not repo:
|
| 530 |
-
raise HTTPException(
|
| 531 |
-
status_code=400,
|
| 532 |
-
detail=f"Could not derive repo from URL. Set HF_LOGS_REPO_{space.upper()} in env.",
|
| 533 |
-
)
|
| 534 |
-
return repo
|
| 535 |
-
|
| 536 |
-
|
| 537 |
-
async def _stream_hf_logs(log_type: str, space_repo: str):
|
| 538 |
-
"""Stream logs from HF API (run or build). space_repo e.g. DATFID-org/datfid_api.
|
| 539 |
-
Hub returns 307 redirect to canonical URL; follow_redirects=True so we get 200 + body."""
|
| 540 |
-
url = f"https://huggingface.co/api/spaces/{space_repo}/logs/{log_type}"
|
| 541 |
-
headers = {"Authorization": f"Bearer {HF_TOKEN}"}
|
| 542 |
-
client = httpx.AsyncClient(timeout=60.0, follow_redirects=True)
|
| 543 |
-
stream_ctx = client.stream("GET", url, headers=headers)
|
| 544 |
-
|
| 545 |
-
async def body_iter():
|
| 546 |
-
try:
|
| 547 |
-
async with stream_ctx as resp:
|
| 548 |
-
if resp.status_code != 200:
|
| 549 |
-
body = await resp.aread()
|
| 550 |
-
raise HTTPException(status_code=resp.status_code, detail=body[:500].decode(errors="replace"))
|
| 551 |
-
async for chunk in resp.aiter_bytes():
|
| 552 |
-
yield chunk
|
| 553 |
-
finally:
|
| 554 |
-
await client.aclose()
|
| 555 |
-
|
| 556 |
-
return StreamingResponse(body_iter(), media_type="text/plain; charset=utf-8")
|
| 557 |
-
|
| 558 |
-
|
| 559 |
-
@app.get("/admin/logs/run")
|
| 560 |
-
async def admin_logs_run(req: Request, space: str = "upstream"):
|
| 561 |
-
"""Admin only: stream run logs. Requires X-Admin-Pass header. Query: space=upstream|demo|self (keys for hf_url, DEMO_FORWARD_URL, SELF_URL)."""
|
| 562 |
_require_admin_token(req)
|
| 563 |
-
|
| 564 |
-
return await _stream_hf_logs("run", space_repo)
|
| 565 |
|
| 566 |
|
| 567 |
-
@app.get("/admin/logs/build")
|
| 568 |
-
async def admin_logs_build(req: Request, space: str = "upstream"):
|
| 569 |
-
"""Admin only: stream build logs. Requires X-Admin-Pass header. Query: space=upstream|demo|self."""
|
| 570 |
-
_require_admin_token(req)
|
| 571 |
-
space_repo = _resolve_space_repo(space)
|
| 572 |
-
return await _stream_hf_logs("build", space_repo)
|
| 573 |
-
|
| 574 |
@app.post("/modelfit/")
|
| 575 |
async def modelfit(req: Request):
|
| 576 |
user_token = _extract_user_token(req)
|
|
|
|
| 1 |
import os, asyncio, httpx
|
|
|
|
| 2 |
from fastapi import FastAPI, Request, HTTPException, UploadFile, File, Form
|
| 3 |
from fastapi.responses import JSONResponse, Response, StreamingResponse
|
| 4 |
from fastapi.middleware.cors import CORSMiddleware
|
|
|
|
| 490 |
raise HTTPException(status_code=401, detail="Invalid or missing X-Admin-Pass.")
|
| 491 |
|
| 492 |
|
| 493 |
+
@app.get("/admin/verify")
|
| 494 |
+
async def admin_verify(req: Request):
|
| 495 |
+
"""Verify DATFID token (Authorization: Bearer) and admin secret. Returns 200 + {ok: true}."""
|
| 496 |
+
user_token = _extract_user_token(req)
|
| 497 |
+
if not user_token:
|
| 498 |
+
raise HTTPException(status_code=401, detail="Missing Authorization Bearer token (dt+...).")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 499 |
_require_admin_token(req)
|
| 500 |
+
return JSONResponse(content={"ok": True})
|
|
|
|
| 501 |
|
| 502 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 503 |
@app.post("/modelfit/")
|
| 504 |
async def modelfit(req: Request):
|
| 505 |
user_token = _extract_user_token(req)
|