use crate::monitoring::{self, HealthAlert};
use chrono::{DateTime, Utc};
use serde::{Serialize, Deserialize};
use tokio::sync::broadcast;
use std::collections::HashMap;
use async_trait::async_trait;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum AlertSeverity {
Critical,
Warning,
Info,
}
impl std::fmt::Display for AlertSeverity {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Critical => write!(f, "CRITICAL"),
Self::Warning => write!(f, "WARNING"),
Self::Info => write!(f, "INFO"),
}
}
}
impl From<&monitoring::AlertSeverity> for AlertSeverity {
fn from(severity: &monitoring::AlertSeverity) -> Self {
match severity {
monitoring::AlertSeverity::Critical => AlertSeverity::Critical,
monitoring::AlertSeverity::Warning => AlertSeverity::Warning,
monitoring::AlertSeverity::Info => AlertSeverity::Info,
}
}
}
#[derive(Debug, Clone, Serialize)]
pub struct AlertNotification {
pub alert: HealthAlert,
pub notification_channels: Vec<NotificationChannel>,
pub escalation_level: EscalationLevel,
pub context: AlertContext,
}
#[derive(Debug, Clone, Serialize)]
pub enum NotificationChannel {
Email(String),
Slack(String),
WebHook(String),
PagerDuty(String),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
pub enum EscalationLevel {
Low,
Medium,
High,
Critical,
}
#[derive(Debug, Clone, Serialize)]
pub struct AlertContext {
pub incident_id: String,
pub affected_components: Vec<String>,
pub additional_info: HashMap<String, String>,
pub created_at: DateTime<Utc>,
}
pub struct AlertManager {
config: AlertConfig,
alert_tx: broadcast::Sender<AlertNotification>,
notification_handlers: Vec<NotificationHandlerType>,
}
#[derive(Clone)]
pub struct AlertConfig {
pub alert_ttl: chrono::Duration,
pub max_alerts_per_window: usize,
pub notification_channels: Vec<NotificationChannel>,
pub escalation_policies: HashMap<AlertSeverity, EscalationPolicy>,
}
#[derive(Clone)]
pub struct EscalationPolicy {
pub initial_delay: chrono::Duration,
pub repeat_interval: chrono::Duration,
pub max_escalations: usize,
pub notification_channels: Vec<NotificationChannel>,
}
#[async_trait]
pub trait NotificationHandler: Send + Sync {
async fn send_notification(&self, notification: &AlertNotification) -> Result<(), NotificationError>;
}
#[derive(Debug, thiserror::Error)]
pub enum NotificationError {
#[error("Failed to send notification: {0}")]
SendError(String),
#[error("Channel configuration error: {0}")]
ConfigError(String),
#[error("Rate limit exceeded")]
RateLimitExceeded,
}
#[derive(Clone)]
pub enum NotificationHandlerType {
Email(crate::notification::EmailNotificationHandler),
Slack(crate::notification::SlackNotificationHandler),
}
impl NotificationHandlerType {
async fn send_notification(&self, notification: &AlertNotification) -> Result<(), NotificationError> {
match self {
Self::Email(handler) => handler.send_notification(notification).await,
Self::Slack(handler) => handler.send_notification(notification).await,
}
}
}
impl AlertManager {
pub fn new(config: AlertConfig) -> Self {
let (tx, _) = broadcast::channel(100);
Self {
config,
alert_tx: tx,
notification_handlers: Vec::new(),
}
}
pub fn register_handler(&mut self, handler: NotificationHandlerType) {
self.notification_handlers.push(handler);
}
pub async fn process_alert(&self, alert: HealthAlert) -> Result<(), NotificationError> {
let notification = self.create_notification(alert);
let _ = self.alert_tx.send(notification.clone());
for handler in &self.notification_handlers {
handler.send_notification(¬ification).await?;
}
Ok(())
}
fn create_notification(&self, alert: HealthAlert) -> AlertNotification {
let severity = AlertSeverity::from(&alert.severity);
let channels = self.get_notification_channels(&severity);
AlertNotification {
alert,
notification_channels: channels,
escalation_level: self.determine_escalation_level(&severity),
context: AlertContext {
incident_id: uuid::Uuid::new_v4().to_string(),
affected_components: Vec::new(),
additional_info: HashMap::new(),
created_at: Utc::now(),
},
}
}
fn get_notification_channels(&self, severity: &AlertSeverity) -> Vec<NotificationChannel> {
self.config
.escalation_policies
.get(severity)
.map(|policy| policy.notification_channels.clone())
.unwrap_or_else(|| self.config.notification_channels.clone())
}
fn determine_escalation_level(&self, severity: &AlertSeverity) -> EscalationLevel {
match severity {
AlertSeverity::Critical => EscalationLevel::Critical,
AlertSeverity::Warning => EscalationLevel::High,
AlertSeverity::Info => EscalationLevel::Low,
}
}
}