78 lines
2.4 KiB
Rust
78 lines
2.4 KiB
Rust
use config::AppConfig;
|
|
use lettre::message::Mailbox;
|
|
use lettre::transport::smtp::authentication::Credentials;
|
|
use lettre::transport::smtp::client::Tls;
|
|
use lettre::{SmtpTransport, Transport};
|
|
use regex::Regex;
|
|
use serde::{Deserialize, Serialize};
|
|
use std::sync::LazyLock;
|
|
use std::time::Duration;
|
|
|
|
#[derive(Clone)]
|
|
pub struct AppEmail {
|
|
pub cred: Credentials,
|
|
pub mailer: SmtpTransport,
|
|
pub from: Mailbox,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
|
pub struct EmailMessage {
|
|
pub to: String,
|
|
pub subject: String,
|
|
pub body: String,
|
|
}
|
|
|
|
static EMAIL_REGEX: LazyLock<Regex> =
|
|
LazyLock::new(|| Regex::new(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$").unwrap());
|
|
|
|
impl AppEmail {
|
|
pub async fn init(cfg: &AppConfig) -> anyhow::Result<Self> {
|
|
let smtp_host = cfg.smtp_host()?;
|
|
let smtp_port = cfg.smtp_port()?;
|
|
let smtp_username = cfg.smtp_username()?;
|
|
let smtp_password = cfg.smtp_password()?;
|
|
let smtp_from = cfg.smtp_from()?;
|
|
let smtp_tls = cfg.smtp_tls()?;
|
|
let smtp_timeout = cfg.smtp_timeout()?;
|
|
let cred = Credentials::new(smtp_username, smtp_password);
|
|
let tls_param = if smtp_tls {
|
|
Tls::Required(
|
|
lettre::transport::smtp::client::TlsParameters::builder(smtp_host.clone())
|
|
.build()
|
|
.map_err(|e| anyhow::anyhow!("Failed to build TLS parameters: {}", e))?,
|
|
)
|
|
} else {
|
|
Tls::None
|
|
};
|
|
|
|
let mailer = SmtpTransport::builder_dangerous(smtp_host)
|
|
.port(smtp_port)
|
|
.tls(tls_param)
|
|
.timeout(Some(Duration::from_secs(smtp_timeout)))
|
|
.credentials(cred.clone())
|
|
.build();
|
|
Ok(AppEmail {
|
|
cred,
|
|
mailer,
|
|
from: smtp_from
|
|
.parse()
|
|
.map_err(|e| anyhow::anyhow!("Invalid from address: {}", e))?,
|
|
})
|
|
}
|
|
pub async fn send(&self, msg: EmailMessage) -> anyhow::Result<()> {
|
|
if !EMAIL_REGEX.is_match(&msg.to) {
|
|
return Err(anyhow::anyhow!("Invalid email address format: {}", msg.to));
|
|
}
|
|
|
|
let email = lettre::Message::builder()
|
|
.from(self.from.clone())
|
|
.to(msg.to.parse()?)
|
|
.subject(msg.subject)
|
|
.body(msg.body)?;
|
|
self.mailer
|
|
.send(&email)
|
|
.map_err(|e| anyhow::anyhow!("{}", e))?;
|
|
Ok(())
|
|
}
|
|
}
|