package server.debug; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.eclipse.jetty.websocket.api.Session; import server.logic.ws_protocol.JSON.ActiveConnectionsRegistry; import server.logic.ws_protocol.JSON.ConnectionContext; import shine.db.dao.ActiveSessionsDAO; import shine.db.entities.ActiveSessionEntry; import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.ArrayList; import java.util.Comparator; import java.util.List; /** * GET /debug/ws/clients */ public class DebugClientsServlet extends HttpServlet { private static final ObjectMapper MAPPER = new ObjectMapper(); private final DebugTokenProvider tokenProvider; public DebugClientsServlet(DebugTokenProvider tokenProvider) { this.tokenProvider = tokenProvider; } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { if (!tokenProvider.isEnabled()) { writeError(resp, 503, "DEBUG_DISABLED", "Debug API отключен: .debug-token не найден или пуст"); return; } if (!tokenProvider.matchesBearerHeader(req.getHeader("Authorization"))) { writeError(resp, 401, "UNAUTHORIZED", "Неверный Bearer token"); return; } List contexts = ActiveConnectionsRegistry.getInstance().getAllConnectionsSnapshot(); List rows = new ArrayList<>(); for (ConnectionContext ctx : contexts) { Session ws = ctx.getWsSession(); if (ws == null || !ws.isOpen()) continue; ObjectNode row = MAPPER.createObjectNode(); String sessionId = safe(ctx.getSessionId()); row.put("sessionId", sessionId); row.put("login", safe(ctx.getLogin())); row.put("authStatus", ctx.getAuthenticationStatus()); row.put("wsOpen", ws.isOpen()); String remote = safeRemote(ws.getRemoteAddress()); row.put("remoteAddress", remote); row.put("ip", extractIp(ws.getRemoteAddress())); row.put("userAgent", safeUserAgent(ws)); ActiveSessionEntry active = null; try { if (!sessionId.isBlank()) { active = ActiveSessionsDAO.getInstance().getBySessionId(sessionId); } } catch (Exception ignored) { } row.put("clientInfoFromClient", active != null ? safe(active.getClientInfoFromClient()) : ""); row.put("clientInfoFromRequest", active != null ? safe(active.getClientInfoFromRequest()) : ""); row.put("userLanguage", active != null ? safe(active.getUserLanguage()) : ""); row.put("sessionCreatedAtMs", active != null ? active.getSessionCreatedAtMs() : 0L); rows.add(row); } rows.sort(Comparator.comparing(a -> a.path("sessionId").asText(""))); ArrayNode clients = MAPPER.createArrayNode(); rows.forEach(clients::add); ObjectNode payload = MAPPER.createObjectNode(); payload.put("count", clients.size()); payload.set("clients", clients); writeOk(resp, payload); } private static String safeUserAgent(Session ws) { try { if (ws.getUpgradeRequest() == null) return ""; return safe(ws.getUpgradeRequest().getHeader("User-Agent")); } catch (Throwable ignored) { return ""; } } private static String extractIp(SocketAddress addr) { if (addr instanceof InetSocketAddress inet) { if (inet.getAddress() != null) { return safe(inet.getAddress().getHostAddress()); } return safe(inet.getHostString()); } return ""; } private static String safeRemote(SocketAddress addr) { return addr == null ? "" : safe(addr.toString()); } private static String safe(String value) { return value == null ? "" : value.trim(); } private static void writeOk(HttpServletResponse resp, ObjectNode payload) throws IOException { ObjectNode root = MAPPER.createObjectNode(); root.put("ok", true); root.set("payload", payload == null ? MAPPER.createObjectNode() : payload); writeJson(resp, 200, root); } private static void writeError(HttpServletResponse resp, int status, String code, String message) throws IOException { ObjectNode root = MAPPER.createObjectNode(); root.put("ok", false); root.put("code", code); root.put("message", message); writeJson(resp, status, root); } private static void writeJson(HttpServletResponse resp, int status, ObjectNode root) throws IOException { resp.setStatus(status); resp.setCharacterEncoding("UTF-8"); resp.setContentType("application/json; charset=UTF-8"); resp.getWriter().write(MAPPER.writeValueAsString(root)); } }