spawn_access_control/notification/
slack.rs

1use reqwest::Client;
2use serde_json::json;
3use crate::alert_system::{
4    NotificationHandler, 
5    NotificationError, 
6    AlertNotification,
7    AlertSeverity,
8    EscalationLevel
9};
10use async_trait::async_trait;
11
12#[derive(Clone)]
13pub struct SlackNotificationHandler {
14    client: reqwest::Client,
15    config: SlackConfig,
16}
17
18#[derive(Clone)]
19pub struct SlackConfig {
20    pub webhook_url: String,
21    pub default_channel: String,
22    pub emergency_channel: String,
23    pub username: String,
24    pub icon_emoji: String,
25}
26
27impl SlackNotificationHandler {
28    pub fn new(config: SlackConfig) -> Self {
29        Self {
30            client: Client::new(),
31            config,
32        }
33    }
34
35    fn create_slack_message(&self, notification: &AlertNotification) -> serde_json::Value {
36        let severity = AlertSeverity::from(&notification.alert.severity);
37        let color = match severity {
38            AlertSeverity::Critical => "#ff0000",
39            AlertSeverity::Warning => "#ffa500",
40            AlertSeverity::Info => "#0000ff",
41        };
42
43        json!({
44            "username": self.config.username,
45            "icon_emoji": self.config.icon_emoji,
46            "channel": if notification.escalation_level == EscalationLevel::Critical {
47                self.config.emergency_channel.clone()
48            } else {
49                self.config.default_channel.clone()
50            },
51            "attachments": [{
52                "color": color,
53                "title": format!("Alert: {}", notification.alert.message),
54                "fields": [
55                    {
56                        "title": "Severity",
57                        "value": format!("{:?}", notification.alert.severity),
58                        "short": true
59                    },
60                    {
61                        "title": "Metric",
62                        "value": notification.alert.metric_name,
63                        "short": true
64                    },
65                    {
66                        "title": "Current Value",
67                        "value": notification.alert.current_value.to_string(),
68                        "short": true
69                    },
70                    {
71                        "title": "Threshold",
72                        "value": notification.alert.threshold.to_string(),
73                        "short": true
74                    },
75                    {
76                        "title": "Incident ID",
77                        "value": notification.context.incident_id,
78                        "short": true
79                    }
80                ],
81                "footer": format!("Alert generated at {}", notification.context.created_at.to_rfc3339())
82            }]
83        })
84    }
85}
86
87#[async_trait]
88impl NotificationHandler for SlackNotificationHandler {
89    async fn send_notification(&self, notification: &AlertNotification) -> Result<(), NotificationError> {
90        let payload = self.create_slack_message(notification);
91
92        let response = self.client
93            .post(&self.config.webhook_url)
94            .json(&payload)
95            .send()
96            .await
97            .map_err(|e| NotificationError::SendError(e.to_string()))?;
98
99        if !response.status().is_success() {
100            return Err(NotificationError::SendError(
101                format!("Slack API error: {}", response.status())
102            ));
103        }
104
105        Ok(())
106    }
107}