export const RCON_COMMAND_REQUESTED = "games.minecraft.account-manager.rcon.command.requested"; export const RCON_COMMAND_COMPLETED = "games.minecraft.account-manager.rcon.command.completed"; export type RconHistoryEvent = { id: string; time: Date; correlationId: string | null; data: Record; }; export type RconCommandHistoryRow = { eventId: string; time: Date; command: string; serverId: string; serverName: string; adminEmail: string | null; adminName: string | null; status: "pending" | "succeeded" | "failed"; reason: string | null; durationMs: number | null; }; type SearchParams = Record; function first(value: string | string[] | undefined) { return (Array.isArray(value) ? value[0] : value)?.trim() ?? ""; } function text(data: Record, key: string) { const value = data[key]; return typeof value === "string" && value ? value : null; } export function normalizeRconHistoryFilters(query: SearchParams, availableServerIds: string[]) { const requestedServerId = first(query.server); return { command: first(query.command).slice(0, 1024), admin: first(query.admin).slice(0, 320), serverId: availableServerIds.includes(requestedServerId) ? requestedServerId : "", }; } export function buildRconCommandHistory( requestedEvents: RconHistoryEvent[], completedEvents: RconHistoryEvent[], ): RconCommandHistoryRow[] { const completions = new Map(completedEvents .filter((event) => event.correlationId) .map((event) => [event.correlationId, event])); return requestedEvents.flatMap((event) => { const command = text(event.data, "command"); const serverId = text(event.data, "serverId"); const serverName = text(event.data, "name"); if (!command || !serverId || !serverName) return []; const completed = event.correlationId ? completions.get(event.correlationId) : undefined; const success = completed?.data.success; const duration = completed?.data.durationMs; return [{ eventId: event.id, time: event.time, command, serverId, serverName, adminEmail: text(event.data, "adminEmail"), adminName: text(event.data, "adminName"), status: success === true ? "succeeded" as const : success === false ? "failed" as const : "pending" as const, reason: completed ? text(completed.data, "reason") : null, durationMs: typeof duration === "number" && Number.isFinite(duration) ? duration : null, }]; }); }