Spaces:
Sleeping
Sleeping
| import asyncio | |
| import logging | |
| import sys | |
| from dotenv import load_dotenv | |
| load_dotenv() | |
| logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(name)s] %(levelname)s: %(message)s") | |
| log = logging.getLogger("app") | |
| # psycopg (used by LangGraph checkpointer) requires SelectorEventLoop on Windows | |
| if sys.platform == "win32": | |
| asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) | |
| from contextlib import asynccontextmanager | |
| from fastapi import FastAPI | |
| from backend.checkpointer import init_checkpointer | |
| from backend.db.connection import init_db_pool | |
| from backend.graph.graph import build_graph | |
| from backend.routers import auth, batches, instructor, interview, sessions, student, topics, upload | |
| async def lifespan(app: FastAPI): | |
| log.info("Starting up β connecting to DB...") | |
| await init_db_pool() | |
| log.info("DB pool ready") | |
| checkpointer = await init_checkpointer() | |
| log.info("LangGraph checkpointer ready") | |
| app.state.graph = build_graph(checkpointer) | |
| log.info("Interview graph compiled") | |
| yield | |
| log.info("Shutting down") | |
| app = FastAPI(lifespan=lifespan) | |
| app.include_router(auth.router, prefix="/api/auth") | |
| app.include_router(batches.router, prefix="/api/batches") | |
| app.include_router(topics.router, prefix="/api/topics") | |
| app.include_router(upload.router, prefix="/api/upload") | |
| app.include_router(student.router, prefix="/api/student") | |
| app.include_router(sessions.router, prefix="/api/sessions") | |
| app.include_router(instructor.router, prefix="/api/instructor") | |
| app.include_router(interview.router, prefix="/interview") | |
| # React static build β MUST be last (serves the SPA for all non-API routes) | |
| from pathlib import Path | |
| from fastapi.staticfiles import StaticFiles | |
| from fastapi.responses import FileResponse | |
| _dist = Path(__file__).resolve().parent.parent / "frontend" / "dist" | |
| if _dist.is_dir(): | |
| # Catch-all for React Router β serve index.html for any path that isn't a real file | |
| async def spa_fallback(full_path: str): | |
| file = _dist / full_path | |
| if file.is_file(): | |
| return FileResponse(str(file)) | |
| return FileResponse(str(_dist / "index.html")) | |
| app.mount("/", StaticFiles(directory=str(_dist)), name="static") | |
| log.info("Static files mounted from %s", _dist) | |
| else: | |
| log.warning("frontend/dist not found at %s β SPA will not be served", _dist) | |