use std::{str::FromStr, sync::Arc, time::Duration}; use anyhow::{anyhow, Result}; use lettre::{ message::Mailbox, transport::smtp::authentication::Credentials, Message, SmtpTransport, Transport, }; use log::info; use serde::Deserialize; use crate::Config; #[derive(Debug, Deserialize)] pub struct MailConfig { from: String, to: Vec, smtp_auth: SmtpAuth, smtp_port: u16, smtp_timeout: Option, smtp_server: String, smtp_username: Option, smtp_password: Option, } #[derive(Clone, Deserialize, Debug)] #[serde(rename_all = "snake_case")] enum SmtpAuth { Plain, Tls, Starttls, } pub async fn send_mail( config: &Arc, service: usize, check: usize, error: String, ) -> Result<()> { let config = config.to_owned(); tokio::task::spawn_blocking(move || { if let Some(mconfig) = &config.mail { info!("sending mail!"); let mut transport = match mconfig.smtp_auth { SmtpAuth::Plain => SmtpTransport::builder_dangerous(&mconfig.smtp_server), SmtpAuth::Tls => SmtpTransport::relay(&mconfig.smtp_server)?, SmtpAuth::Starttls => SmtpTransport::starttls_relay(&mconfig.smtp_server)?, } .port(mconfig.smtp_port); if let Some(username) = mconfig.smtp_username.clone() { let password = mconfig .smtp_password .clone() .ok_or(anyhow!("smtp password missing"))?; transport = transport.credentials(Credentials::new(username, password)); } if let Some(timeout) = mconfig.smtp_timeout { transport = transport.timeout(Some(Duration::from_secs_f64(timeout))) } let transport = transport.build(); for recipient in &mconfig.to { let service = &config.services[service]; let message = Message::builder() .from(Mailbox::from_str(&mconfig.from)?) .to(Mailbox::from_str(&recipient)?) .subject(format!("{} failed.", service.title)) .body(format!( "Check {:?} reported:\n{}", service.checks[check].display(), error ))?; transport.send(&message)?; } } Ok::<_, anyhow::Error>(()) }) .await??; Ok(()) }