Spaces:
Runtime error
Runtime error
| """ | |
| 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) | |
| 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 | |
| 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 | |
| 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 | |
| async def health_check(): | |
| """Health check endpoint""" | |
| return { | |
| "status": "healthy", | |
| "service": "hirely-api", | |
| "version": "1.0.0" | |
| } | |
| # Root endpoint | |
| 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 | |
| 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" | |
| ) |