Files
bakery-ia/services/data/app/main.py
2025-07-18 12:34:28 +02:00

142 lines
5.3 KiB
Python

# ================================================================
# services/data/app/main.py - FIXED VERSION
# ================================================================
"""
Data Service Main Application - Fixed middleware issue
"""
import logging
from contextlib import asynccontextmanager
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from app.core.config import settings
from app.core.database import init_db
from app.api.sales import router as sales_router
from app.api.weather import router as weather_router
from app.api.traffic import router as traffic_router
from shared.monitoring import setup_logging, HealthChecker
from shared.monitoring.metrics import setup_metrics_early
# Setup logging first
setup_logging("data-service", settings.LOG_LEVEL)
logger = logging.getLogger(__name__)
# Global variables for lifespan access
metrics_collector = None
health_checker = None
# Create FastAPI app FIRST
app = FastAPI(
title="Bakery Data Service",
description="External data integration service for weather, traffic, and sales data",
version="1.0.0"
)
# Setup metrics BEFORE any middleware and BEFORE lifespan
metrics_collector = setup_metrics_early(app, "data-service")
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Application lifespan events - NO MIDDLEWARE ADDED HERE"""
global health_checker
# Startup
logger.info("Starting Data Service...")
try:
# Initialize database
await init_db()
logger.info("Database initialized")
# Register custom metrics (metrics_collector already exists)
metrics_collector.register_counter("sales_records_created_total", "Total sales records created")
metrics_collector.register_counter("sales_queries_total", "Sales record queries")
metrics_collector.register_counter("weather_api_calls_total", "Weather API calls")
metrics_collector.register_counter("weather_api_success_total", "Successful weather API calls")
metrics_collector.register_counter("weather_api_failures_total", "Failed weather API calls")
metrics_collector.register_counter("traffic_api_calls_total", "Traffic API calls")
metrics_collector.register_counter("import_jobs_total", "Data import jobs")
metrics_collector.register_counter("template_downloads_total", "Template downloads")
metrics_collector.register_counter("errors_total", "Total errors")
metrics_collector.register_histogram("sales_create_duration_seconds", "Sales record creation duration")
metrics_collector.register_histogram("sales_list_duration_seconds", "Sales record list duration")
metrics_collector.register_histogram("import_duration_seconds", "Data import duration")
metrics_collector.register_histogram("weather_current_duration_seconds", "Current weather API duration")
metrics_collector.register_histogram("weather_forecast_duration_seconds", "Weather forecast API duration")
metrics_collector.register_histogram("external_api_duration_seconds", "External API call duration")
# Setup health checker
health_checker = HealthChecker("data-service")
# Add database health check
async def check_database():
try:
from app.core.database import get_db
async for db in get_db():
await db.execute("SELECT 1")
return True
except Exception as e:
return f"Database error: {e}"
health_checker.add_check("database", check_database, timeout=5.0, critical=True)
# Store health checker in app state
app.state.health_checker = health_checker
logger.info("Data Service started successfully")
except Exception as e:
logger.error(f"Failed to start Data Service: {e}")
raise
yield
# Shutdown
logger.info("Shutting down Data Service...")
# Set lifespan AFTER metrics setup
app.router.lifespan_context = lifespan
# CORS middleware (added after metrics setup)
app.add_middleware(
CORSMiddleware,
allow_origins=getattr(settings, 'CORS_ORIGINS', ["*"]),
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Include routers
app.include_router(sales_router, prefix="/api/v1/sales", tags=["sales"])
app.include_router(weather_router, prefix="/api/v1/weather", tags=["weather"])
app.include_router(traffic_router, prefix="/api/v1/traffic", tags=["traffic"])
# Health check endpoint
@app.get("/health")
async def health_check():
"""Comprehensive health check endpoint"""
if health_checker:
return await health_checker.check_health()
else:
return {
"service": "data-service",
"status": "healthy",
"version": "1.0.0"
}
# Exception handlers
@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):
"""Global exception handler with metrics"""
logger.error(f"Unhandled exception: {exc}", exc_info=True)
# Record error metric if available
if metrics_collector:
metrics_collector.increment_counter("errors_total", labels={"type": "unhandled"})
return JSONResponse(
status_code=500,
content={"detail": "Internal server error"}
)