Skip to content

Commit

Permalink
Merge pull request #132 from t3hmrman/refactor/notify-email/print-mor…
Browse files Browse the repository at this point in the history
…e-errors

refactor(notify/email): improve observability for email notifier
  • Loading branch information
valeriansaliou authored Aug 11, 2023
2 parents cec7159 + 3202e6c commit bf6cc65
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 80 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ openssl-probe = "0.1"
reqwest = { version = "0.11", features = ["native-tls-vendored", "gzip", "blocking", "json"], default-features = false }
ping = "0.4"
run_script = "0.10"
lettre = { version = "0.10", features = ["smtp-transport", "native-tls", "hostname", "builder"], default-features = false, optional = true }
lettre = { version = "0.10.4", features = ["smtp-transport", "native-tls", "hostname", "builder"], default-features = false, optional = true }
libstrophe = { version = "0.17", optional = true }

[features]
Expand Down
166 changes: 87 additions & 79 deletions src/notifier/email.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,92 +9,104 @@ use std::time::Duration;
use lettre::message::{Mailbox, Message};
use lettre::transport::smtp::authentication::Credentials;
use lettre::transport::smtp::client::{Tls, TlsParameters};
use lettre::transport::smtp::SmtpTransport;
use lettre::transport::smtp::{Error as SmtpError, SmtpTransport};
use lettre::{Address, Transport};

use super::generic::{GenericNotifier, Notification, DISPATCH_TIMEOUT_SECONDS};
use crate::config::config::ConfigNotify;
use crate::APP_CONF;

#[derive(Default)]
pub struct EmailNotifier;

impl GenericNotifier for EmailNotifier {
fn attempt(notify: &ConfigNotify, notification: &Notification) -> Result<(), bool> {
if let Some(ref email_config) = notify.email {
let nodes_label = notification.replicas.join(", ");

// Build up the message text
let mut message = String::new();

if notification.startup == true {
message.push_str(&format!(
"Status startup alert from: {}\n",
APP_CONF.branding.page_title
));
} else if notification.changed == true {
message.push_str(&format!(
"Status change report from: {}\n",
APP_CONF.branding.page_title
));
} else {
message.push_str(&format!(
"Status unchanged reminder from: {}\n",
APP_CONF.branding.page_title
));
let email_config = match &notify.email {
Some(cfg) => cfg,
None => return Err(false),
};

let nodes_label = notification.replicas.join(", ");

// Build up the message text
let mut message = String::new();

if notification.startup == true {
message.push_str(&format!(
"Status startup alert from: {}\n",
APP_CONF.branding.page_title
));
} else if notification.changed == true {
message.push_str(&format!(
"Status change report from: {}\n",
APP_CONF.branding.page_title
));
} else {
message.push_str(&format!(
"Status unchanged reminder from: {}\n",
APP_CONF.branding.page_title
));
}

message.push_str("\n--\n");
message.push_str(&format!("Status: {:?}\n", notification.status));
message.push_str(&format!("Nodes: {}\n", &nodes_label));
message.push_str(&format!("Time: {}\n", &notification.time));
message.push_str(&format!("URL: {}", APP_CONF.branding.page_url.as_str()));

message.push_str("\n--\n");
message.push_str("\n");
message.push_str("To unsubscribe, please edit your status page configuration.");

debug!("will send email notification with message: {}", &message);

// Build up the email
let email_message = Message::builder()
.to(Mailbox::new(
None,
email_config.to.parse::<Address>().or(Err(true))?,
))
.from(Mailbox::new(
Some(APP_CONF.branding.page_title.to_owned()),
email_config.from.parse::<Address>().or(Err(true))?,
))
.subject(format!(
"{} | {}",
notification.status.as_str().to_uppercase(),
&nodes_label
))
.body(message)
.or(Err(true))?;

// Create the transport if not present
let transport = match acquire_transport(
&email_config.smtp_host,
email_config.smtp_port,
email_config.smtp_username.to_owned(),
email_config.smtp_password.to_owned(),
email_config.smtp_encrypt,
) {
Ok(t) => t,
Err(e) => {
error!("failed to build email transport: {e}");
return Err(true);
}
};

message.push_str("\n--\n");
message.push_str(&format!("Status: {:?}\n", notification.status));
message.push_str(&format!("Nodes: {}\n", &nodes_label));
message.push_str(&format!("Time: {}\n", &notification.time));
message.push_str(&format!("URL: {}", APP_CONF.branding.page_url.as_str()));

message.push_str("\n--\n");
message.push_str("\n");
message.push_str("To unsubscribe, please edit your status page configuration.");

debug!("will send email notification with message: {}", &message);

// Build up the email
let email_message = Message::builder()
.to(Mailbox::new(
None,
email_config.to.parse::<Address>().or(Err(true))?,
))
.from(Mailbox::new(
Some(APP_CONF.branding.page_title.to_owned()),
email_config.from.parse::<Address>().or(Err(true))?,
))
.subject(format!(
"{} | {}",
notification.status.as_str().to_uppercase(),
&nodes_label
))
.body(message)
.or(Err(true))?;

// Deliver the message
return acquire_transport(
&email_config.smtp_host,
email_config.smtp_port,
email_config.smtp_username.to_owned(),
email_config.smtp_password.to_owned(),
email_config.smtp_encrypt,
)
.map(|transport| transport.send(&email_message))
.and(Ok(()))
.or(Err(true));
// Deliver the message
if let Err(e) = transport.send(&email_message) {
error!("failed to send email: {e}");
return Err(true);
}

Err(false)
Ok(())
}

fn can_notify(notify: &ConfigNotify, notification: &Notification) -> bool {
if let Some(ref email_config) = notify.email {
notification.expected(email_config.reminders_only)
} else {
false
}
notify
.email
.as_ref()
.map_or(false, |cfg| notification.expected(cfg.reminders_only))
}

fn name() -> &'static str {
Expand All @@ -108,7 +120,7 @@ fn acquire_transport(
smtp_username: Option<String>,
smtp_password: Option<String>,
smtp_encrypt: bool,
) -> Result<SmtpTransport, ()> {
) -> Result<SmtpTransport, SmtpError> {
// Acquire credentials (if any)
let credentials = if let (Some(smtp_username_value), Some(smtp_password_value)) =
(smtp_username, smtp_password)
Expand All @@ -122,20 +134,16 @@ fn acquire_transport(
};

// Acquire TLS wrapper (may fail)
let tls_wrapper = if let Ok(tls_parameters) = TlsParameters::new(smtp_host.into()) {
if smtp_encrypt == true {
Ok(Tls::Required(tls_parameters))
} else {
Ok(Tls::Opportunistic(tls_parameters))
}
} else {
Err(())
let tls_wrapper = match TlsParameters::new(smtp_host.into()) {
Ok(p) if smtp_encrypt => Tls::Required(p),
Ok(p) => Tls::Opportunistic(p),
Err(e) => return Err(e),
};

// Build transport
let mut mailer = SmtpTransport::builder_dangerous(smtp_host)
.port(smtp_port)
.tls(tls_wrapper.or(Err(()))?)
.tls(tls_wrapper)
.timeout(Some(Duration::from_secs(DISPATCH_TIMEOUT_SECONDS)));

if let Some(credentials_value) = credentials {
Expand Down

0 comments on commit bf6cc65

Please sign in to comment.