Hirely-Backend / backend /api_app.py
NaikPranav11's picture
changes
7ae1497
Raw
History Blame Contribute Delete
7.17 kB
"""
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"
)