"""Ten31Portal FastAPI application.""" import os from contextlib import asynccontextmanager from pathlib import Path from fastapi import FastAPI from fastapi.staticfiles import StaticFiles from starlette.middleware.sessions import SessionMiddleware from starlette.responses import FileResponse from ten31portal.config import SESSION_SECRET from ten31portal.db_init import run_migrations from ten31portal.routers.auth_router import router as auth_router from ten31portal.routers.audit_router import router as audit_router from ten31portal.routers.entity_router import router as entity_router from ten31portal.routers.holding_router import router as holding_router from ten31portal.routers.position_router import router as position_router from ten31portal.routers.round_router import router as round_router from ten31portal.routers.import_router import router as import_router @asynccontextmanager async def lifespan(app: FastAPI): run_migrations() yield app = FastAPI(title="Ten31Portal", version="0.1.0", lifespan=lifespan) app.add_middleware(SessionMiddleware, secret_key=SESSION_SECRET) app.include_router(auth_router) app.include_router(audit_router) app.include_router(entity_router) app.include_router(holding_router) app.include_router(position_router) app.include_router(round_router) app.include_router(import_router) @app.get("/api/health") def health() -> dict[str, str]: return {"status": "ok"} # Serve built frontend in production (when static/ dir exists next to the app) _static_dir = Path(__file__).resolve().parent.parent / "static" if _static_dir.is_dir(): @app.get("/{path:path}") async def serve_spa(path: str): file = _static_dir / path if file.is_file(): return FileResponse(file) return FileResponse(_static_dir / "index.html") def cli() -> None: import uvicorn uvicorn.run("ten31portal.main:app", host="0.0.0.0", port=8000, reload=True) if __name__ == "__main__": cli()