feat: alerter module SMTP avec lettre
This commit is contained in:
124
src/alerter.rs
124
src/alerter.rs
@@ -1 +1,123 @@
|
||||
// Alerter module — Task 3
|
||||
use crate::config::SmtpConfig;
|
||||
use lettre::{
|
||||
message::header::ContentType,
|
||||
transport::smtp::authentication::Credentials,
|
||||
AsyncSmtpTransport, AsyncTransport, Message, Tokio1Executor,
|
||||
};
|
||||
|
||||
pub fn is_smtp_configured(smtp: &SmtpConfig) -> bool {
|
||||
!smtp.server.is_empty() && !smtp.from_email.is_empty() && !smtp.to_emails.is_empty()
|
||||
}
|
||||
|
||||
pub struct Alerter;
|
||||
|
||||
impl Alerter {
|
||||
pub async fn send(&self, smtp: &SmtpConfig, subject: &str, body: &str) -> (bool, String) {
|
||||
if !is_smtp_configured(smtp) {
|
||||
return (false, "SMTP non configure".into());
|
||||
}
|
||||
match self.send_email(smtp, subject, body).await {
|
||||
Ok(msg) => (true, msg),
|
||||
Err(e) => (false, e),
|
||||
}
|
||||
}
|
||||
|
||||
async fn send_email(
|
||||
&self,
|
||||
smtp: &SmtpConfig,
|
||||
subject: &str,
|
||||
body: &str,
|
||||
) -> Result<String, String> {
|
||||
let from = smtp
|
||||
.from_email
|
||||
.parse()
|
||||
.map_err(|_| "Email expediteur invalide".to_string())?;
|
||||
|
||||
let mut builder = Message::builder().from(from).subject(subject);
|
||||
for recipient in &smtp.to_emails {
|
||||
let mb = recipient
|
||||
.parse()
|
||||
.map_err(|_| format!("Destinataire invalide: {}", recipient))?;
|
||||
builder = builder.to(mb);
|
||||
}
|
||||
let email = builder
|
||||
.header(ContentType::TEXT_PLAIN)
|
||||
.body(body.to_string())
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
let transport = self.build_transport(smtp)?;
|
||||
transport
|
||||
.send(email)
|
||||
.await
|
||||
.map(|_| "Email envoye avec succes".to_string())
|
||||
.map_err(|e| format!("Erreur SMTP: {}", e))
|
||||
}
|
||||
|
||||
fn build_transport(
|
||||
&self,
|
||||
smtp: &SmtpConfig,
|
||||
) -> Result<AsyncSmtpTransport<Tokio1Executor>, String> {
|
||||
let mut builder = if smtp.use_tls {
|
||||
AsyncSmtpTransport::<Tokio1Executor>::starttls_relay(&smtp.server)
|
||||
.map_err(|e| e.to_string())?
|
||||
} else {
|
||||
AsyncSmtpTransport::<Tokio1Executor>::builder_dangerous(&smtp.server)
|
||||
};
|
||||
|
||||
builder = builder.port(smtp.port);
|
||||
|
||||
if !smtp.username.is_empty() {
|
||||
builder = builder.credentials(Credentials::new(
|
||||
smtp.username.clone(),
|
||||
smtp.password.clone(),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(builder.build())
|
||||
}
|
||||
|
||||
pub async fn send_test(&self, smtp: &SmtpConfig) -> (bool, String) {
|
||||
let subject = "[TEST] Supervision - Test de configuration email";
|
||||
let body = "Ceci est un email de test.\n\nSi vous recevez ce message, la configuration SMTP est correcte.\n\n-- Supervision";
|
||||
self.send(smtp, subject, body).await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn not_configured_when_server_empty() {
|
||||
let smtp = SmtpConfig::default();
|
||||
assert!(!is_smtp_configured(&smtp));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn configured_when_all_fields_present() {
|
||||
let smtp = SmtpConfig {
|
||||
server: "smtp.example.com".into(),
|
||||
port: 587,
|
||||
use_tls: true,
|
||||
username: "user".into(),
|
||||
password: "pass".into(),
|
||||
from_email: "from@example.com".into(),
|
||||
to_emails: vec!["to@example.com".into()],
|
||||
};
|
||||
assert!(is_smtp_configured(&smtp));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_configured_when_no_recipients() {
|
||||
let smtp = SmtpConfig {
|
||||
server: "smtp.example.com".into(),
|
||||
port: 587,
|
||||
use_tls: true,
|
||||
username: "user".into(),
|
||||
password: "pass".into(),
|
||||
from_email: "from@example.com".into(),
|
||||
to_emails: vec![],
|
||||
};
|
||||
assert!(!is_smtp_configured(&smtp));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user