from __future__ import annotations from pathlib import Path from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import FileResponse from fastapi.staticfiles import StaticFiles from app.api.routes import router from app.core.config import get_settings from app.core.database import SessionLocal from app.services.bootstrap import bootstrap_database from app.services.realtime import start_realtime_services, stop_realtime_services settings = get_settings() app = FastAPI( title="Blum AI Financial Intelligence API", version=settings.app_version, description=( "Open-source AI financial intelligence backend for equities, ETFs, " "semantic news analysis, signal scoring, explainability and validation." ), ) origins = ["*"] if settings.cors_origins == "*" else [item.strip() for item in settings.cors_origins.split(",")] app.add_middleware( CORSMiddleware, allow_origins=origins, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) app.include_router(router) STATIC_DIR = Path(__file__).parent / "static" if (STATIC_DIR / "_next").exists(): app.mount("/_next", StaticFiles(directory=STATIC_DIR / "_next"), name="next-static") if STATIC_DIR.exists(): app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static") @app.on_event("startup") def startup() -> None: with SessionLocal() as db: bootstrap_database(db) start_realtime_services() @app.on_event("shutdown") def shutdown() -> None: stop_realtime_services() @app.get("/{full_path:path}", include_in_schema=False) def serve_frontend(full_path: str): if not STATIC_DIR.exists(): return {"message": "Frontend build not found. Run the Next.js build or use the API docs at /docs."} target = STATIC_DIR / full_path if target.is_file(): return FileResponse(target) html_file = target / "index.html" if html_file.is_file(): return FileResponse(html_file) return FileResponse(STATIC_DIR / "index.html")