from contextlib import asynccontextmanager from pathlib import Path from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles from sqlalchemy import select from app.api.v1.router import api_router from app.auth.api_key import hash_api_key from app.config import get_settings from app.database import SessionLocal from app.models import ApiKey ROOT = Path(__file__).resolve().parents[2] def bootstrap_api_key() -> None: settings = get_settings() if not settings.sac_bootstrap_api_key: return db = SessionLocal() try: key_hash = hash_api_key(settings.sac_bootstrap_api_key) exists = db.scalar(select(ApiKey).where(ApiKey.key_hash == key_hash)) if exists: return prefix = settings.sac_bootstrap_api_key[:12] db.add( ApiKey( name="bootstrap", key_prefix=prefix, key_hash=key_hash, is_active=True, ) ) db.commit() finally: db.close() @asynccontextmanager async def lifespan(_app: FastAPI): bootstrap_api_key() yield def create_app() -> FastAPI: settings = get_settings() app = FastAPI( title="Security Alert Center", version="0.1.0", lifespan=lifespan, ) origins = [o.strip() for o in settings.cors_origins.split(",") if o.strip()] app.add_middleware( CORSMiddleware, allow_origins=origins if origins != ["*"] else ["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) from app.api.v1.health import router as health_router app.include_router(api_router, prefix="/api/v1") app.include_router(health_router) frontend_dist = ROOT / "frontend" / "dist" if frontend_dist.is_dir(): app.mount("/", StaticFiles(directory=str(frontend_dist), html=True), name="ui") return app app = create_app()