use super::mail_template::mail_template;
use crate::{order_info::retrieve::StripeCheckoutLineItems, AppConfig, MailConfig};
use lettre::{
message::MultiPart,
transport::smtp::{authentication::Credentials, client::TlsParameters},
Message, SendmailTransport, SmtpTransport, Transport,
};
use rocket::{figment::providers::Env, Config};
use std::sync::LazyLock;
use thiserror::Error;
static SENDMAILER: LazyLock<SendmailTransport> = LazyLock::new(SendmailTransport::new);
static SMTPMAILER: LazyLock<Result<SmtpTransport, MailConfigError>> = LazyLock::new(|| {
let config = Config::figment()
.merge(Env::prefixed("CORRELATE_"))
.extract::<AppConfig>()?;
create_smtp_transport_from_figment(config.email)
});
fn create_smtp_transport_from_figment(
config: MailConfig,
) -> Result<SmtpTransport, MailConfigError> {
let host = if let Some(host) = config.smtp_relay_host {
if host.is_empty() {
return Err(MailConfigError::InvalidSmtpHost);
}
host
} else {
return Err(MailConfigError::InvalidSmtpHost);
};
let smtp_username = config.smtp_username.unwrap_or_default();
let smtp_password = config.smtp_password.unwrap_or_default();
if smtp_password.is_empty() || smtp_username.is_empty() {
let params = TlsParameters::builder(host.to_owned()).build()?;
let mailer_transport = SmtpTransport::builder_dangerous(host)
.port(config.smtp_relay_port)
.tls(lettre::transport::smtp::client::Tls::Opportunistic(params));
Ok(mailer_transport.build())
} else {
let creds = Credentials::new(smtp_username, smtp_password);
let mailer_transport = SmtpTransport::starttls_relay(&host)?.credentials(creds);
Ok(mailer_transport.build())
}
}
pub struct AdditionalFields<'b> {
pub message_summary: &'b str,
pub cta_link: Option<&'b str>,
pub logo_img: Option<&'b str>,
pub ordered_items: Option<&'b StripeCheckoutLineItems>,
}
pub fn default_subject_line() -> &'static str {
"New message from"
}
#[derive(Error, Debug)]
pub enum MailConfigError {
#[error("A malformed email was detected. Please format your email address properly: {0}.")]
AddressParse(#[from] lettre::address::AddressError),
#[error("Unable to create email message.")]
EmailBuild(#[from] lettre::error::Error),
#[error("Unable to send email through SMTP")]
GenericSmtpTransport,
#[error("Invalid SMTP host: host is empty or not provided")]
InvalidSmtpHost,
#[error("Unable to deserialize email config: {0}.")]
MailConfig(#[from] rocket::figment::error::Error),
#[error("Unable to send email because of {0}")]
SendmailTransport(#[from] lettre::transport::sendmail::Error),
#[error("Unable to send email because of {0}")]
SmtpTransportBuild(#[from] lettre::transport::smtp::Error),
#[error("Unable to create mail template because {0}")]
Tera(#[from] tera::Error),
}
pub fn send_email(
form_email: &str,
form_full_name: &str,
form_subject: &str,
form_message: &str,
form_site: &str,
form_extra: AdditionalFields<'_>,
config: &MailConfig,
) -> Result<(), MailConfigError> {
let mail_subject = format!("{} {}", default_subject_line(), form_site);
let message_sender = format!("{form_full_name} <{form_email}>");
let html_body = mail_template(
&message_sender,
form_subject,
form_message,
&mail_subject,
&form_extra,
"welcome.html",
)?;
let txt_body = mail_template(
&message_sender,
form_subject,
form_message,
&mail_subject,
&form_extra,
"welcome.txt",
)?;
let reply_to_email = form_email.parse::<lettre::message::Mailbox>()?;
let sending_email =
format!("{form_full_name} <{}>", config.sender).parse::<lettre::message::Mailbox>()?;
let destination_email = config.recipient.parse::<lettre::message::Mailbox>()?;
let email_msg = Message::builder()
.from(sending_email)
.reply_to(reply_to_email)
.to(destination_email)
.subject(mail_subject)
.multipart(MultiPart::alternative_plain_html(txt_body, html_body))?;
if config.smtp_relay_host.is_none() || config.smtp_relay_host == Some(String::from("")) {
SENDMAILER.send(&email_msg)?;
Ok(())
} else {
match &*SMTPMAILER {
Ok(mailer) => {
mailer.send(&email_msg)?;
Ok(())
}
Err(err) => match err {
MailConfigError::MailConfig(err) => Err(MailConfigError::MailConfig(
rocket::figment::error::Error::from(err.to_string()),
)),
MailConfigError::InvalidSmtpHost => Err(MailConfigError::InvalidSmtpHost),
_ => Err(MailConfigError::GenericSmtpTransport),
},
}
}
}