use std::time::Duration; use lettre::{ message::Mailbox, transport::smtp::client::{Tls, TlsParameters}, AsyncSmtpTransport, Message, Tokio1Executor, }; use rocket::http::uri::Absolute; use serde_json::json; use crate::types::UserID; pub struct Mailer { from: Mailbox, verify_base: Absolute<'static>, transport: AsyncSmtpTransport, } impl Mailer { pub fn new( from: Mailbox, host: &str, port: u16, verify_base: Absolute<'static>, ) -> anyhow::Result { Ok(Mailer { from, verify_base, transport: AsyncSmtpTransport::::builder_dangerous(host) .port(port) .tls(Tls::Opportunistic(TlsParameters::new(host.to_string())?)) .timeout(Some(Duration::from_secs(5))) .build(), }) } pub(crate) async fn send_account_verify( &self, uid: &UserID, to: &str, code: &str, ) -> anyhow::Result<()> { let fragment = base64::encode_config( serde_json::to_string(&json!({ "uid": uid, "email": to, "code": code, }))?, base64::URL_SAFE, ); let email = Message::builder() .from(self.from.clone()) .to(to.parse()?) .subject("account verify code") .body(format!("{}/#/verify/{fragment}", self.verify_base))?; lettre::AsyncTransport::send(&self.transport, email).await?; Ok(()) } pub(crate) async fn send_session_verify(&self, to: &str, code: &str) -> anyhow::Result<()> { let email = Message::builder() .from(self.from.clone()) .to(to.parse()?) .subject("session verify code") .body(format!("{code}"))?; lettre::AsyncTransport::send(&self.transport, email).await?; Ok(()) } pub(crate) async fn send_password_changed(&self, to: &str) -> anyhow::Result<()> { let email = Message::builder() .from(self.from.clone()) .to(to.parse()?) .subject("account password has been changed") .body(String::from( "your account password has been changed. if you haven't done this, \ you're probably in trouble now.", ))?; lettre::AsyncTransport::send(&self.transport, email).await?; Ok(()) } pub(crate) async fn send_password_forgot(&self, to: &str, code: &str) -> anyhow::Result<()> { let email = Message::builder() .from(self.from.clone()) .to(to.parse()?) .subject("account reset code") .body(code.to_string())?; lettre::AsyncTransport::send(&self.transport, email).await?; Ok(()) } pub(crate) async fn send_account_reset(&self, to: &str) -> anyhow::Result<()> { let email = Message::builder() .from(self.from.clone()) .to(to.parse()?) .subject("account has been reset") .body(String::from( "your account has been reset. if you haven't done this, \ you're probably in trouble now.", ))?; lettre::AsyncTransport::send(&self.transport, email).await?; Ok(()) } }