Add email notification service for appeals using Spring Mail and Thymeleaf templates.

This commit is contained in:
akastijn 2025-08-16 20:23:35 +02:00
parent f026f24263
commit db642103ed
6 changed files with 100 additions and 1 deletions

View File

@ -36,6 +36,8 @@ dependencies {
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.security:spring-security-oauth2-resource-server")
implementation("org.springframework.security:spring-security-oauth2-jose")
implementation("org.springframework.boot:spring-boot-starter-mail:3.1.5")
implementation("org.springframework.boot:spring-boot-starter-thymeleaf")
//AOP
implementation("org.aspectj:aspectjrt:1.9.19")

View File

@ -5,7 +5,7 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@SpringBootApplication
@SpringBootApplication(scanBasePackages = {"com.alttd.altitudeweb"})
@EnableAspectJAutoProxy
public class AltitudeWebApplication {

View File

@ -10,6 +10,7 @@ import com.alttd.altitudeweb.model.DiscordAppealDto;
import com.alttd.altitudeweb.model.MinecraftAppealDto;
import com.alttd.altitudeweb.model.UpdateMailDto;
import com.alttd.altitudeweb.services.limits.RateLimit;
import com.alttd.altitudeweb.services.mail.AppealMail;
import com.alttd.altitudeweb.setup.Connection;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -29,6 +30,7 @@ import java.util.concurrent.TimeUnit;
public class AppealController implements AppealsApi {
private final AppealDataMapper mapper;
private final AppealMail appealMail;
@RateLimit(limit = 3, timeValue = 1, timeUnit = TimeUnit.HOURS, key = "discordAppeal")
@Override
@ -55,6 +57,8 @@ public class AppealController implements AppealsApi {
});
Appeal appeal = appealCompletableFuture.join();
appealMail.sendAppealNotification(appeal);
AppealResponseDto appealResponseDto = new AppealResponseDto(
appeal.id().toString(),
"Your appeal has been submitted. You will be notified when it has been reviewed.",

View File

@ -0,0 +1,61 @@
package com.alttd.altitudeweb.services.mail;
import com.alttd.altitudeweb.database.web_db.forms.Appeal;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import org.thymeleaf.context.Context;
import org.thymeleaf.spring6.SpringTemplateEngine;
@Slf4j
@Service
@RequiredArgsConstructor
public class AppealMail {
private final JavaMailSender mailSender;
@Value("${spring.mail.username}")
private String fromEmail;
private static final String APPEAL_EMAIL = "appeal@alttd.com";
/**
* Sends an email notification about the appeal to both the user and the appeals team.
*
* @param appeal The appeal object containing all necessary information
*/
public void sendAppealNotification(Appeal appeal) {
try {
sendEmailToAppealsTeam(appeal);
log.info("Appeal notification emails sent successfully for appeal ID: {}", appeal.id());
} catch (Exception e) {
log.error("Failed to send appeal notification emails for appeal ID: {}", appeal.id(), e);
}
}
private SpringTemplateEngine templateEngine;
private void sendEmailToAppealsTeam(Appeal appeal) throws MessagingException {
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setFrom(fromEmail);
helper.setTo(APPEAL_EMAIL);
helper.setReplyTo(appeal.email());
helper.setSubject("New Appeal Submitted - " + appeal.username());
Context context = new Context();
context.setVariable("appeal", appeal);
String content = templateEngine.process("appeal-email", context);
helper.setText(content, true);
mailSender.send(message);
}
}

View File

@ -10,3 +10,10 @@ particles.file_path=${user.home}/.altitudeweb/particles
notification.server.url=${SERVER_IP:10.0.0.107}:${SERVER_PORT:8080}
my-server.address=${SERVER_ADDRESS:https://alttd.com}
logging.level.com.alttd.altitudeweb=INFO
discord.token=${DISCORD_TOKEN}
spring.mail.host=${MAIL_HOST:smtp.zoho.com}
spring.mail.port=${MAIL_PORT:465}
spring.mail.username=${MAIL_USER}
spring.mail.password=${MAIL_PASSWORD}
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true

View File

@ -0,0 +1,25 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="en">
<head>
<meta charset="UTF-8">
<title>Appeal Notification</title>
<style>
ul {
list-style-type: none;
padding-left: 20px;
}
</style>
</head>
<body>
<h2 th:text="'Appeal by ' + ${appeal.username}">Appeal by Username</h2>
<p>Punishment information</p>
<ul>
<li><strong>Username:</strong> <span th:text="${appeal.username}">username</span></li>
<li><strong>UUID:</strong> <span th:text="${appeal.uuid}">uuid</span></li>
<li><strong>Email:</strong> <span th:text="${appeal.email}">email</span></li>
<li><strong>Submitted:</strong> <span th:text="${appeal.createdAt}">date</span></li>
</ul>
<h3>Appeal:</h3>
<p th:text="${appeal.reason}">Reason text</p>
</body>
</html>