From a4adff38459c1fb47569c0a203fd592e00bdeb10 Mon Sep 17 00:00:00 2001 From: akastijn Date: Tue, 29 Sep 2026 22:07:28 +0200 Subject: [PATCH] Add chat history retrieval: implement `/api/site/chat/history` endpoint, database query, service logic, and permission checks. --- .../altitudeweb/config/SecurityConfig.java | 1 + .../controllers/chat/ChatInfoController.java | 10 +++++++ .../controllers/chat/ChatMessageMapper.java | 25 ++++++++++++++++++ .../services/chat/ChatInfoService.java | 26 +++++++++++++++++++ .../database/chat/ChatLogMapper.java | 9 +++++++ 5 files changed, 71 insertions(+) diff --git a/backend/src/main/java/com/alttd/altitudeweb/config/SecurityConfig.java b/backend/src/main/java/com/alttd/altitudeweb/config/SecurityConfig.java index c342c39..2e5b1b8 100644 --- a/backend/src/main/java/com/alttd/altitudeweb/config/SecurityConfig.java +++ b/backend/src/main/java/com/alttd/altitudeweb/config/SecurityConfig.java @@ -75,6 +75,7 @@ public class SecurityConfig { .requestMatchers("/api/history/admin/**").hasAuthority(PermissionClaimDto.HEAD_MOD.getValue()) .requestMatchers("/api/login/userLogin/**").permitAll() .requestMatchers("/api/site/chat/**").authenticated() + .requestMatchers("/api/site/chat/history").hasAuthority(PermissionClaimDto.HEAD_MOD.getValue()) .requestMatchers("/api/site/chat/send-admin-chat/**").hasAnyAuthority(PermissionClaimDto.HEAD_MOD.getValue(), PermissionClaimDto.MOD.getValue()) .requestMatchers("/api/site/chat/punish/**").hasAnyAuthority(PermissionClaimDto.HEAD_MOD.getValue(), PermissionClaimDto.MOD.getValue()) .requestMatchers("/api/chat/server/send/**").permitAll() diff --git a/backend/src/main/java/com/alttd/altitudeweb/controllers/chat/ChatInfoController.java b/backend/src/main/java/com/alttd/altitudeweb/controllers/chat/ChatInfoController.java index f30d413..4ba9f20 100644 --- a/backend/src/main/java/com/alttd/altitudeweb/controllers/chat/ChatInfoController.java +++ b/backend/src/main/java/com/alttd/altitudeweb/controllers/chat/ChatInfoController.java @@ -1,6 +1,7 @@ package com.alttd.altitudeweb.controllers.chat; import com.alttd.altitudeweb.api.ChatInfoApi; +import com.alttd.altitudeweb.model.ChatMessageDto; import com.alttd.altitudeweb.model.PartyNameDto; import com.alttd.altitudeweb.model.PlayerNameDto; import com.alttd.altitudeweb.services.chat.ChatInfoService; @@ -9,6 +10,8 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RestController; +import java.time.OffsetDateTime; +import java.util.List; import java.util.UUID; @Slf4j @@ -43,4 +46,11 @@ public class ChatInfoController implements ChatInfoApi { .orElse(ResponseEntity.notFound().build()); } + @Override + public ResponseEntity> getChatHistory(OffsetDateTime from, OffsetDateTime to) { + List chatMessageDtoList = chatInfoService.getHistory(from.toInstant(), to.toInstant()).join() + .stream().map(ChatMessageMapper::toDto).toList(); + return ResponseEntity.ok(chatMessageDtoList); + } + } diff --git a/backend/src/main/java/com/alttd/altitudeweb/controllers/chat/ChatMessageMapper.java b/backend/src/main/java/com/alttd/altitudeweb/controllers/chat/ChatMessageMapper.java index d3dd066..4925880 100644 --- a/backend/src/main/java/com/alttd/altitudeweb/controllers/chat/ChatMessageMapper.java +++ b/backend/src/main/java/com/alttd/altitudeweb/controllers/chat/ChatMessageMapper.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import lombok.experimental.UtilityClass; +import java.time.ZoneOffset; import java.util.UUID; @UtilityClass @@ -74,4 +75,28 @@ public class ChatMessageMapper { throw new IllegalStateException("Failed to serialize ChatMessage to JSON: " + chatMessage.getUuid(), e); } } + + public static ChatMessageDto toDto(ChatMessage chatMessage) { + ChatMessageDto chatMessageDto = new ChatMessageDto(); + chatMessageDto.setUuid(chatMessage.getUuid()); + chatMessageDto.setTimestamp(chatMessage.getTimestamp().atOffset(ZoneOffset.UTC)); + chatMessageDto.setServer(chatMessage.getServer()); + chatMessageDto.setType(toDto(chatMessage.getType())); + chatMessageDto.setChannel(chatMessage.getChannel()); + chatMessageDto.setReceiver(chatMessage.getReceiver()); + chatMessageDto.setMessage(chatMessage.getMessageJson()); + chatMessageDto.setBlocked(chatMessage.isNotBlocked()); + return chatMessageDto; + } + + public static ChatMessageDto.TypeEnum toDto(ChatMessageType chatMessageType) { + return switch (chatMessageType) { + case PUBLIC -> ChatMessageDto.TypeEnum.PUBLIC; + case GLOBAL -> ChatMessageDto.TypeEnum.GLOBAL; + case PARTY -> ChatMessageDto.TypeEnum.PARTY; + case GAC -> ChatMessageDto.TypeEnum.GAC; + case MSG -> ChatMessageDto.TypeEnum.MSG; + case CUSTOM -> ChatMessageDto.TypeEnum.CUSTOM; + }; + } } diff --git a/backend/src/main/java/com/alttd/altitudeweb/services/chat/ChatInfoService.java b/backend/src/main/java/com/alttd/altitudeweb/services/chat/ChatInfoService.java index 14675aa..bc33b38 100644 --- a/backend/src/main/java/com/alttd/altitudeweb/services/chat/ChatInfoService.java +++ b/backend/src/main/java/com/alttd/altitudeweb/services/chat/ChatInfoService.java @@ -1,6 +1,9 @@ package com.alttd.altitudeweb.services.chat; +import com.alttd.altitudeweb.controllers.chat.ChatMessage; +import com.alttd.altitudeweb.controllers.chat.ChatMessageMapper; import com.alttd.altitudeweb.database.Databases; +import com.alttd.altitudeweb.database.chat.ChatLogMapper; import com.alttd.altitudeweb.database.chat.NicknameMapper; import com.alttd.altitudeweb.database.chat.PartyMapper; import com.alttd.altitudeweb.database.luckperms.UUIDUsernameMapper; @@ -10,8 +13,11 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import java.time.Instant; +import java.util.List; import java.util.Optional; import java.util.UUID; +import java.util.concurrent.CompletableFuture; @Slf4j @Service @@ -41,4 +47,24 @@ public class ChatInfoService { .runQueryWithResult(sqlSession -> sqlSession.getMapper(PartyMapper.class).getPartyName(id)); } + + public CompletableFuture> getHistory(Instant from, Instant to) { + CompletableFuture> completableFuture = new CompletableFuture<>(); + Connection.getConnection(Databases.CHAT) + .runQuery(sqlSession -> { + log.debug("Loading chat history from {} to {}", from, to); + try { + List list = sqlSession.getMapper(ChatLogMapper.class) + .getChatLogHistory(from.toEpochMilli(), to.toEpochMilli()) + .stream() + .map(ChatMessageMapper::fromDao) + .toList(); + completableFuture.complete(list); + } catch (Exception e) { + log.error("Failed to load history count", e); + } + }); + return completableFuture; + } + } diff --git a/database/src/main/java/com/alttd/altitudeweb/database/chat/ChatLogMapper.java b/database/src/main/java/com/alttd/altitudeweb/database/chat/ChatLogMapper.java index e4b49e7..a9876a8 100644 --- a/database/src/main/java/com/alttd/altitudeweb/database/chat/ChatLogMapper.java +++ b/database/src/main/java/com/alttd/altitudeweb/database/chat/ChatLogMapper.java @@ -16,6 +16,15 @@ public interface ChatLogMapper { """) List getChatLogs(@Param("since") long since); + @Select(""" + SELECT uuid, time_stamp, server, type, channel, receiver, mini_message, blocked + FROM chat_log + WHERE mini_message IS NOT NULL + AND time_stamp >= #{since} + AND time_stamp <= #{to} + """) + List getChatLogHistory(@Param("since") long since, @Param("to") long to); + @Select(""" SELECT party_id FROM chat_users WHERE uuid = #{uuid}