mirror of
https://github.com/plexusorg/Module-HTTPD.git
synced 2026-06-04 00:56:54 +00:00
94cb2a98c4
Also, resolves the long standing issues #2 and #3
200 lines
7.4 KiB
Java
200 lines
7.4 KiB
Java
package dev.plex.request.impl;
|
|
|
|
import dev.plex.Plex;
|
|
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 <span class=\"font-mono text-foreground\">" + escapeHtml(query) + "</span>.");
|
|
}
|
|
|
|
List<Punishment> 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<Punishment> 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() ? "<span class=\"italic text-muted-foreground/70\">No reason provided</span>" : escapeHtml(rawReason);
|
|
String punisher = resolveName(p.getPunisher());
|
|
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 = "<span class=\"inline-flex h-5 items-center rounded-full bg-destructive/10 px-2 font-mono text-[10px] uppercase tracking-wider text-destructive\">active</span>";
|
|
}
|
|
else
|
|
{
|
|
status = "expired";
|
|
statusChip = "<span class=\"inline-flex h-5 items-center rounded-full bg-muted px-2 font-mono text-[10px] uppercase tracking-wider text-muted-foreground\">expired</span>";
|
|
}
|
|
}
|
|
|
|
String ipRow = "";
|
|
String ipBlob = "";
|
|
if (showIps && p.getIp() != null && !p.getIp().isBlank())
|
|
{
|
|
ipBlob = p.getIp();
|
|
ipRow = """
|
|
<dt class="text-muted-foreground uppercase tracking-wider">IP</dt>
|
|
<dd class="text-foreground/80 break-all">%s</dd>
|
|
""".formatted(escapeHtml(p.getIp()));
|
|
}
|
|
|
|
String searchBlob = escapeHtml((typeName + " " + rawReason + " " + punisher + " " + status + " " + ipBlob).toLowerCase());
|
|
|
|
return """
|
|
<article class="ring-card rounded-2xl bg-card p-5" data-search="%s" data-type="%s" data-status="%s">
|
|
<header class="flex flex-wrap items-center gap-2">
|
|
<span class="inline-flex h-6 items-center rounded-full bg-%s/10 px-2.5 font-mono text-xs font-medium uppercase tracking-wider text-%s">%s</span>
|
|
%s
|
|
</header>
|
|
<p class="mt-3 text-sm">%s</p>
|
|
<dl class="mt-4 grid grid-cols-[max-content_1fr] gap-x-3 gap-y-1.5 border-t border-border/60 pt-3 font-mono text-[11px]">
|
|
<dt class="text-muted-foreground uppercase tracking-wider">Punisher</dt>
|
|
<dd class="text-foreground/80">%s</dd>
|
|
<dt class="text-muted-foreground uppercase tracking-wider">Expires</dt>
|
|
<dd class="text-foreground/80">%s</dd>
|
|
%s
|
|
</dl>
|
|
</article>
|
|
""".formatted(searchBlob, typeName, status, accent, accent, typeName, 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 resolveName(UUID uuid)
|
|
{
|
|
if (uuid == null) return "CONSOLE";
|
|
try
|
|
{
|
|
String name = Plex.get().getSqlPlayerData().getNameByUUID(uuid);
|
|
if (name != null && !name.isBlank()) return name;
|
|
}
|
|
catch (Throwable ignored)
|
|
{
|
|
}
|
|
return 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("\"", """);
|
|
}
|
|
}
|