import asyncio import threading from contextlib import asynccontextmanager from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from app.routers import analytics, papers, attempts, questions def _resume_stale_papers(): """启动时检查卡在 processing 的 paper,自动续传 AI trio""" try: from app.services.supabase_client import get_supabase from app.services.paper_processor import process_paper sb = get_supabase() stale = sb.table("papers").select("id").eq("status", "processing").execute().data if not stale: return for p in stale: paper_id = p["id"] print(f"[STARTUP] Resuming processing for paper {paper_id[:8]}...") def run(pid=paper_id): asyncio.run(process_paper(pid, b"", None)) threading.Thread(target=run, daemon=True).start() except Exception as e: print(f"[STARTUP] Resume skipped: {e}") @asynccontextmanager async def lifespan(app: FastAPI): # Startup _resume_stale_papers() yield # Shutdown (nothing to do) app = FastAPI(title="PastPaper Master API", version="0.1.0", lifespan=lifespan) app.add_middleware( CORSMiddleware, allow_origins=["*"], # 开发阶段先放开,上线收紧 allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) app.include_router(papers.router, prefix="/api/papers", tags=["papers"]) app.include_router(attempts.router, prefix="/api/attempts", tags=["attempts"]) app.include_router(questions.router, prefix="/api/questions", tags=["questions"]) app.include_router(analytics.router, prefix="/api/analytics", tags=["analytics"]) @app.get("/health") def health(): return {"status": "ok"}