spawn_access_control/notification/
email.rs

1use crate::alert_system::{AlertNotification, NotificationError, EscalationLevel};
2use crate::async_trait;
3use lettre::{
4    transport::smtp::authentication::Credentials,
5    AsyncSmtpTransport, AsyncTransport, Message, Tokio1Executor,
6};
7use serde::{Serialize, Deserialize};
8
9#[derive(Clone, Debug, Serialize, Deserialize)]
10pub struct SmtpConfig {
11    pub host: String,
12    pub port: u16,
13    pub username: String,
14    pub password: String,
15}
16
17#[derive(Clone)]
18pub struct EmailConfig {
19    pub smtp: SmtpConfig,
20    pub templates: EmailTemplates,
21    pub recipients: EmailRecipients,
22}
23
24#[derive(Clone)]
25pub struct EmailTemplates {
26    pub alert_template: String,
27}
28
29#[derive(Clone)]
30pub struct EmailRecipients {
31    pub primary: Vec<String>,
32    pub emergency: Vec<String>,
33}
34
35#[derive(Clone)]
36pub struct EmailNotificationHandler {
37    config: EmailConfig,
38    transport: AsyncSmtpTransport<Tokio1Executor>,
39}
40
41impl EmailNotificationHandler {
42    pub fn new(config: EmailConfig) -> Result<Self, NotificationError> {
43        let creds = Credentials::new(
44            config.smtp.username.clone(),
45            config.smtp.password.clone(),
46        );
47
48        let transport = AsyncSmtpTransport::<Tokio1Executor>::relay(&config.smtp.host)
49            .map_err(|e| NotificationError::ConfigError(e.to_string()))?
50            .port(config.smtp.port)
51            .credentials(creds)
52            .build();
53
54        Ok(Self {
55            config,
56            transport,
57        })
58    }
59
60    fn render_alert_template(&self, notification: &AlertNotification) -> Result<String, NotificationError> {
61        Ok(format!("Alert: {}", notification.alert.message))
62    }
63}
64
65#[async_trait]
66impl crate::alert_system::NotificationHandler for EmailNotificationHandler {
67    async fn send_notification(&self, notification: &AlertNotification) -> Result<(), NotificationError> {
68        let body = self.render_alert_template(notification)?;
69        let subject = format!(
70            "[{:?}] Alert: {} - {}",
71            notification.alert.severity,
72            notification.alert.metric_name,
73            notification.alert.message
74        );
75
76        let recipients = match notification.escalation_level {
77            EscalationLevel::Critical => {
78                self.config.recipients.emergency.clone()
79            }
80            _ => self.config.recipients.primary.clone(),
81        };
82
83        let email = Message::builder()
84            .subject(subject)
85            .body(body)
86            .map_err(|e| NotificationError::ConfigError(e.to_string()))?;
87
88        for _recipient in recipients {
89            self.transport
90                .send(email.clone())
91                .await
92                .map_err(|e| NotificationError::SendError(e.to_string()))?;
93        }
94
95        Ok(())
96    }
97}