import os from contextlib import asynccontextmanager from fastapi import FastAPI from fastapi.staticfiles import StaticFiles from fastapi.responses import FileResponse, JSONResponse from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.gzip import GZipMiddleware from backend.database import init_db @asynccontextmanager async def lifespan(app: FastAPI): init_db() yield app = FastAPI( title="Compost API", description="Unified AI Tool Integration Platform", version="1.0.0", docs_url="/api/docs", redoc_url="/api/redoc", lifespan=lifespan ) app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) app.add_middleware(GZipMiddleware, minimum_size=1000) from backend.routers import auth, apps, tools, connections, apikeys, analytics, playground, settings, mcp, auth_configs app.include_router(auth.router, prefix="/api/auth", tags=["auth"]) app.include_router(apps.router, prefix="/api/apps", tags=["apps"]) app.include_router(tools.router, prefix="/api/tools", tags=["tools"]) app.include_router(connections.router, prefix="/api/connections", tags=["connections"]) app.include_router(apikeys.router, prefix="/api/apikeys", tags=["apikeys"]) app.include_router(analytics.router, prefix="/api/analytics", tags=["analytics"]) app.include_router(playground.router, prefix="/api/playground", tags=["playground"]) app.include_router(settings.router, prefix="/api/settings", tags=["settings"]) app.include_router(mcp.router, prefix="/mcp", tags=["mcp"]) app.include_router(auth_configs.router, prefix="/api/auth-configs", tags=["auth-configs"]) @app.get("/health") async def health(): return {"status": "healthy", "version": "1.0.0"} STATIC_DIR = os.path.join(os.path.dirname(__file__), "static") if os.path.exists(STATIC_DIR): app.mount("/_next", StaticFiles(directory=f"{STATIC_DIR}/_next"), name="next-assets") @app.get("/{full_path:path}") async def serve_spa(full_path: str): if full_path.startswith("api/") or full_path.startswith("mcp/") or full_path == "health": return JSONResponse({"detail": "Not found"}, status_code=404) static_file = os.path.join(STATIC_DIR, full_path) if os.path.isfile(static_file): return FileResponse(static_file) return FileResponse(os.path.join(STATIC_DIR, "index.html")) @app.get("/") async def root(): return FileResponse(os.path.join(STATIC_DIR, "index.html"))