""" Main FastAPI application for Hirely API """ import os from fastapi import FastAPI, HTTPException, Request from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse from fastapi.staticfiles import StaticFiles import uvicorn from contextlib import asynccontextmanager # Import API endpoints from api.endpoints import auth, jobs, candidates, interviews, dashboard, tests, candidate_tests, reports, ai_interview, questions, rag, enhanced_features, monitoring from models import create_tables # Create uploads directory os.makedirs("uploads", exist_ok=True) os.makedirs("uploads/resumes", exist_ok=True) @asynccontextmanager async def lifespan(app: FastAPI): # Startup: Create database tables create_tables() print("✅ Database tables created/verified") # Initialize real-time interview WebSocket handlers try: from api.websocket_manager import manager from services.realtime_transcription_service import RealtimeTranscriptionService from api.websocket_handlers.realtime_interview import register_handlers from models.database import SessionLocal # Create transcription service instance transcription_service = RealtimeTranscriptionService( websocket_manager=manager, whisper_model_size="base", db_session_factory=SessionLocal ) # Register handlers register_handlers(manager, transcription_service) print("✅ Real-time interview WebSocket handlers registered") except Exception as e: print(f"⚠️ Warning: Could not register real-time interview handlers: {e}") yield # Shutdown: cleanup if needed print("🔄 API server shutting down") # Create FastAPI app app = FastAPI( title="Hirely API", description="AI-Driven Candidate Interview and Screening Platform API", version="1.0.0", docs_url="/docs", redoc_url="/redoc", lifespan=lifespan ) # CORS middleware app.add_middleware( CORSMiddleware, allow_origins=["*"], # Allow all origins for deployment flexibility allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Mount static files for uploads app.mount("/uploads", StaticFiles(directory="uploads"), name="uploads") # Global exception handler @app.exception_handler(Exception) async def global_exception_handler(request: Request, exc: Exception): import traceback error_detail = str(exc) traceback_str = ''.join(traceback.format_tb(exc.__traceback__)) # Print to console for debugging print(f"❌ ERROR: {error_detail}") print(f"Traceback:\n{traceback_str}") return JSONResponse( status_code=500, content={ "success": False, "error": "Internal server error", "detail": error_detail, "traceback": traceback_str if os.getenv("DEBUG") == "true" else None } ) # Add validation exception handler for better error messages from fastapi.exceptions import RequestValidationError @app.exception_handler(RequestValidationError) async def validation_exception_handler(request: Request, exc: RequestValidationError): print("=" * 80) print("❌ VALIDATION ERROR") print("=" * 80) print(f"Request URL: {request.url}") print(f"Request method: {request.method}") print(f"Errors: {exc.errors()}") print(f"Body: {exc.body}") print("=" * 80) return JSONResponse( status_code=422, content={ "detail": exc.errors(), "body": str(exc.body) } ) # Health check endpoint @app.get("/health") async def health_check(): """Health check endpoint""" return { "status": "healthy", "service": "hirely-api", "version": "1.0.0" } # Root endpoint @app.get("/") async def root(): """Root endpoint""" return { "message": "Welcome to Hirely API", "version": "1.0.0", "docs": "/docs", "health": "/health" } # Include API routers app.include_router(auth.router, prefix="/api/v1") app.include_router(jobs.router, prefix="/api/v1") app.include_router(candidates.router, prefix="/api/v1") app.include_router(interviews.router, prefix="/api/v1") app.include_router(dashboard.router, prefix="/api/v1") app.include_router(tests.router, prefix="/api/v1") app.include_router(candidate_tests.router, prefix="/api/v1") # Public candidate test endpoints app.include_router(reports.router, prefix="/api/v1") # Reports endpoints app.include_router(ai_interview.router, prefix="/api/v1") # AI Interview WebSocket endpoints app.include_router(questions.router, prefix="/api/v1") # Questions management endpoints app.include_router(rag.router, prefix="/api/v1") # RAG endpoints for intelligent question generation app.include_router(enhanced_features.router, prefix="/api/v1") # Enhanced AI interview features endpoints app.include_router(monitoring.router, prefix="/api/v1") # Real-time interview monitoring endpoints # Additional utility endpoints @app.get("/api/v1/enums") async def get_enums(): """Get all enum values for frontend""" from models import UserRole, UserStatus, JobStatus, JobType, ExperienceLevel from models import CandidateStatus, CandidateSource, InterviewType, InterviewStatus from models import TestType, TestStatus, TestResultStatus try: return { "user_roles": [role.value if hasattr(role, 'value') else str(role) for role in UserRole], "user_statuses": [status.value if hasattr(status, 'value') else str(status) for status in UserStatus], "job_statuses": [status.value if hasattr(status, 'value') else str(status) for status in JobStatus], "job_types": [type_.value if hasattr(type_, 'value') else str(type_) for type_ in JobType], "experience_levels": [level.value if hasattr(level, 'value') else str(level) for level in ExperienceLevel], "candidate_statuses": [status.value if hasattr(status, 'value') else str(status) for status in CandidateStatus], "candidate_sources": [source.value if hasattr(source, 'value') else str(source) for source in CandidateSource], "interview_types": [type_.value if hasattr(type_, 'value') else str(type_) for type_ in InterviewType], "interview_statuses": [status.value if hasattr(status, 'value') else str(status) for status in InterviewStatus], "test_types": [type_.value if hasattr(type_, 'value') else str(type_) for type in TestType], "test_statuses": [status.value if hasattr(status, 'value') else str(status) for status in TestStatus], "test_result_statuses": [status.value if hasattr(status, 'value') else str(status) for status in TestResultStatus] } except Exception as e: print(f"Error in get_enums: {str(e)}") import traceback traceback.print_exc() raise if __name__ == "__main__": # Run the API server uvicorn.run( "api_app:app", host="0.0.0.0", port=8000, reload=True, log_level="info" )