Skip to main content

bestool_alertd/targets/
email.rs

1use mailgun_rs::{EmailAddress, Mailgun, Message};
2use miette::{IntoDiagnostic, Result, WrapErr, miette};
3use tracing::debug;
4
5use crate::{EmailConfig, alert::AlertDefinition};
6
7#[derive(serde::Deserialize, serde::Serialize, Clone, Debug)]
8#[serde(rename_all = "snake_case")]
9pub struct TargetEmail {
10	pub addresses: Vec<String>,
11}
12
13impl TargetEmail {
14	pub async fn send(
15		&self,
16		alert: &AlertDefinition,
17		email: Option<&EmailConfig>,
18		subject: &str,
19		body: &str,
20		dry_run: bool,
21	) -> Result<()> {
22		let body = {
23			let parser = pulldown_cmark::Parser::new(body);
24			let mut html_output = String::new();
25			pulldown_cmark::html::push_html(&mut html_output, parser);
26			html_output
27		};
28
29		if dry_run {
30			println!("-------------------------------");
31			println!("Alert: {}", alert.file.display());
32			println!("Recipients: {}", self.addresses.join(", "));
33			println!("Subject: {subject}");
34			println!("Body: {body}");
35			return Ok(());
36		}
37
38		debug!(?self.addresses, "sending email");
39		let email_config = email.ok_or_else(|| miette!("missing email config"))?;
40		let sender = EmailAddress::address(&email_config.from);
41		let message = Message {
42			to: self
43				.addresses
44				.iter()
45				.map(|email| EmailAddress::address(email))
46				.collect(),
47			subject: subject.into(),
48			html: body,
49			..Default::default()
50		};
51		let mailgun = Mailgun {
52			api_key: email_config.mailgun_api_key.clone(),
53			domain: email_config.mailgun_domain.clone(),
54		};
55		mailgun
56			.async_send(mailgun_rs::MailgunRegion::US, &sender, message, None)
57			.await
58			.into_diagnostic()
59			.wrap_err("sending email")
60			.map(drop)
61	}
62}