-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathEmailService.java
More file actions
119 lines (100 loc) · 4.23 KB
/
EmailService.java
File metadata and controls
119 lines (100 loc) · 4.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
package com.gamzabat.algohub.feature.user.service;
import java.util.concurrent.CompletableFuture;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import com.gamzabat.algohub.constants.EmailTemplateStrings;
import com.gamzabat.algohub.enums.EmailType;
import com.gamzabat.algohub.exception.MessagingRuntimeException;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Service
@RequiredArgsConstructor
@Slf4j
public class EmailService {
@Value("${spring.profiles.active:dev}")
private String activeProfile;
private static final String FROM_ADDRESS = "noreply@algohub.kr";
private final JavaMailSender mailSender;
private final TemplateEngine templateEngine;
@Async
@Retryable(
retryFor = {MessagingException.class},
backoff = @org.springframework.retry.annotation.Backoff(delay = 3000)
)
public CompletableFuture<Void> sendVerificationMail(String to, String token, EmailType type) {
sendMail(to, type, getEmailContent(type, token));
return CompletableFuture.completedFuture(null);
}
@Recover
public CompletableFuture<Void> failedToSendEmail(MessagingRuntimeException e, String to, String token,
EmailType type) {
return handleSendingEmailFailed(e, type.getValue(), to);
}
private void sendMail(String recipient, EmailType type, String content) {
try {
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
helper.setTo(recipient);
helper.setFrom(FROM_ADDRESS);
helper.setSubject(getEmailSubject(type));
helper.setText(content, true);
mailSender.send(message);
} catch (MessagingException e) {
log.warn("Failed to send email, retry. : {}", e.toString());
throw new MessagingRuntimeException(e);
}
}
private CompletableFuture<Void> handleSendingEmailFailed(MessagingRuntimeException e, String purpose,
String email) {
log.error("Failed to send {} email to {} after retries. Exception: {}", purpose, email, e.getMessage(), e);
CompletableFuture<Void> failedFuture = new CompletableFuture<>();
failedFuture.completeExceptionally(e);
return failedFuture;
}
private String getEmailContent(EmailType type, String token) {
switch (type) {
case RESET_PASSWORD: {
Context context = new Context();
String clientEndpoint = getClientEndpoint("reset-password");
context.setVariable("title", EmailTemplateStrings.RESET_PASSWORD_TITLE);
context.setVariable("content", EmailTemplateStrings.RESET_PASSWORD_CONTENT);
context.setVariable("button", EmailTemplateStrings.RESET_PASSWORD_BUTTON);
context.setVariable("url", clientEndpoint + "?token=" + token);
return templateEngine.process("email-template", context);
}
case EMAIL_VALIDATION:
Context context = new Context();
String clientEndpoint = getClientEndpoint("signup");
context.setVariable("title", EmailTemplateStrings.EMAIL_VALIDATION_TITLE);
context.setVariable("content", EmailTemplateStrings.EMAIL_VALIDATION_CONTENT);
context.setVariable("button", EmailTemplateStrings.EMAIL_VALIDATION_BUTTON);
context.setVariable("url", clientEndpoint + "?token=" + token);
return templateEngine.process("email-template", context);
default:
throw new IllegalArgumentException("LOGIC ERROR : Unreachable code block");
}
}
private String getEmailSubject(EmailType type) {
return switch (type) {
case RESET_PASSWORD -> EmailTemplateStrings.RESET_PASSWORD_SUBJECT;
case EMAIL_VALIDATION -> EmailTemplateStrings.EMAIL_VALIDATION_SUBJECT;
default -> throw new IllegalArgumentException("LOGIC ERROR : Unreachable code block");
};
}
private String getClientEndpoint(String apiType) {
if ("prod".equals(activeProfile)) {
return ("https://algohub.kr/" + apiType);
} else {
return ("https://rc.algohub.kr/" + apiType);
}
}
}