1use crate::monitoring::{self, HealthAlert};
2use chrono::{DateTime, Utc};
3use serde::{Serialize, Deserialize};
4use tokio::sync::broadcast;
5use std::collections::HashMap;
6use async_trait::async_trait;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
9pub enum AlertSeverity {
10 Critical,
11 Warning,
12 Info,
13}
14
15impl std::fmt::Display for AlertSeverity {
16 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17 match self {
18 Self::Critical => write!(f, "CRITICAL"),
19 Self::Warning => write!(f, "WARNING"),
20 Self::Info => write!(f, "INFO"),
21 }
22 }
23}
24
25impl From<&monitoring::AlertSeverity> for AlertSeverity {
26 fn from(severity: &monitoring::AlertSeverity) -> Self {
27 match severity {
28 monitoring::AlertSeverity::Critical => AlertSeverity::Critical,
29 monitoring::AlertSeverity::Warning => AlertSeverity::Warning,
30 monitoring::AlertSeverity::Info => AlertSeverity::Info,
31 }
32 }
33}
34
35#[derive(Debug, Clone, Serialize)]
36pub struct AlertNotification {
37 pub alert: HealthAlert,
38 pub notification_channels: Vec<NotificationChannel>,
39 pub escalation_level: EscalationLevel,
40 pub context: AlertContext,
41}
42
43#[derive(Debug, Clone, Serialize)]
44pub enum NotificationChannel {
45 Email(String),
46 Slack(String),
47 WebHook(String),
48 PagerDuty(String),
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
52pub enum EscalationLevel {
53 Low,
54 Medium,
55 High,
56 Critical,
57}
58
59#[derive(Debug, Clone, Serialize)]
60pub struct AlertContext {
61 pub incident_id: String,
62 pub affected_components: Vec<String>,
63 pub additional_info: HashMap<String, String>,
64 pub created_at: DateTime<Utc>,
65}
66
67pub struct AlertManager {
68 config: AlertConfig,
69 alert_tx: broadcast::Sender<AlertNotification>,
70 notification_handlers: Vec<NotificationHandlerType>,
71}
72
73#[derive(Clone)]
74pub struct AlertConfig {
75 pub alert_ttl: chrono::Duration,
76 pub max_alerts_per_window: usize,
77 pub notification_channels: Vec<NotificationChannel>,
78 pub escalation_policies: HashMap<AlertSeverity, EscalationPolicy>,
79}
80
81#[derive(Clone)]
82pub struct EscalationPolicy {
83 pub initial_delay: chrono::Duration,
84 pub repeat_interval: chrono::Duration,
85 pub max_escalations: usize,
86 pub notification_channels: Vec<NotificationChannel>,
87}
88
89#[async_trait]
90pub trait NotificationHandler: Send + Sync {
91 async fn send_notification(&self, notification: &AlertNotification) -> Result<(), NotificationError>;
92}
93
94#[derive(Debug, thiserror::Error)]
95pub enum NotificationError {
96 #[error("Failed to send notification: {0}")]
97 SendError(String),
98
99 #[error("Channel configuration error: {0}")]
100 ConfigError(String),
101
102 #[error("Rate limit exceeded")]
103 RateLimitExceeded,
104}
105
106#[derive(Clone)]
107pub enum NotificationHandlerType {
108 Email(crate::notification::EmailNotificationHandler),
109 Slack(crate::notification::SlackNotificationHandler),
110}
111
112impl NotificationHandlerType {
113 async fn send_notification(&self, notification: &AlertNotification) -> Result<(), NotificationError> {
114 match self {
115 Self::Email(handler) => handler.send_notification(notification).await,
116 Self::Slack(handler) => handler.send_notification(notification).await,
117 }
118 }
119}
120
121impl AlertManager {
122 pub fn new(config: AlertConfig) -> Self {
123 let (tx, _) = broadcast::channel(100);
124 Self {
125 config,
126 alert_tx: tx,
127 notification_handlers: Vec::new(),
128 }
129 }
130
131 pub fn register_handler(&mut self, handler: NotificationHandlerType) {
132 self.notification_handlers.push(handler);
133 }
134
135 pub async fn process_alert(&self, alert: HealthAlert) -> Result<(), NotificationError> {
136 let notification = self.create_notification(alert);
137
138 let _ = self.alert_tx.send(notification.clone());
140
141 for handler in &self.notification_handlers {
143 handler.send_notification(¬ification).await?;
144 }
145
146 Ok(())
147 }
148
149 fn create_notification(&self, alert: HealthAlert) -> AlertNotification {
150 let severity = AlertSeverity::from(&alert.severity);
151 let channels = self.get_notification_channels(&severity);
152
153 AlertNotification {
154 alert,
155 notification_channels: channels,
156 escalation_level: self.determine_escalation_level(&severity),
157 context: AlertContext {
158 incident_id: uuid::Uuid::new_v4().to_string(),
159 affected_components: Vec::new(),
160 additional_info: HashMap::new(),
161 created_at: Utc::now(),
162 },
163 }
164 }
165
166 fn get_notification_channels(&self, severity: &AlertSeverity) -> Vec<NotificationChannel> {
167 self.config
168 .escalation_policies
169 .get(severity)
170 .map(|policy| policy.notification_channels.clone())
171 .unwrap_or_else(|| self.config.notification_channels.clone())
172 }
173
174 fn determine_escalation_level(&self, severity: &AlertSeverity) -> EscalationLevel {
175 match severity {
176 AlertSeverity::Critical => EscalationLevel::Critical,
177 AlertSeverity::Warning => EscalationLevel::High,
178 AlertSeverity::Info => EscalationLevel::Low,
179 }
180 }
181}