"""Main FastAPI application entry point.""" from contextlib import asynccontextmanager from typing import AsyncGenerator from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse from app.core.config import settings from app.core.database import close_db, init_db @asynccontextmanager async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]: """ Lifespan context manager for FastAPI application. Handles startup and shutdown events. """ # Startup print("Starting up...") if settings.DEBUG: print("Debug mode is ON") print(f"Database URL: {settings.DATABASE_URL}") print(f"Redis URL: {settings.FULL_REDIS_URL}") # Initialize database await init_db() print("Database initialized") yield # Shutdown print("Shutting down...") await close_db() print("Database connections closed") # Create FastAPI application app = FastAPI( title=settings.APP_NAME, version=settings.APP_VERSION, description="Alternative to Spotify with YouTube streaming", docs_url="/api/docs", redoc_url="/api/redoc", openapi_url="/api/openapi.json", lifespan=lifespan, ) # Configure CORS app.add_middleware( CORSMiddleware, allow_origins=settings.BACKEND_CORS_ORIGINS, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) @app.get("/") async def root() -> dict[str, str]: """Root endpoint with API information.""" return { "name": settings.APP_NAME, "version": settings.APP_VERSION, "status": "running", "docs": "/api/docs", } @app.get("/health") async def health_check() -> dict[str, str]: """Health check endpoint.""" return {"status": "healthy"} @app.get("/api/v1") async def api_v1_info() -> dict[str, str]: """API v1 information endpoint.""" return { "version": "v1", "prefix": settings.API_V1_PREFIX, "docs": "/api/docs", } # Exception handlers @app.exception_handler(Exception) async def global_exception_handler(request, exc) -> JSONResponse: """Global exception handler for unhandled exceptions.""" if settings.DEBUG: # In debug mode, return full error details return JSONResponse( status_code=500, content={ "detail": str(exc), "type": type(exc).__name__, }, ) # In production, return generic error message return JSONResponse( status_code=500, content={"detail": "Internal server error"}, ) # API routes from app.api.v1 import auth, music, playlists app.include_router(auth.router, prefix=settings.API_V1_PREFIX, tags=["authentication"]) app.include_router(music.router, prefix=settings.API_V1_PREFIX, tags=["music"]) app.include_router(playlists.router, prefix=settings.API_V1_PREFIX, tags=["playlists"]) if __name__ == "__main__": import uvicorn uvicorn.run( "app.main:app", host=settings.HOST, port=settings.PORT, reload=settings.DEBUG, log_level=settings.LOG_LEVEL.lower(), )