File size: 4,447 Bytes
6210d3a e3cd6e2 5e44686 35454f8 91299cb 6210d3a 1bf457e 358dfff 6210d3a 358dfff b04c4ca 358dfff 6210d3a 358dfff 2fba0ae a074596 e3cd6e2 35454f8 1bf457e 358dfff 1bf457e b04c4ca 358dfff 1bf457e 358dfff 6210d3a 358dfff e3cd6e2 35454f8 6210d3a 35454f8 e3cd6e2 5e44686 e3cd6e2 40a8d66 358dfff e3cd6e2 33c29a9 e3cd6e2 91299cb e3cd6e2 33c29a9 e3cd6e2 91299cb e3cd6e2 33c29a9 e3cd6e2 33c29a9 2fba0ae 33c29a9 2fba0ae 33c29a9 e3cd6e2 91299cb 33c29a9 35454f8 6210d3a 35454f8 e3cd6e2 358dfff e3cd6e2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | 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",
)
|