const logsContainer = document.getElementById("logs-container"); const logsEmpty = document.getElementById("logs-empty"); const lastUpdate = document.getElementById("last-update"); const clientFilter = document.getElementById("client-filter"); const startDateInput = document.getElementById("start-date"); const endDateInput = document.getElementById("end-date"); const refreshBtn = document.getElementById("refresh-btn"); const prevPageBtn = document.getElementById("prev-page"); const nextPageBtn = document.getElementById("next-page"); const pageInfo = document.getElementById("page-info"); const actionLabels = { client_first_connect: { label: "First Connect", className: "bg-emerald-900/40 text-emerald-200 border border-emerald-800", icon: "fa-plug-circle-bolt", }, client_reconnect: { label: "Reconnect", className: "bg-blue-900/40 text-blue-200 border border-blue-800", icon: "fa-rotate", }, client_disconnect: { label: "Disconnect", className: "bg-orange-900/40 text-orange-200 border border-orange-800", icon: "fa-plug-circle-xmark", }, uninstall: { label: "Uninstall", className: "bg-rose-900/40 text-rose-200 border border-rose-800", icon: "fa-trash", }, }; let page = 1; const pageSize = 50; let total = 0; function formatTimestamp(ts) { if (!ts) return "-"; const date = new Date(ts); return `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`; } function escapeHtml(text) { const div = document.createElement("div"); div.textContent = text || ""; return div.innerHTML; } function parseDetails(details) { if (!details) return ""; try { const parsed = JSON.parse(details); return escapeHtml(JSON.stringify(parsed)); } catch { return escapeHtml(details); } } function getSelectedActions() { return Array.from(document.querySelectorAll(".action-checkbox")) .filter((el) => el.checked) .map((el) => el.value); } function buildQuery() { const params = new URLSearchParams(); params.set("page", String(page)); params.set("pageSize", String(pageSize)); const actions = getSelectedActions(); if (actions.length > 0) { params.set("actions", actions.join(",")); } const clientId = clientFilter?.value?.trim(); if (clientId) { params.set("clientId", clientId); } if (startDateInput?.value) { const ts = new Date(startDateInput.value).getTime(); if (!Number.isNaN(ts)) params.set("startDate", String(ts)); } if (endDateInput?.value) { const ts = new Date(endDateInput.value).getTime(); if (!Number.isNaN(ts)) params.set("endDate", String(ts)); } return params; } function renderLogs(logs) { if (!logsContainer || !logsEmpty) return; logsContainer.innerHTML = ""; if (!logs || logs.length === 0) { logsEmpty.classList.remove("hidden"); return; } logsEmpty.classList.add("hidden"); logsContainer.innerHTML = logs .map((log) => { const meta = actionLabels[log.action] || { label: log.action, className: "bg-slate-800/60 text-slate-200 border border-slate-700", icon: "fa-circle-info", }; const clientId = log.targetClientId || "-"; const shortId = clientId.length > 8 ? `${clientId.slice(0, 8)}...` : clientId; const detailText = parseDetails(log.details); return `
${meta.label} Client: ${escapeHtml(shortId)}
${formatTimestamp(log.timestamp)}
IP: ${escapeHtml(log.ip || "-")} User: ${escapeHtml(log.username || "-")}
${detailText ? `
Details: ${detailText}
` : ""}
`; }) .join(""); } async function loadLogs() { try { const params = buildQuery(); const res = await fetch(`/api/audit-logs?${params.toString()}`); if (!res.ok) { throw new Error("Failed to load logs"); } const data = await res.json(); total = data.total || 0; renderLogs(data.logs || []); const totalPages = Math.max(1, Math.ceil(total / pageSize)); if (pageInfo) pageInfo.textContent = `Page ${page} of ${totalPages}`; if (prevPageBtn) prevPageBtn.disabled = page <= 1; if (nextPageBtn) nextPageBtn.disabled = page >= totalPages; if (lastUpdate) lastUpdate.textContent = new Date().toLocaleTimeString(); } catch (err) { console.error("Failed to load logs", err); if (logsContainer) { logsContainer.innerHTML = `
Failed to load logs. Please try again.
`; } } } function resetAndLoad() { page = 1; loadLogs(); } let debounceTimer = null; function debounceLoad() { if (debounceTimer) window.clearTimeout(debounceTimer); debounceTimer = window.setTimeout(() => resetAndLoad(), 300); } clientFilter?.addEventListener("input", debounceLoad); startDateInput?.addEventListener("change", resetAndLoad); endDateInput?.addEventListener("change", resetAndLoad); refreshBtn?.addEventListener("click", resetAndLoad); Array.from(document.querySelectorAll(".action-checkbox")).forEach((cb) => { cb.addEventListener("change", resetAndLoad); }); prevPageBtn?.addEventListener("click", () => { if (page > 1) { page -= 1; loadLogs(); } }); nextPageBtn?.addEventListener("click", () => { page += 1; loadLogs(); }); loadLogs();