202 lines
6.8 KiB
Java
202 lines
6.8 KiB
Java
package dev.lions.services;
|
|
|
|
import dev.lions.models.EmailMessage;
|
|
import jakarta.enterprise.context.ApplicationScoped;
|
|
import jakarta.inject.Inject;
|
|
import jakarta.mail.Message;
|
|
import jakarta.mail.MessagingException;
|
|
import jakarta.mail.Session;
|
|
import jakarta.mail.Transport;
|
|
import jakarta.mail.internet.InternetAddress;
|
|
import jakarta.mail.internet.MimeMessage;
|
|
import jakarta.validation.Valid;
|
|
import jakarta.validation.constraints.NotNull;
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
import dev.lions.config.ApplicationConfig;
|
|
import dev.lions.models.EmailTemplate;
|
|
import dev.lions.models.Notification;
|
|
import dev.lions.exceptions.EmailException;
|
|
import dev.lions.utils.TemplateProcessor;
|
|
|
|
import java.util.Map;
|
|
import java.util.Properties;
|
|
|
|
/**
|
|
* Service gérant l'envoi des emails dans l'application.
|
|
* Cette classe assure la configuration SMTP, le traitement des modèles
|
|
* et l'envoi sécurisé des emails.
|
|
*/
|
|
@Slf4j
|
|
@ApplicationScoped
|
|
public class EmailService {
|
|
|
|
@Inject
|
|
ApplicationConfig config;
|
|
|
|
@Inject
|
|
TemplateProcessor templateProcessor;
|
|
|
|
private static final int MAX_RETRY_ATTEMPTS = 3;
|
|
private static final long RETRY_DELAY_MS = 1000;
|
|
|
|
/**
|
|
* Envoie un email basé sur un modèle.
|
|
*
|
|
* @param template Modèle d'email à utiliser
|
|
*/
|
|
public void sendTemplatedEmail(@Valid @NotNull EmailTemplate template) {
|
|
log.info("Préparation de l'envoi d'email avec le modèle : {}",
|
|
template.getTemplateName());
|
|
|
|
try {
|
|
String htmlContent = processTemplate(template);
|
|
|
|
EmailMessage message = EmailMessage.builder()
|
|
.from(config.getSystemEmailAddress())
|
|
.to(template.getRecipient())
|
|
.subject(template.getSubject())
|
|
.htmlContent(htmlContent)
|
|
.build();
|
|
|
|
sendEmailWithRetry(message);
|
|
|
|
} catch (Exception e) {
|
|
log.error("Erreur lors de l'envoi de l'email", e);
|
|
throw new EmailException("Impossible d'envoyer l'email");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Traite le contenu du modèle avec les paramètres fournis.
|
|
*/
|
|
private String processTemplate(EmailTemplate template) {
|
|
log.debug("Traitement du modèle d'email : {}", template.getTemplateName());
|
|
return templateProcessor.process(
|
|
template.getContent(),
|
|
template.getParameters()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Envoie un email avec mécanisme de reprise en cas d'échec.
|
|
*/
|
|
private void sendEmailWithRetry(EmailMessage message) {
|
|
Exception lastException = null;
|
|
|
|
for (int attempt = 1; attempt <= MAX_RETRY_ATTEMPTS; attempt++) {
|
|
try {
|
|
sendEmail(message);
|
|
log.info("Email envoyé avec succès à : {}", message.getTo());
|
|
return;
|
|
} catch (Exception e) {
|
|
lastException = e;
|
|
log.warn("Échec de l'envoi (tentative {}/{})",
|
|
attempt, MAX_RETRY_ATTEMPTS);
|
|
|
|
if (attempt < MAX_RETRY_ATTEMPTS) {
|
|
sleep(RETRY_DELAY_MS * attempt);
|
|
}
|
|
}
|
|
}
|
|
|
|
throw new EmailException(
|
|
"Échec de l'envoi après " + MAX_RETRY_ATTEMPTS + " tentatives");
|
|
}
|
|
|
|
/**
|
|
* Envoie effectif de l'email via SMTP.
|
|
*/
|
|
private void sendEmail(EmailMessage message) throws MessagingException {
|
|
Properties props = configureSmtpProperties();
|
|
Session session = createSmtpSession(props);
|
|
|
|
MimeMessage mimeMessage = new MimeMessage(session);
|
|
configureMimeMessage(mimeMessage, message);
|
|
|
|
Transport.send(mimeMessage);
|
|
}
|
|
|
|
/**
|
|
* Configure les propriétés SMTP.
|
|
*/
|
|
private Properties configureSmtpProperties() {
|
|
Properties props = new Properties();
|
|
props.put("mail.smtp.host", config.getSmtpHost());
|
|
props.put("mail.smtp.port", config.getSmtpPort());
|
|
props.put("mail.smtp.auth", "true");
|
|
props.put("mail.smtp.starttls.enable", "true");
|
|
props.put("mail.smtp.connectiontimeout", "5000");
|
|
props.put("mail.smtp.timeout", "5000");
|
|
|
|
if (config.isSmtpSslEnabled()) {
|
|
props.put("mail.smtp.ssl.enable", "true");
|
|
props.put("mail.smtp.ssl.trust", config.getSmtpHost());
|
|
}
|
|
|
|
return props;
|
|
}
|
|
|
|
/**
|
|
* Crée une session SMTP authentifiée.
|
|
*/
|
|
private Session createSmtpSession(Properties props) {
|
|
return Session.getInstance(props, new jakarta.mail.Authenticator() {
|
|
@Override
|
|
protected jakarta.mail.PasswordAuthentication getPasswordAuthentication() {
|
|
return new jakarta.mail.PasswordAuthentication(
|
|
config.getSmtpUsername().orElseThrow(() ->
|
|
new EmailException("Nom d'utilisateur SMTP manquant")),
|
|
config.getSmtpPassword().orElseThrow(() ->
|
|
new EmailException("Mot de passe SMTP manquant"))
|
|
);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Configure le message MIME avec les paramètres fournis.
|
|
*/
|
|
private void configureMimeMessage(MimeMessage mimeMessage, EmailMessage message)
|
|
throws MessagingException {
|
|
mimeMessage.setFrom(new InternetAddress(message.getFrom()));
|
|
mimeMessage.setRecipients(
|
|
Message.RecipientType.TO,
|
|
InternetAddress.parse(message.getTo())
|
|
);
|
|
mimeMessage.setSubject(message.getSubject());
|
|
mimeMessage.setContent(message.getHtmlContent(), "text/html; charset=utf-8");
|
|
}
|
|
|
|
/**
|
|
* Envoie une notification par email.
|
|
*/
|
|
public void sendNotificationEmail(@NotNull Notification notification) {
|
|
EmailTemplate template = EmailTemplate.builder()
|
|
.templateName("notification-email")
|
|
.recipient(config.getAdminEmailAddress())
|
|
.subject("Notification système : " + notification.getTitle())
|
|
.parameters(Map.of(
|
|
"title", notification.getTitle(),
|
|
"message", notification.getMessage(),
|
|
"type", notification.getType().toString(),
|
|
"timestamp", notification.getTimestamp().toString(),
|
|
"actionUrl", notification.getActionUrl()
|
|
))
|
|
.build();
|
|
|
|
sendTemplatedEmail(template);
|
|
}
|
|
|
|
private void sleep(long milliseconds) {
|
|
try {
|
|
Thread.sleep(milliseconds);
|
|
} catch (InterruptedException e) {
|
|
Thread.currentThread().interrupt();
|
|
throw new EmailException("Interruption pendant la reprise");
|
|
}
|
|
}
|
|
}
|
|
|