subul / backend /main.py
Kaadan's picture
Merge branch 'master'
1bf457e
import os
from pathlib import Path
from fastapi import FastAPI, APIRouter, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse
import logging
from contextlib import asynccontextmanager
from database.database import engine
from models import Base
from api.routes import router as root_router
from api.user_routes import router as user_router
from api.job_routes import router as job_router
from api.assessment_routes import router as assessment_router
from api.application_routes import router as application_router
from config import settings
from logging_config import get_logger
from db_seeder import seed_database
# Create logger for this module
logging.basicConfig(level=settings.log_level)
logger = logging.getLogger(__name__)
logger = get_logger(__name__)
PORT = int(os.environ.get("PORT", 7860))
HOST = os.environ.get("HOST", "0.0.0.0")
DEBUG = os.getenv("DEBUG", str(settings.debug)).lower() == "true"
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Handle startup and shutdown events"""
# Startup
logger.info(f"Starting {settings.app_name} v{settings.app_version}")
logger.info(f"Database URL: {settings.database_url}")
# Seed the database if it's empty
logger.info("Checking if database needs seeding...")
seed_database()
logger.info("Database seeding check completed")
logger.info("Application started successfully")
yield
# Shutdown
logger.info("Application shutting down")
# Initialize FastAPI app with settings
app = FastAPI(
title=settings.app_name,
description=settings.app_description,
version=settings.app_version
)
# ----------------------------
# Create database tables on startup
# ----------------------------
@app.on_event("startup")
def startup_event():
Base.metadata.create_all(bind=engine)
logger.info(f"Database tables created/verified at {settings.database_url}")
# ----------------------------
# Configure CORS
# ----------------------------
origins = os.getenv("CORS_ORIGINS", "http://localhost:5173,http://localhost:3000").split(",")
app.add_middleware(
CORSMiddleware,
allow_origins=[o.strip() for o in origins if o.strip()],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# ----------------------------
# API routers under /api
# ----------------------------
api_router = APIRouter(prefix="/api")
api_router.include_router(root_router)
api_router.include_router(user_router)
api_router.include_router(job_router)
api_router.include_router(assessment_router)
api_router.include_router(application_router)
app.include_router(api_router)
react_build_path = Path("static")
if react_build_path.exists():
# Serve actual static files (JS/CSS/images) under /static
app.mount("/static", StaticFiles(directory=react_build_path / "assets"), name="static_assets")
else:
logger.warning("React build not found in /static. Make sure to build frontend.")
# ----------------------------
# SPA fallback for non-API routes (this will handle all non-API routes)
# NOTE: This must be defined LAST to avoid catching other routes
# ----------------------------
@app.get("/{full_path:path}")
async def serve_spa(full_path: str, request: Request):
"""
Serve React SPA for any non-API path that doesn't match a real file.
This handles client-side routing for React Router.
"""
# If it's an API route, return 404 since we already handled API routes separately
if request.url.path.startswith("/api"):
from fastapi.responses import JSONResponse
return JSONResponse({"detail": "API route not found."}, status_code=404)
# For all other routes, serve the SPA index.html so React Router can handle it
index_file = react_build_path / "index.html"
if index_file.exists():
return FileResponse(index_file)
from fastapi.responses import JSONResponse
return JSONResponse({"detail": "SPA not found, build your frontend first."}, status_code=500)
logger.info("Application routes registered")
# ----------------------------
# Run server (local/dev)
# ----------------------------
if __name__ == "__main__":
import uvicorn
logger.info(f"Starting server on {HOST}:{PORT}")
uvicorn.run(
"main:app",
host=HOST,
port=PORT,
log_level="info",
workers=1,
loop="asyncio",
)