from contextlib import asynccontextmanager from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles from fastapi.responses import FileResponse from pathlib import Path import os from backend.core.database import init_db from backend.api.api import api_router @asynccontextmanager async def lifespan(app: FastAPI): # Startup try: await init_db() except Exception as e: print(f"WARNING: Database initialization failed. Running in MOCK mode. Error: {e}") yield # Shutdown app = FastAPI( title="GeoQuery API", description="Geospatial Analysis Agent API", version="0.1.0", lifespan=lifespan ) app.add_middleware( CORSMiddleware, allow_origins=["*"], # Allow all for dev allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) app.include_router(api_router, prefix="/api/v1") # Serve static files (Frontend) static_dir = Path(__file__).parent / "static" if static_dir.exists(): app.mount("/_next", StaticFiles(directory=static_dir / "_next"), name="next") # app.mount("/assets", StaticFiles(directory=static_dir / "assets"), name="assets") # Standard Next.js Output might not use this top-level @app.get("/{full_path:path}") async def serve_frontend(full_path: str): # API requests are already handled by include_router above (because specific routes take precedence? No, order matters). # Wait, explicit routes define earlier take precedence. # But include_router adds routes. # A catch-all route /{full_path:path} will capture everything NOT matched by previous routes. # Since api_router is included first (implicitly? No, verify order). # FastAPI router priority: First declared wins. # So app.include_router MUST be before this catch-all. It is. file_path = static_dir / full_path if file_path.exists() and file_path.is_file(): return FileResponse(file_path) # Fallback to index.html for SPA routing index_path = static_dir / "index.html" if index_path.exists(): return FileResponse(index_path) return {"error": "Frontend not found"} else: @app.get("/") def read_root(): return {"message": "GeoQuery API is running (Frontend not built)"}