import asyncio import hmac import os import re import threading import time from fastapi import FastAPI, Header, HTTPException from fastapi.responses import FileResponse, HTMLResponse, JSONResponse import builder app = FastAPI(title="Lesson content server") state = {"status": "idle", "log": [], "errors": {}, "finished": None, "voices": {}} lock = threading.Lock() def _log(msg): state["log"].append(msg) del state["log"][:-200] def run_build(): if not lock.acquire(blocking=False): return try: state.update(status="building", log=[], errors={}) _, errors = asyncio.run(builder.build_all(_log)) state.update(status="ready", errors=errors, voices=dict(builder.INFO), finished=time.strftime("%Y-%m-%d %H:%M:%S")) except Exception as e: # noqa: BLE001 _log(f"خطای کلی: {e}") state["status"] = "failed" finally: lock.release() @app.on_event("startup") def startup(): threading.Thread(target=run_build, daemon=True).start() def check_password(p): expected = os.getenv("REBUILD_PASSWORD", "") if expected and not hmac.compare_digest(p or "", expected): raise HTTPException(401, "رمز اشتباه است") @app.get("/manifest.json") def manifest(): f = builder.OUT / "manifest.json" if not f.exists(): return JSONResponse({"status": state["status"]}, status_code=503) return FileResponse(f, media_type="application/json", headers={"Cache-Control": "no-cache"}) @app.get("/packs/{name}") def pack(name: str): if not re.fullmatch(r"[A-Za-z0-9_.-]+\.zip", name): raise HTTPException(400, "bad name") f = builder.OUT / "packs" / name if not f.exists(): raise HTTPException(404, "not found") return FileResponse(f, media_type="application/zip") @app.get("/status") def status(): return state @app.post("/rebuild") def rebuild(x_password: str = Header(default="")): check_password(x_password) if state["status"] == "building": return {"ok": False, "msg": "ساخت در حال انجام است"} threading.Thread(target=run_build, daemon=True).start() return {"ok": True} PAGE = """ سرور درس‌ها

سرور درس‌ها

وضعیت: …

آدرس فهرست:

گزارش
""" @app.get("/", response_class=HTMLResponse) def home(): return PAGE