package dev.plex.request.impl; import dev.plex.cache.DataUtils; import dev.plex.player.PlexPlayer; import dev.plex.punishment.Punishment; import dev.plex.punishment.PunishmentType; import dev.plex.request.AbstractServlet; import dev.plex.request.GetMapping; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.List; import java.util.UUID; public class PunishmentsUIEndpoint extends AbstractServlet { private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm z"); @GetMapping(endpoint = "/punishments/") public String getPunishments(HttpServletRequest request, HttpServletResponse response) { String path = request.getPathInfo(); if (path == null || path.equals("/")) { return readFile(this.getClass().getResourceAsStream("/httpd/punishments.html")); } String query = path.replace("/", "").trim(); if (query.isEmpty()) { return readFile(this.getClass().getResourceAsStream("/httpd/punishments.html")); } PlexPlayer punished = lookupPlayer(query); if (punished == null) { return errorHTML("No player found matching " + escapeHtml(query) + "."); } List punishments = punished.getPunishments(); if (punishments == null || punishments.isEmpty()) { return goodHTML(escapeHtml(punished.getName()) + " has no punishments on record."); } boolean showIps = currentStaff(request) != null; return resultsHTML(punished, punishments, showIps); } private static PlexPlayer lookupPlayer(String query) { try { return DataUtils.getPlayer(UUID.fromString(query)); } catch (IllegalArgumentException ignored) { return DataUtils.getPlayer(query); } } private String resultsHTML(PlexPlayer player, List punishments, boolean showIps) { StringBuilder cards = new StringBuilder(); for (Punishment p : punishments) { cards.append(renderCard(p, showIps)); } String file = readFile(this.getClass().getResourceAsStream("/httpd/punishments_results.html")); file = file.replace("${player_name}", escapeHtml(player.getName())); file = file.replace("${player_uuid}", player.getUuid().toString()); file = file.replace("${punishment_count}", String.valueOf(punishments.size())); file = file.replace("${punishment_label}", punishments.size() == 1 ? "punishment" : "punishments"); file = file.replace("${punishments}", cards.toString()); return file; } private static String renderCard(Punishment p, boolean showIps) { PunishmentType type = p.getType(); String typeName = type == null ? "UNKNOWN" : type.name(); String accent = accentFor(type); String rawReason = (p.getReason() == null || p.getReason().isBlank()) ? "" : p.getReason(); String reason = rawReason.isEmpty() ? "No reason provided" : escapeHtml(rawReason); String punisher = resolvePunisher(p); String endDate = p.getEndDate() == null ? "permanent" : escapeHtml(formatDate(p.getEndDate())); boolean isBan = type == PunishmentType.BAN || type == PunishmentType.TEMPBAN; String status = ""; String statusChip = ""; if (isBan) { if (p.isActive()) { status = "active"; statusChip = "Active"; } else { status = "expired"; statusChip = "Expired"; } } String ipRow = ""; String ipBlob = ""; if (showIps && p.getIp() != null && !p.getIp().isBlank()) { ipBlob = p.getIp(); ipRow = """
IP
%s
""".formatted(escapeHtml(p.getIp())); } String searchBlob = escapeHtml((typeName + " " + rawReason + " " + punisher + " " + status + " " + ipBlob).toLowerCase()); String typeLabel = titleCase(typeName); return """
%s %s

%s

Punisher
%s
Expires
%s
%s
""".formatted(searchBlob, typeName, status, accent, accent, typeLabel, statusChip, reason, escapeHtml(punisher), endDate, ipRow); } private static String accentFor(PunishmentType type) { if (type == null) return "muted-foreground"; return switch (type) { case BAN, SMITE -> "destructive"; case TEMPBAN, MUTE -> "warning"; case KICK, FREEZE -> "primary"; }; } private static String resolvePunisher(Punishment p) { try { String name = Punishment.punisherDisplayName(p); if (name != null && !name.isBlank()) return name; } catch (Throwable ignored) { } UUID uuid = p.getPunisher(); return uuid == null ? "CONSOLE" : uuid.toString(); } private static String formatDate(ZonedDateTime date) { try { return DATE_FMT.format(date); } catch (Throwable t) { return date.toString(); } } private String errorHTML(String message) { String file = readFile(this.getClass().getResourceAsStream("/httpd/punishments_error.html")); file = file.replace("${MESSAGE}", message); return file; } private String goodHTML(String message) { String file = readFile(this.getClass().getResourceAsStream("/httpd/punishments_good.html")); file = file.replace("${MESSAGE}", message); return file; } private static String escapeHtml(String s) { if (s == null) return ""; return s.replace("&", "&") .replace("<", "<") .replace(">", ">") .replace("\"", """); } private static String titleCase(String s) { if (s == null || s.isEmpty()) return s; return Character.toUpperCase(s.charAt(0)) + s.substring(1).toLowerCase(); } }