use lettre::{ transport::smtp::authentication::Credentials, AsyncSmtpTransport, AsyncTransport, Message, Tokio1Executor, }; use std::env; use crate::{error::Result, models::PendingEvent}; #[derive(Clone)] pub struct EmailConfig { pub smtp_host: String, pub smtp_port: u16, pub smtp_user: String, pub smtp_pass: String, pub from_email: String, pub admin_email: String, } impl EmailConfig { pub fn from_env() -> Result { Ok(EmailConfig { smtp_host: env::var("SMTP_HOST").expect("SMTP_HOST not set"), smtp_port: env::var("SMTP_PORT") .unwrap_or_else(|_| "587".to_string()) .parse() .expect("Invalid SMTP_PORT"), smtp_user: env::var("SMTP_USER").expect("SMTP_USER not set"), smtp_pass: env::var("SMTP_PASS").expect("SMTP_PASS not set"), from_email: env::var("SMTP_FROM").expect("SMTP_FROM not set"), admin_email: env::var("ADMIN_EMAIL").expect("ADMIN_EMAIL not set"), }) } } pub struct Mailer { transport: AsyncSmtpTransport, config: EmailConfig, } impl Mailer { pub fn new(config: EmailConfig) -> Result { let creds = Credentials::new(config.smtp_user.clone(), config.smtp_pass.clone()); let transport = AsyncSmtpTransport::::starttls_relay(&config.smtp_host)? .port(config.smtp_port) .credentials(creds) .build(); Ok(Mailer { transport, config }) } pub async fn send_event_submission_notification(&self, event: &PendingEvent) -> Result<()> { let email = Message::builder() .from(self.config.from_email.parse()?) .to(self.config.admin_email.parse()?) .subject(&format!("New Event Submission: {}", event.title)) .body(format!( "New event submitted for approval:\n\nTitle: {}\nDescription: {}\nStart: {}\nLocation: {}\nSubmitted by: {}", event.title, event.description, event.start_time, event.location, event.submitter_email.as_deref().unwrap_or("Unknown") ))?; self.transport.send(email).await?; tracing::info!("Event submission email sent successfully"); Ok(()) } pub async fn send_event_approval_notification(&self, event: &PendingEvent, _admin_notes: Option<&str>) -> Result<()> { if let Some(submitter_email) = &event.submitter_email { let email = Message::builder() .from(self.config.from_email.parse()?) .to(submitter_email.parse()?) .subject(&format!("Event Approved: {}", event.title)) .body(format!( "Great news! Your event '{}' has been approved and will be published.", event.title ))?; self.transport.send(email).await?; } Ok(()) } pub async fn send_event_rejection_notification(&self, event: &PendingEvent, admin_notes: Option<&str>) -> Result<()> { if let Some(submitter_email) = &event.submitter_email { let email = Message::builder() .from(self.config.from_email.parse()?) .to(submitter_email.parse()?) .subject(&format!("Event Update: {}", event.title)) .body(format!( "Thank you for submitting '{}'. After review, we're unable to include this event at this time.\n\n{}", event.title, admin_notes.unwrap_or("Please feel free to submit future events.") ))?; self.transport.send(email).await?; } Ok(())