"""Mantimon TCG - FastAPI Application Entry Point. This module configures and starts the FastAPI application with: - Database initialization and cleanup - Redis connection management - Card service loading - CORS middleware - API routers Usage: uvicorn app.main:app --reload """ import logging from collections.abc import AsyncGenerator from contextlib import asynccontextmanager from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from app.api.auth import router as auth_router from app.api.collections import router as collections_router from app.api.config import router as config_router from app.api.decks import router as decks_router from app.api.games import router as games_router from app.api.users import router as users_router from app.config import settings from app.db import close_db, init_db from app.db.redis import close_redis, init_redis from app.services import get_card_service from app.socketio import create_socketio_app logger = logging.getLogger(__name__) @asynccontextmanager async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]: """Application lifespan context manager. Handles startup and shutdown events: - Startup: Initialize DB, Redis, and load cards - Shutdown: Close DB and Redis connections Args: app: The FastAPI application instance. Yields: None - Control returns to the application during its lifetime. """ # === STARTUP === logger.info("Starting Mantimon TCG server...") # Initialize database connection pool logger.info("Initializing database...") await init_db() logger.info("Database initialized") # Initialize Redis connection pool logger.info("Initializing Redis...") await init_redis() logger.info("Redis initialized") # Load card definitions into memory logger.info("Loading card definitions...") card_service = get_card_service() await card_service.load_all() card_count = len(card_service.get_all_cards()) logger.info(f"Loaded {card_count} card definitions") logger.info("Mantimon TCG server started successfully") yield # Application runs here # === SHUTDOWN === logger.info("Shutting down Mantimon TCG server...") # Close Redis connections logger.info("Closing Redis connections...") await close_redis() logger.info("Redis connections closed") # Close database connections logger.info("Closing database connections...") await close_db() logger.info("Database connections closed") logger.info("Mantimon TCG server shutdown complete") # Create FastAPI application with lifespan app = FastAPI( title="Mantimon TCG", description="A home-rule-modified Pokemon Trading Card Game API", version="0.1.0", lifespan=lifespan, docs_url="/docs" if settings.is_development else None, redoc_url="/redoc" if settings.is_development else None, ) # Configure CORS app.add_middleware( CORSMiddleware, allow_origins=settings.cors_origins, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # === Health Check Endpoints === @app.get("/health") async def health_check() -> dict[str, str]: """Basic health check endpoint. Returns: Health status indicating the server is running. """ return {"status": "healthy"} @app.get("/health/ready") async def readiness_check() -> dict[str, str | int]: """Readiness check with service status. Verifies that all required services are available: - Database connection - Redis connection - Card service loaded Returns: Detailed status of each service. """ from app.db import get_engine from app.db.redis import get_pool status: dict[str, str | int] = {"status": "ready"} # Check database try: get_engine() # Raises RuntimeError if not initialized status["database"] = "connected" except RuntimeError: status["database"] = "not initialized" status["status"] = "not ready" # Check Redis try: get_pool() # Raises RuntimeError if not initialized status["redis"] = "connected" except RuntimeError: status["redis"] = "not initialized" status["status"] = "not ready" # Check card service card_service = get_card_service() card_count = len(card_service.get_all_cards()) if card_count > 0: status["cards_loaded"] = card_count else: status["cards_loaded"] = 0 status["status"] = "not ready" return status # === API Routers === app.include_router(auth_router, prefix="/api") app.include_router(config_router, prefix="/api") app.include_router(users_router, prefix="/api") app.include_router(collections_router, prefix="/api") app.include_router(decks_router, prefix="/api") app.include_router(games_router, prefix="/api") # TODO: Add remaining routers in future phases # from app.api import cards, campaign # app.include_router(cards.router, prefix="/api/cards", tags=["cards"]) # app.include_router(campaign.router, prefix="/api/campaign", tags=["campaign"]) # === Socket.IO Integration === # Wrap FastAPI with Socket.IO ASGI app for WebSocket support. # The combined app handles: # - WebSocket connections at /socket.io/ # - HTTP requests passed through to FastAPI # # Note: The module-level 'app' variable is now the combined ASGI app. # This is intentional - uvicorn imports 'app' and will serve both # FastAPI HTTP routes and Socket.IO WebSocket connections. _fastapi_app = app app = create_socketio_app(_fastapi_app)