import sys import os sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 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 app.routes.predict import router as predict_router from app.routes.weather import router as weather_router from app.routes.holiday import router as holiday_router from app.routes.city import router as city_router from app.models.loader import ModelLoader @asynccontextmanager async def lifespan(app: FastAPI): print("🔄 Loading ML model...") ModelLoader.get_model() ModelLoader.get_feature_columns() print("✅ CrowdRoute API is ready!") yield print("🛑 Shutting down...") app = FastAPI( title="🚌 CrowdRoute API", description="Predict crowd levels across public transport options", version="1.0.0", lifespan=lifespan ) app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # ── API Routes ──────────────────────────────────────────── app.include_router(predict_router, prefix="/api/v1", tags=["Predictions"]) app.include_router(weather_router, prefix="/api/v1", tags=["Weather"]) app.include_router(holiday_router, prefix="/api/v1", tags=["Holiday"]) app.include_router(city_router, prefix="/api/v1", tags=["City"]) # ── Serve React Frontend ────────────────────────────────── FRONTEND_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "static") ASSETS_DIR = os.path.join(FRONTEND_DIR, "assets") INDEX_FILE = os.path.join(FRONTEND_DIR, "index.html") # Only mount static files if they exist if os.path.exists(ASSETS_DIR): app.mount("/assets", StaticFiles(directory=ASSETS_DIR), name="assets") print(f"✅ Serving frontend from: {FRONTEND_DIR}") else: print(f"⚠️ No frontend found at {FRONTEND_DIR} — API only mode") @app.get("/") async def root(): if os.path.exists(INDEX_FILE): return FileResponse(INDEX_FILE) return { "message": "🚌 CrowdRoute API", "docs": "/docs", "health": "/api/v1/health", "version": "1.0.0" } @app.get("/{full_path:path}") async def catch_all(full_path: str): # Don't intercept API routes if full_path.startswith("api/") or full_path.startswith("docs") or full_path.startswith("openapi"): from fastapi import HTTPException raise HTTPException(status_code=404, detail="Not found") # Serve React app for all other routes if os.path.exists(INDEX_FILE): return FileResponse(INDEX_FILE) return {"message": "🚌 CrowdRoute API — Frontend not deployed yet"}