Add notification service

This commit is contained in:
Urtzi Alfaro
2025-07-21 22:44:11 +02:00
parent d029630c8e
commit d9affc950a
11 changed files with 3680 additions and 57 deletions

View File

@@ -1,61 +1,186 @@
# ================================================================
# services/notification/app/main.py - COMPLETE IMPLEMENTATION
# ================================================================
"""
uLunotification Service
Notification Service Main Application
Handles email and WhatsApp notifications with full integration
"""
import structlog
from fastapi import FastAPI
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 database_manager
from shared.monitoring.logging import setup_logging
from shared.monitoring.metrics import MetricsCollector
from app.core.database import init_db
from app.api.notifications import router as notification_router
from app.services.messaging import setup_messaging, cleanup_messaging
from shared.monitoring import setup_logging, HealthChecker
from shared.monitoring.metrics import setup_metrics_early
# Setup logging
setup_logging("notification-service", "INFO")
# Setup logging first
setup_logging("notification-service", settings.LOG_LEVEL)
logger = structlog.get_logger()
# Create FastAPI app
# Global variables for lifespan access
metrics_collector = None
health_checker = None
# Create FastAPI app FIRST
app = FastAPI(
title="uLunotification Service",
description="uLunotification service for bakery forecasting",
version="1.0.0"
title="Bakery Notification Service",
description="Email and WhatsApp notification service for bakery forecasting platform",
version="1.0.0",
docs_url="/docs",
redoc_url="/redoc"
)
# Initialize metrics collector
metrics_collector = MetricsCollector("notification-service")
# Setup metrics BEFORE any middleware and BEFORE lifespan
metrics_collector = setup_metrics_early(app, "notification-service")
# CORS middleware
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Application lifespan events - NO MIDDLEWARE ADDED HERE"""
global health_checker
# Startup
logger.info("Starting Notification Service...")
try:
# Initialize database
await init_db()
logger.info("Database initialized")
# Setup messaging
await setup_messaging()
logger.info("Messaging initialized")
# Register custom metrics (metrics_collector already exists)
metrics_collector.register_counter("notifications_sent_total", "Total notifications sent", labels=["type", "status"])
metrics_collector.register_counter("emails_sent_total", "Total emails sent", labels=["status"])
metrics_collector.register_counter("whatsapp_sent_total", "Total WhatsApp messages sent", labels=["status"])
metrics_collector.register_histogram("notification_processing_duration_seconds", "Time spent processing notifications")
metrics_collector.register_gauge("notification_queue_size", "Current notification queue size")
# Setup health checker
health_checker = HealthChecker("notification-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)
# Add email service health check
async def check_email_service():
try:
from app.services.email_service import EmailService
email_service = EmailService()
return await email_service.health_check()
except Exception as e:
return f"Email service error: {e}"
health_checker.add_check("email_service", check_email_service, timeout=10.0, critical=True)
# Add WhatsApp service health check
async def check_whatsapp_service():
try:
from app.services.whatsapp_service import WhatsAppService
whatsapp_service = WhatsAppService()
return await whatsapp_service.health_check()
except Exception as e:
return f"WhatsApp service error: {e}"
health_checker.add_check("whatsapp_service", check_whatsapp_service, timeout=10.0, critical=False)
# Add messaging health check
def check_messaging():
try:
# Check if messaging is properly initialized
from app.services.messaging import notification_publisher
return notification_publisher.connected if notification_publisher else False
except Exception as e:
return f"Messaging error: {e}"
health_checker.add_check("messaging", check_messaging, timeout=3.0, critical=False)
# Store health checker in app state
app.state.health_checker = health_checker
logger.info("Notification Service started successfully")
except Exception as e:
logger.error(f"Failed to start Notification Service: {e}")
raise
yield
# Shutdown
logger.info("Shutting down Notification Service...")
try:
await cleanup_messaging()
logger.info("Messaging cleanup completed")
except Exception as e:
logger.error(f"Error during messaging cleanup: {e}")
# Set lifespan AFTER metrics setup
app.router.lifespan_context = lifespan
# CORS middleware (added after metrics setup)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_origins=getattr(settings, 'CORS_ORIGINS', ["*"]),
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.on_event("startup")
async def startup_event():
"""Application startup"""
logger.info("Starting uLunotification Service")
# Create database tables
await database_manager.create_tables()
# Start metrics server
metrics_collector.start_metrics_server(8080)
logger.info("uLunotification Service started successfully")
# Include routers
app.include_router(notification_router, prefix="/api/v1", tags=["notifications"])
# Health check endpoint
@app.get("/health")
async def health_check():
"""Health check endpoint"""
return {
"status": "healthy",
"service": "notification-service",
"version": "1.0.0"
}
"""Comprehensive health check endpoint"""
if health_checker:
return await health_checker.check_health()
else:
return {
"service": "notification-service",
"status": "healthy",
"version": "1.0.0"
}
# Metrics endpoint
@app.get("/metrics")
async def metrics():
"""Prometheus metrics endpoint"""
if metrics_collector:
return metrics_collector.generate_latest()
return {"metrics": "not_available"}
# 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"}
)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
uvicorn.run(app, host="0.0.0.0", port=8000)