Compare commits
No commits in common. "cdbf862ecf9bae2c080b28c0b845b8185cd2bd7c" and "f886609a0e0c3a99e231fd42ba53b7ca58cc9d1d" have entirely different histories.
cdbf862ecf
...
f886609a0e
|
|
@ -1,123 +1,15 @@
|
||||||
package com.alttd.altitudeweb.controllers.forms;
|
package com.alttd.altitudeweb.controllers.forms;
|
||||||
|
|
||||||
import com.alttd.altitudeweb.api.ApplicationsApi;
|
import com.alttd.altitudeweb.api.ApplicationsApi;
|
||||||
import com.alttd.altitudeweb.controllers.data_from_auth.AuthenticatedUuid;
|
|
||||||
import com.alttd.altitudeweb.database.Databases;
|
|
||||||
import com.alttd.altitudeweb.database.web_db.forms.StaffApplication;
|
|
||||||
import com.alttd.altitudeweb.database.web_db.forms.StaffApplicationMapper;
|
|
||||||
import com.alttd.altitudeweb.database.web_db.mail.EmailVerification;
|
|
||||||
import com.alttd.altitudeweb.database.web_db.mail.EmailVerificationMapper;
|
|
||||||
import com.alttd.altitudeweb.mappers.StaffApplicationDataMapper;
|
|
||||||
import com.alttd.altitudeweb.model.FormResponseDto;
|
import com.alttd.altitudeweb.model.FormResponseDto;
|
||||||
import com.alttd.altitudeweb.model.StaffApplicationDto;
|
import com.alttd.altitudeweb.model.StaffApplicationDto;
|
||||||
import com.alttd.altitudeweb.services.discord.StaffApplicationDiscord;
|
import org.springframework.http.HttpStatusCode;
|
||||||
import com.alttd.altitudeweb.services.limits.RateLimit;
|
|
||||||
import com.alttd.altitudeweb.services.mail.StaffApplicationMail;
|
|
||||||
import com.alttd.altitudeweb.setup.Connection;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.server.ResponseStatusException;
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
@RestController
|
|
||||||
@AllArgsConstructor
|
|
||||||
@RateLimit(limit = 30, timeValue = 1, timeUnit = TimeUnit.HOURS)
|
|
||||||
public class ApplicationController implements ApplicationsApi {
|
public class ApplicationController implements ApplicationsApi {
|
||||||
|
|
||||||
private final StaffApplicationDataMapper staffApplicationDataMapper;
|
|
||||||
private final StaffApplicationMail staffApplicationMail;
|
|
||||||
private final StaffApplicationDiscord staffApplicationDiscord;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ResponseEntity<FormResponseDto> submitStaffApplication(StaffApplicationDto staffApplicationDto) {
|
public ResponseEntity<FormResponseDto> submitStaffApplication(StaffApplicationDto staffApplicationDto) {
|
||||||
UUID userUuid = AuthenticatedUuid.getAuthenticatedUserUuid();
|
throw new ResponseStatusException(HttpStatusCode.valueOf(501), "Staff applications are not yet supported");
|
||||||
|
|
||||||
StaffApplication application = staffApplicationDataMapper.map(userUuid, staffApplicationDto);
|
|
||||||
saveApplication(application);
|
|
||||||
|
|
||||||
Optional<EmailVerification> optionalEmail = fetchEmailVerification(userUuid, application.email());
|
|
||||||
boolean verified = optionalEmail.map(EmailVerification::verified).orElse(false);
|
|
||||||
|
|
||||||
boolean success = true;
|
|
||||||
if (verified) {
|
|
||||||
// Send mail first; only if sent, send to Discord, then mark as sent
|
|
||||||
boolean mailSent = false;
|
|
||||||
try {
|
|
||||||
mailSent = staffApplicationMail.sendApplicationEmail(application);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("Error while sending staff application email for {}", application.id(), e);
|
|
||||||
success = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mailSent) {
|
|
||||||
try {
|
|
||||||
staffApplicationDiscord.sendApplicationToDiscord(application);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("Failed to send staff application {} to Discord", application.id(), e);
|
|
||||||
success = false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
success = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (success) {
|
|
||||||
markAsSent(application.id());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (verified && !success) {
|
|
||||||
return ResponseEntity.internalServerError().build();
|
|
||||||
}
|
|
||||||
|
|
||||||
FormResponseDto response = buildResponse(application, verified);
|
|
||||||
return ResponseEntity.status(201).body(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveApplication(StaffApplication application) {
|
|
||||||
CompletableFuture<Void> saveFuture = new CompletableFuture<>();
|
|
||||||
Connection.getConnection(Databases.DEFAULT)
|
|
||||||
.runQuery(sqlSession -> {
|
|
||||||
try {
|
|
||||||
sqlSession.getMapper(StaffApplicationMapper.class).insert(application);
|
|
||||||
saveFuture.complete(null);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("Failed to insert staff application", e);
|
|
||||||
saveFuture.completeExceptionally(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
saveFuture.join();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Optional<EmailVerification> fetchEmailVerification(UUID userUuid, String email) {
|
|
||||||
CompletableFuture<Optional<EmailVerification>> emailVerificationFuture = new CompletableFuture<>();
|
|
||||||
Connection.getConnection(Databases.DEFAULT)
|
|
||||||
.runQuery(sqlSession -> {
|
|
||||||
EmailVerification verifiedMail = sqlSession.getMapper(EmailVerificationMapper.class)
|
|
||||||
.findByUserAndEmail(userUuid, email);
|
|
||||||
emailVerificationFuture.complete(Optional.ofNullable(verifiedMail));
|
|
||||||
});
|
|
||||||
return emailVerificationFuture.join();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void markAsSent(UUID applicationId) {
|
|
||||||
Connection.getConnection(Databases.DEFAULT)
|
|
||||||
.runQuery(sqlSession -> sqlSession.getMapper(StaffApplicationMapper.class).markAsSent(applicationId));
|
|
||||||
}
|
|
||||||
|
|
||||||
private FormResponseDto buildResponse(StaffApplication application, boolean verified) {
|
|
||||||
String message = verified
|
|
||||||
? "Your staff application has been submitted. You will be notified when it has been reviewed."
|
|
||||||
: "Application created. Please verify your email to complete submission.";
|
|
||||||
return new FormResponseDto(
|
|
||||||
application.id().toString(),
|
|
||||||
message,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
package com.alttd.altitudeweb.mappers;
|
|
||||||
|
|
||||||
import com.alttd.altitudeweb.database.web_db.forms.StaffApplication;
|
|
||||||
import com.alttd.altitudeweb.model.StaffApplicationDto;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
@Service
|
|
||||||
public class StaffApplicationDataMapper {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maps the incoming DTO and the authenticated user's UUID to a StaffApplication entity.
|
|
||||||
* Normalizes and prepares fields as needed (lowercase email, join availableDays, timestamps, ids).
|
|
||||||
*/
|
|
||||||
public StaffApplication map(UUID userUuid, StaffApplicationDto dto) {
|
|
||||||
String email = dto.getEmail() == null ? null : dto.getEmail().toLowerCase();
|
|
||||||
String availableDaysJoined = joinList(dto.getAvailableDays());
|
|
||||||
|
|
||||||
return new StaffApplication(
|
|
||||||
UUID.randomUUID(),
|
|
||||||
userUuid,
|
|
||||||
email,
|
|
||||||
dto.getAge(),
|
|
||||||
dto.getDiscordUsername(),
|
|
||||||
Boolean.TRUE.equals(dto.getMeetsRequirements()),
|
|
||||||
dto.getPronouns(),
|
|
||||||
dto.getJoinDate(),
|
|
||||||
dto.getWeeklyPlaytime(),
|
|
||||||
availableDaysJoined,
|
|
||||||
dto.getAvailableTimes(),
|
|
||||||
dto.getPreviousExperience(),
|
|
||||||
dto.getPluginExperience(),
|
|
||||||
dto.getModeratorExpectations(),
|
|
||||||
dto.getAdditionalInfo(),
|
|
||||||
Instant.now(),
|
|
||||||
null,
|
|
||||||
null
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String joinList(List<String> list) {
|
|
||||||
if (list == null) return null;
|
|
||||||
// Avoid NPEs and trim entries
|
|
||||||
return list.stream()
|
|
||||||
.filter(s -> s != null && !s.isBlank())
|
|
||||||
.map(String::trim)
|
|
||||||
.collect(Collectors.joining(","));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,103 +0,0 @@
|
||||||
package com.alttd.altitudeweb.services.discord;
|
|
||||||
|
|
||||||
import com.alttd.altitudeweb.database.Databases;
|
|
||||||
import com.alttd.altitudeweb.database.discord.OutputChannel;
|
|
||||||
import com.alttd.altitudeweb.database.discord.OutputChannelMapper;
|
|
||||||
import com.alttd.altitudeweb.database.web_db.forms.StaffApplication;
|
|
||||||
import com.alttd.altitudeweb.setup.Connection;
|
|
||||||
import com.alttd.webinterface.send_message.DiscordSender;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.time.ZoneId;
|
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
@Service
|
|
||||||
public class StaffApplicationDiscord {
|
|
||||||
|
|
||||||
private static final String OUTPUT_TYPE = "STAFF_APPLICATION";
|
|
||||||
|
|
||||||
public void sendApplicationToDiscord(StaffApplication application) {
|
|
||||||
// Fetch channels for staff applications
|
|
||||||
CompletableFuture<List<OutputChannel>> channelsFuture = new CompletableFuture<>();
|
|
||||||
Connection.getConnection(Databases.DISCORD).runQuery(sql -> {
|
|
||||||
try {
|
|
||||||
List<OutputChannel> channels = sql.getMapper(OutputChannelMapper.class)
|
|
||||||
.getChannelsWithOutputType(OUTPUT_TYPE);
|
|
||||||
channelsFuture.complete(channels);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("Failed to load output channels for {}", OUTPUT_TYPE, e);
|
|
||||||
channelsFuture.complete(new ArrayList<>());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
List<OutputChannel> channels = channelsFuture.join();
|
|
||||||
if (channels.isEmpty()) {
|
|
||||||
log.warn("No Discord output channels found for type {}. Skipping Discord send.", OUTPUT_TYPE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build embed content
|
|
||||||
List<DiscordSender.EmbedField> fields = new ArrayList<>();
|
|
||||||
fields.add(new DiscordSender.EmbedField(
|
|
||||||
"Applicant",
|
|
||||||
"UUID: " + safe(String.valueOf(application.uuid())) + "\n" +
|
|
||||||
"Discord: " + safe(application.discordUsername()) + "\n" +
|
|
||||||
"Email: " + safe(application.email()) + "\n" +
|
|
||||||
"Age: " + safe(String.valueOf(application.age())) + "\n" +
|
|
||||||
"Meets reqs: " + (application.meetsRequirements() != null && application.meetsRequirements()),
|
|
||||||
false
|
|
||||||
));
|
|
||||||
fields.add(new DiscordSender.EmbedField(
|
|
||||||
"Availability",
|
|
||||||
"Days: " + safe(application.availableDays()) + "\n" +
|
|
||||||
"Times: " + safe(application.availableTimes()),
|
|
||||||
false
|
|
||||||
));
|
|
||||||
fields.add(new DiscordSender.EmbedField(
|
|
||||||
"Experience",
|
|
||||||
"Previous: " + safe(application.previousExperience()) + "\n" +
|
|
||||||
"Plugins: " + safe(application.pluginExperience()) + "\n" +
|
|
||||||
"Expectations: " + safe(application.moderatorExpectations()),
|
|
||||||
false
|
|
||||||
));
|
|
||||||
if (application.additionalInfo() != null && !application.additionalInfo().isBlank()) {
|
|
||||||
fields.add(new DiscordSender.EmbedField(
|
|
||||||
"Additional Info",
|
|
||||||
application.additionalInfo(),
|
|
||||||
false
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Long> channelIds = channels.stream()
|
|
||||||
.map(OutputChannel::channel)
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
Instant timestamp = application.createdAt() != null ? application.createdAt() : Instant.now();
|
|
||||||
DiscordSender.getInstance().sendEmbedToChannels(
|
|
||||||
channelIds,
|
|
||||||
"New Staff Application Submitted",
|
|
||||||
"Join date: " + (application.joinDate() != null ? application.joinDate().toString() : "unknown") +
|
|
||||||
"\nSubmitted: " + formatInstant(timestamp),
|
|
||||||
fields,
|
|
||||||
null,
|
|
||||||
timestamp,
|
|
||||||
null
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String safe(String s) {
|
|
||||||
return s == null ? "unknown" : s;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String formatInstant(Instant instant) {
|
|
||||||
if (instant == null) return "unknown";
|
|
||||||
return instant.atZone(ZoneId.of("UTC"))
|
|
||||||
.format(DateTimeFormatter.ofPattern("yyyy MMMM dd hh:mm a '(UTC)'"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,71 +0,0 @@
|
||||||
package com.alttd.altitudeweb.services.mail;
|
|
||||||
|
|
||||||
import com.alttd.altitudeweb.database.web_db.forms.StaffApplication;
|
|
||||||
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;
|
|
||||||
|
|
||||||
import java.time.ZoneId;
|
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
@Service
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class StaffApplicationMail {
|
|
||||||
|
|
||||||
private final JavaMailSender mailSender;
|
|
||||||
private final SpringTemplateEngine templateEngine;
|
|
||||||
|
|
||||||
@Value("${spring.mail.username}")
|
|
||||||
private String fromEmail;
|
|
||||||
|
|
||||||
private static final String STAFF_APPLICATION_EMAIL = "staff@alttd.com";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends an email with the staff application details to the staff applications team mailbox.
|
|
||||||
* Returns true if the email was sent successfully.
|
|
||||||
*/
|
|
||||||
public boolean sendApplicationEmail(StaffApplication application) {
|
|
||||||
try {
|
|
||||||
doSend(application);
|
|
||||||
log.info("Staff application email sent successfully for application ID: {}", application.id());
|
|
||||||
return true;
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("Failed to send staff application email for application ID: {}", application.id(), e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void doSend(StaffApplication application) throws MessagingException {
|
|
||||||
MimeMessage message = mailSender.createMimeMessage();
|
|
||||||
MimeMessageHelper helper = new MimeMessageHelper(message, true);
|
|
||||||
|
|
||||||
helper.setFrom(fromEmail);
|
|
||||||
helper.setTo(STAFF_APPLICATION_EMAIL);
|
|
||||||
helper.setReplyTo(application.email());
|
|
||||||
helper.setSubject("New Staff Application - " + safe(application.discordUsername()));
|
|
||||||
|
|
||||||
// Prepare template context
|
|
||||||
String createdAt = application.createdAt()
|
|
||||||
.atZone(ZoneId.of("UTC"))
|
|
||||||
.format(DateTimeFormatter.ofPattern("yyyy MMMM dd hh:mm a '(UTC)'"));
|
|
||||||
Context context = new Context();
|
|
||||||
context.setVariable("application", application);
|
|
||||||
context.setVariable("createdAt", createdAt);
|
|
||||||
|
|
||||||
String content = templateEngine.process("staff-application-email", context);
|
|
||||||
helper.setText(content, true);
|
|
||||||
mailSender.send(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String safe(String s) {
|
|
||||||
return s == null ? "unknown" : s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,138 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html xmlns:th="http://www.thymeleaf.org" lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Staff Application</title>
|
|
||||||
<style>
|
|
||||||
@font-face {
|
|
||||||
font-family: 'minecraft-title';
|
|
||||||
src: url('https://beta.alttd.com/public/fonts/minecraft-title.ttf') format('truetype'),
|
|
||||||
url('https://beta.alttd.com/public/fonts/minecraft-title.eot') format('embedded-opentype'),
|
|
||||||
url('https://beta.alttd.com/public/fonts/minecraft-title.svg') format('svg'),
|
|
||||||
url('https://beta.alttd.com/public/fonts/minecraft-title.woff') format('woff');
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'minecraft-text';
|
|
||||||
src: url('https://beta.alttd.com/public/fonts/minecraft-text.ttf') format('truetype'),
|
|
||||||
url('https://beta.alttd.com/public/fonts/minecraft-text.eot') format('embedded-opentype'),
|
|
||||||
url('https://beta.alttd.com/public/fonts/minecraft-text.svg') format('svg'),
|
|
||||||
url('https://beta.alttd.com/public/fonts/minecraft-text.woff') format('woff');
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'opensans';
|
|
||||||
src: url('https://beta.alttd.com/public/fonts/opensans.ttf') format('truetype'),
|
|
||||||
url('https://beta.alttd.com/public/fonts/opensans.eot') format('embedded-opentype'),
|
|
||||||
url('https://beta.alttd.com/public/fonts/opensans.svg') format('svg'),
|
|
||||||
url('https://beta.alttd.com/public/fonts/opensans.woff') format('woff');
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'opensans-bold';
|
|
||||||
src: url('https://beta.alttd.com/public/fonts/opensans-bold.ttf') format('truetype'),
|
|
||||||
url('https://beta.alttd.com/public/fonts/opensans-bold.eot') format('embedded-opentype'),
|
|
||||||
url('https://beta.alttd.com/public/fonts/opensans-bold.svg') format('svg'),
|
|
||||||
url('https://beta.alttd.com/public/fonts/opensans-bold.woff') format('woff');
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: 'minecraft-title', sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.columnSection {
|
|
||||||
width: 80%;
|
|
||||||
max-width: 800px;
|
|
||||||
margin: 0 auto;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.columnContainer {
|
|
||||||
flex: 1 1 200px;
|
|
||||||
min-width: 200px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 0 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
display: block;
|
|
||||||
margin: auto;
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
list-style-type: none;
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
padding-bottom: 7px;
|
|
||||||
}
|
|
||||||
|
|
||||||
li, p {
|
|
||||||
font-family: 'opensans', sans-serif;
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 1150px) {
|
|
||||||
.columnContainer, .columnSection {
|
|
||||||
width: 90%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 690px) {
|
|
||||||
.columnContainer {
|
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<main>
|
|
||||||
<img id="header-img" src="https://beta.alttd.com/public/img/logos/logo.png" alt="The Altitude Minecraft Server" height="159" width="275">
|
|
||||||
<h1 style="text-align: center;" th:text="'Staff application by ' + ${application.discordUsername}">Staff application</h1>
|
|
||||||
|
|
||||||
<section class="columnSection">
|
|
||||||
<div class="columnContainer">
|
|
||||||
<div>
|
|
||||||
<h2>Applicant</h2>
|
|
||||||
<ul>
|
|
||||||
<li><strong>UUID:</strong> <span th:text="${application.uuid}">uuid</span></li>
|
|
||||||
<li><strong>Email:</strong> <span th:text="${application.email}">email</span></li>
|
|
||||||
<li><strong>Discord:</strong> <span th:text="${application.discordUsername}">discord</span></li>
|
|
||||||
<li><strong>Age:</strong> <span th:text="${application.age}">age</span></li>
|
|
||||||
<li><strong>Pronouns:</strong> <span th:text="${application.pronouns}">pronouns</span></li>
|
|
||||||
<li><strong>Join date:</strong> <span th:text="${application.joinDate}">date</span></li>
|
|
||||||
<li><strong>Submitted at:</strong> <span th:text="${createdAt}">date</span></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="columnContainer">
|
|
||||||
<div>
|
|
||||||
<h2>Availability</h2>
|
|
||||||
<ul>
|
|
||||||
<li><strong>Days:</strong> <span th:text="${application.availableDays}">days</span></li>
|
|
||||||
<li><strong>Times:</strong> <span th:text="${application.availableTimes}">times</span></li>
|
|
||||||
<li><strong>Weekly playtime:</strong> <span th:text="${application.weeklyPlaytime}">0</span> hours</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h2>Experience</h2>
|
|
||||||
<p><strong>Previous:</strong><br><span th:text="${application.previousExperience}">previous experience</span></p>
|
|
||||||
<p><strong>Plugins:</strong><br><span th:text="${application.pluginExperience}">plugin experience</span></p>
|
|
||||||
<p><strong>Expectations:</strong><br><span th:text="${application.moderatorExpectations}">moderator expectations</span></p>
|
|
||||||
|
|
||||||
<div th:if="${application.additionalInfo} != null and ${application.additionalInfo} != ''">
|
|
||||||
<h2>Additional info</h2>
|
|
||||||
<p th:text="${application.additionalInfo}">additional info</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
package com.alttd.altitudeweb.database.web_db.forms;
|
|
||||||
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public record StaffApplication(
|
|
||||||
UUID id,
|
|
||||||
UUID uuid,
|
|
||||||
String email,
|
|
||||||
Integer age,
|
|
||||||
String discordUsername,
|
|
||||||
Boolean meetsRequirements,
|
|
||||||
String pronouns,
|
|
||||||
LocalDate joinDate,
|
|
||||||
Integer weeklyPlaytime,
|
|
||||||
String availableDays,
|
|
||||||
String availableTimes,
|
|
||||||
String previousExperience,
|
|
||||||
String pluginExperience,
|
|
||||||
String moderatorExpectations,
|
|
||||||
String additionalInfo,
|
|
||||||
Instant createdAt,
|
|
||||||
Instant sendAt,
|
|
||||||
Long assignedTo
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
package com.alttd.altitudeweb.database.web_db.forms;
|
|
||||||
|
|
||||||
import org.apache.ibatis.annotations.*;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public interface StaffApplicationMapper {
|
|
||||||
|
|
||||||
@Insert("""
|
|
||||||
INSERT INTO staff_applications (
|
|
||||||
id, uuid, email, age, discord_username, meets_requirements, pronouns, join_date,
|
|
||||||
weekly_playtime, available_days, available_times, previous_experience, plugin_experience,
|
|
||||||
moderator_expectations, additional_info, created_at, send_at, assigned_to
|
|
||||||
) VALUES (
|
|
||||||
#{id}, #{uuid}, #{email}, #{age}, #{discordUsername}, #{meetsRequirements}, #{pronouns}, #{joinDate},
|
|
||||||
#{weeklyPlaytime},
|
|
||||||
#{availableDays},
|
|
||||||
#{availableTimes}, #{previousExperience}, #{pluginExperience},
|
|
||||||
#{moderatorExpectations}, #{additionalInfo}, #{createdAt}, #{sendAt}, #{assignedTo}
|
|
||||||
)
|
|
||||||
""")
|
|
||||||
void insert(StaffApplication application);
|
|
||||||
|
|
||||||
@Update("""
|
|
||||||
UPDATE staff_applications SET send_at = NOW()
|
|
||||||
WHERE id = #{id}
|
|
||||||
""")
|
|
||||||
void markAsSent(@Param("id") UUID id);
|
|
||||||
}
|
|
||||||
|
|
@ -5,7 +5,6 @@ import com.alttd.altitudeweb.database.web_db.KeyPairMapper;
|
||||||
import com.alttd.altitudeweb.database.web_db.PrivilegedUserMapper;
|
import com.alttd.altitudeweb.database.web_db.PrivilegedUserMapper;
|
||||||
import com.alttd.altitudeweb.database.web_db.SettingsMapper;
|
import com.alttd.altitudeweb.database.web_db.SettingsMapper;
|
||||||
import com.alttd.altitudeweb.database.web_db.forms.AppealMapper;
|
import com.alttd.altitudeweb.database.web_db.forms.AppealMapper;
|
||||||
import com.alttd.altitudeweb.database.web_db.forms.StaffApplicationMapper;
|
|
||||||
import com.alttd.altitudeweb.database.web_db.mail.EmailVerificationMapper;
|
import com.alttd.altitudeweb.database.web_db.mail.EmailVerificationMapper;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.ibatis.session.SqlSession;
|
import org.apache.ibatis.session.SqlSession;
|
||||||
|
|
@ -24,7 +23,6 @@ public class InitializeWebDb {
|
||||||
configuration.addMapper(KeyPairMapper.class);
|
configuration.addMapper(KeyPairMapper.class);
|
||||||
configuration.addMapper(PrivilegedUserMapper.class);
|
configuration.addMapper(PrivilegedUserMapper.class);
|
||||||
configuration.addMapper(AppealMapper.class);
|
configuration.addMapper(AppealMapper.class);
|
||||||
configuration.addMapper(StaffApplicationMapper.class);
|
|
||||||
configuration.addMapper(EmailVerificationMapper.class);
|
configuration.addMapper(EmailVerificationMapper.class);
|
||||||
}).join()
|
}).join()
|
||||||
.runQuery(sqlSession -> {
|
.runQuery(sqlSession -> {
|
||||||
|
|
@ -33,7 +31,6 @@ public class InitializeWebDb {
|
||||||
createPrivilegedUsersTable(sqlSession);
|
createPrivilegedUsersTable(sqlSession);
|
||||||
createPrivilegesTable(sqlSession);
|
createPrivilegesTable(sqlSession);
|
||||||
createAppealTable(sqlSession);
|
createAppealTable(sqlSession);
|
||||||
createStaffApplicationsTable(sqlSession);
|
|
||||||
createUserEmailsTable(sqlSession);
|
createUserEmailsTable(sqlSession);
|
||||||
});
|
});
|
||||||
log.debug("Initialized WebDb");
|
log.debug("Initialized WebDb");
|
||||||
|
|
@ -129,37 +126,6 @@ public class InitializeWebDb {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void createStaffApplicationsTable(@NotNull SqlSession sqlSession) {
|
|
||||||
String query = """
|
|
||||||
CREATE TABLE IF NOT EXISTS staff_applications (
|
|
||||||
id UUID NOT NULL DEFAULT (UUID()) PRIMARY KEY,
|
|
||||||
uuid UUID NOT NULL,
|
|
||||||
email VARCHAR(320) NOT NULL,
|
|
||||||
age INT NOT NULL,
|
|
||||||
discord_username VARCHAR(32) NOT NULL,
|
|
||||||
meets_requirements BOOLEAN NOT NULL,
|
|
||||||
pronouns VARCHAR(32) NULL,
|
|
||||||
join_date DATE NOT NULL,
|
|
||||||
weekly_playtime INT NOT NULL,
|
|
||||||
available_days TEXT NOT NULL,
|
|
||||||
available_times TEXT NOT NULL,
|
|
||||||
previous_experience TEXT NOT NULL,
|
|
||||||
plugin_experience TEXT NOT NULL,
|
|
||||||
moderator_expectations TEXT NOT NULL,
|
|
||||||
additional_info TEXT NULL,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
|
||||||
send_at TIMESTAMP NULL,
|
|
||||||
assigned_to BIGINT UNSIGNED NULL,
|
|
||||||
FOREIGN KEY (uuid) REFERENCES privileged_users(uuid) ON DELETE CASCADE ON UPDATE CASCADE
|
|
||||||
);
|
|
||||||
""";
|
|
||||||
try (Statement statement = sqlSession.getConnection().createStatement()) {
|
|
||||||
statement.execute(query);
|
|
||||||
} catch (SQLException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void createAppealTable(@NotNull SqlSession sqlSession) {
|
private static void createAppealTable(@NotNull SqlSession sqlSession) {
|
||||||
String query = """
|
String query = """
|
||||||
CREATE TABLE IF NOT EXISTS appeals (
|
CREATE TABLE IF NOT EXISTS appeals (
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user