Files
bakery-ia/services/notification/app/main.py

223 lines
8.4 KiB
Python
Raw Normal View History

2025-07-21 22:44:11 +02:00
# ================================================================
2025-08-23 10:19:58 +02:00
# services/notification/app/main.py - ENHANCED WITH SSE SUPPORT
2025-07-21 22:44:11 +02:00
# ================================================================
"""
2025-07-21 22:44:11 +02:00
Notification Service Main Application
2025-08-23 10:19:58 +02:00
Handles email, WhatsApp notifications and SSE for real-time alerts/recommendations
"""
2025-09-29 13:13:12 +02:00
from fastapi import FastAPI
from app.core.config import settings
2025-09-29 13:13:12 +02:00
from app.core.database import database_manager
2025-07-21 22:44:11 +02:00
from app.api.notifications import router as notification_router
2025-08-23 10:19:58 +02:00
from app.api.sse_routes import router as sse_router
2025-07-21 22:44:11 +02:00
from app.services.messaging import setup_messaging, cleanup_messaging
2025-08-23 10:19:58 +02:00
from app.services.sse_service import SSEService
from app.services.notification_orchestrator import NotificationOrchestrator
from app.services.email_service import EmailService
from app.services.whatsapp_service import WhatsAppService
2025-09-29 13:13:12 +02:00
from shared.service_base import StandardFastAPIService
2025-09-29 13:13:12 +02:00
class NotificationService(StandardFastAPIService):
"""Notification Service with standardized setup"""
def __init__(self):
# Define expected database tables for health checks
notification_expected_tables = [
'notifications', 'notification_templates', 'notification_preferences',
'notification_logs', 'email_templates', 'whatsapp_templates'
]
self.sse_service = None
self.orchestrator = None
self.email_service = None
self.whatsapp_service = None
# Define custom metrics for notification service
notification_custom_metrics = {
"notifications_sent_total": {
"type": "counter",
"description": "Total notifications sent",
"labels": ["type", "status", "channel"]
},
"emails_sent_total": {
"type": "counter",
"description": "Total emails sent",
"labels": ["status"]
},
"whatsapp_sent_total": {
"type": "counter",
"description": "Total WhatsApp messages sent",
"labels": ["status"]
},
"sse_events_sent_total": {
"type": "counter",
"description": "Total SSE events sent",
"labels": ["tenant", "event_type"]
},
"notification_processing_duration_seconds": {
"type": "histogram",
"description": "Time spent processing notifications"
}
}
# Define custom health checks for notification service components
2025-07-21 22:44:11 +02:00
async def check_email_service():
2025-09-29 13:13:12 +02:00
"""Check email service health"""
2025-07-21 22:44:11 +02:00
try:
2025-09-29 13:13:12 +02:00
return await self.email_service.health_check() if self.email_service else False
2025-07-21 22:44:11 +02:00
except Exception as e:
2025-09-29 13:13:12 +02:00
self.logger.error("Email service health check failed", error=str(e))
return False
2025-07-21 22:44:11 +02:00
async def check_whatsapp_service():
2025-09-29 13:13:12 +02:00
"""Check WhatsApp service health"""
2025-07-21 22:44:11 +02:00
try:
2025-09-29 13:13:12 +02:00
return await self.whatsapp_service.health_check() if self.whatsapp_service else False
2025-07-21 22:44:11 +02:00
except Exception as e:
2025-09-29 13:13:12 +02:00
self.logger.error("WhatsApp service health check failed", error=str(e))
return False
2025-08-23 10:19:58 +02:00
async def check_sse_service():
2025-09-29 13:13:12 +02:00
"""Check SSE service health"""
2025-08-23 10:19:58 +02:00
try:
2025-09-29 13:13:12 +02:00
if self.sse_service:
metrics = self.sse_service.get_metrics()
return bool(metrics.get("redis_connected", False))
return False
2025-08-23 10:19:58 +02:00
except Exception as e:
2025-09-29 13:13:12 +02:00
self.logger.error("SSE service health check failed", error=str(e))
return False
async def check_messaging():
"""Check messaging service health"""
2025-07-21 22:44:11 +02:00
try:
from app.services.messaging import notification_publisher
2025-09-29 13:13:12 +02:00
return bool(notification_publisher and notification_publisher.connected)
2025-07-21 22:44:11 +02:00
except Exception as e:
2025-09-29 13:13:12 +02:00
self.logger.error("Messaging health check failed", error=str(e))
return False
super().__init__(
service_name="notification-service",
app_name="Bakery Notification Service",
description="Email, WhatsApp and SSE notification service for bakery alerts and recommendations",
version="2.0.0",
log_level=settings.LOG_LEVEL,
cors_origins=getattr(settings, 'CORS_ORIGINS', ["*"]),
api_prefix="/api/v1",
database_manager=database_manager,
expected_tables=notification_expected_tables,
custom_health_checks={
"email_service": check_email_service,
"whatsapp_service": check_whatsapp_service,
"sse_service": check_sse_service,
"messaging": check_messaging
},
enable_messaging=True,
custom_metrics=notification_custom_metrics
)
async def _setup_messaging(self):
"""Setup messaging for notification service"""
await setup_messaging()
self.logger.info("Messaging initialized")
async def _cleanup_messaging(self):
"""Cleanup messaging for notification service"""
2025-07-21 22:44:11 +02:00
await cleanup_messaging()
2025-09-29 13:13:12 +02:00
async def on_startup(self, app: FastAPI):
"""Custom startup logic for notification service"""
# Initialize services
self.email_service = EmailService()
self.whatsapp_service = WhatsAppService()
# Initialize SSE service
self.sse_service = SSEService(settings.REDIS_URL)
await self.sse_service.initialize()
self.logger.info("SSE service initialized")
# Create orchestrator
self.orchestrator = NotificationOrchestrator(
email_service=self.email_service,
whatsapp_service=self.whatsapp_service,
sse_service=self.sse_service
)
# Store services in app state
app.state.orchestrator = self.orchestrator
app.state.sse_service = self.sse_service
app.state.email_service = self.email_service
app.state.whatsapp_service = self.whatsapp_service
async def on_shutdown(self, app: FastAPI):
"""Custom shutdown logic for notification service"""
# Shutdown SSE service
if self.sse_service:
await self.sse_service.shutdown()
self.logger.info("SSE service shutdown completed")
def get_service_features(self):
"""Return notification-specific features"""
return [
"email_notifications",
"whatsapp_notifications",
"sse_real_time_updates",
"notification_templates",
"notification_orchestration",
"messaging_integration",
"multi_channel_support"
]
def setup_custom_endpoints(self):
"""Setup custom endpoints for notification service"""
# SSE metrics endpoint
@self.app.get("/sse-metrics")
async def sse_metrics():
"""Get SSE service metrics"""
if self.sse_service:
try:
sse_metrics = self.sse_service.get_metrics()
return {
'active_tenants': sse_metrics.get('active_tenants', 0),
'total_connections': sse_metrics.get('total_connections', 0),
'active_listeners': sse_metrics.get('active_listeners', 0),
'redis_connected': bool(sse_metrics.get('redis_connected', False))
}
except Exception as e:
return {"error": str(e)}
return {"error": "SSE service not available"}
# Metrics endpoint
@self.app.get("/metrics")
async def metrics():
"""Prometheus metrics endpoint"""
if self.metrics_collector:
return self.metrics_collector.get_metrics()
return {"metrics": "not_available"}
# Create service instance
service = NotificationService()
# Create FastAPI app with standardized setup
app = service.create_app(
docs_url="/docs",
redoc_url="/redoc"
)
2025-09-29 13:13:12 +02:00
# Setup standard endpoints
service.setup_standard_endpoints()
# Setup custom endpoints
service.setup_custom_endpoints()
2025-07-21 22:44:11 +02:00
2025-09-29 13:13:12 +02:00
# Include routers
service.add_router(notification_router, tags=["notifications"])
service.add_router(sse_router, tags=["sse"])
if __name__ == "__main__":
import uvicorn
2025-07-21 22:44:11 +02:00
uvicorn.run(app, host="0.0.0.0", port=8000)