# services/recipes/app/main.py """ Recipe Service - FastAPI application Handles recipe management, production planning, and inventory consumption tracking """ from fastapi import FastAPI, Request from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.gzip import GZipMiddleware from fastapi.responses import JSONResponse import time import logging from contextlib import asynccontextmanager from .core.config import settings from .core.database import db_manager from .api import recipes, production, ingredients # Configure logging logging.basicConfig( level=getattr(logging, settings.LOG_LEVEL.upper()), format="%(asctime)s - %(name)s - %(levelname)s - %(message)s" ) logger = logging.getLogger(__name__) @asynccontextmanager async def lifespan(app: FastAPI): """Application lifespan events""" # Startup logger.info(f"Starting {settings.SERVICE_NAME} service v{settings.SERVICE_VERSION}") # Create database tables try: db_manager.create_all_tables() logger.info("Database tables created successfully") except Exception as e: logger.error(f"Failed to create database tables: {e}") yield # Shutdown logger.info(f"Shutting down {settings.SERVICE_NAME} service") # Create FastAPI application app = FastAPI( title="Recipe Management Service", description="Comprehensive recipe management, production planning, and inventory consumption tracking for bakery operations", version=settings.SERVICE_VERSION, lifespan=lifespan, docs_url="/docs" if settings.DEBUG else None, redoc_url="/redoc" if settings.DEBUG else None, ) # Add middleware app.add_middleware( CORSMiddleware, allow_origins=settings.ALLOWED_ORIGINS, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) app.add_middleware(GZipMiddleware, minimum_size=1000) # Request timing middleware @app.middleware("http") async def add_process_time_header(request: Request, call_next): """Add processing time header to responses""" start_time = time.time() response = await call_next(request) process_time = time.time() - start_time response.headers["X-Process-Time"] = str(process_time) return response # Global exception handler @app.exception_handler(Exception) async def global_exception_handler(request: Request, exc: Exception): """Global exception handler""" logger.error(f"Global exception on {request.url}: {exc}", exc_info=True) return JSONResponse( status_code=500, content={ "detail": "Internal server error", "error": str(exc) if settings.DEBUG else "An unexpected error occurred" } ) # Health check endpoint @app.get("/health") async def health_check(): """Health check endpoint""" try: # Test database connection with db_manager.get_session() as db: db.execute("SELECT 1") return { "status": "healthy", "service": settings.SERVICE_NAME, "version": settings.SERVICE_VERSION, "environment": settings.ENVIRONMENT } except Exception as e: logger.error(f"Health check failed: {e}") return JSONResponse( status_code=503, content={ "status": "unhealthy", "service": settings.SERVICE_NAME, "version": settings.SERVICE_VERSION, "error": str(e) } ) # Include API routers app.include_router( recipes.router, prefix=f"{settings.API_V1_PREFIX}/recipes", tags=["recipes"] ) app.include_router( production.router, prefix=f"{settings.API_V1_PREFIX}/production", tags=["production"] ) app.include_router( ingredients.router, prefix=f"{settings.API_V1_PREFIX}/ingredients", tags=["ingredients"] ) @app.get("/") async def root(): """Root endpoint""" return { "service": settings.SERVICE_NAME, "version": settings.SERVICE_VERSION, "status": "running", "docs_url": "/docs" if settings.DEBUG else None } if __name__ == "__main__": import uvicorn uvicorn.run( "main:app", host="0.0.0.0", port=8000, reload=settings.DEBUG, log_level=settings.LOG_LEVEL.lower() )