const refreshSeconds = window.HNSDOH_UI_REFRESH_SECONDS || 30; function badgeFor(result) { const badgeClass = result.ok ? "badge badge-ok" : "badge badge-bad"; const label = result.ok ? "UP" : "DOWN"; const latency = result.latency_ms === null ? "" : ` (${result.latency_ms} ms)`; const reason = result.reason || ""; return ` ${label}${latency} ${reason} `; } function historyDots(history, protocol) { if (!history || history.length === 0) { return ""; } const recent = history.slice(-10); const dots = recent .map((entry) => { const r = entry[protocol]; if (!r) return ''; return ``; }) .join(""); return `
${dots}
`; } function rowForNode(node, history) { const udp = node.results.dns_udp; const tcp = node.results.dns_tcp; const doh = node.results.doh; const dot = node.results.dot; return ` ${node.ip} ${badgeFor(udp)}${historyDots(history, "dns_udp")} ${badgeFor(tcp)}${historyDots(history, "dns_tcp")} ${badgeFor(doh)}${historyDots(history, "doh")} ${badgeFor(dot)}${historyDots(history, "dot")} `; } async function refreshStatus() { try { const response = await fetch("/api/status", { cache: "no-store" }); const data = await response.json(); const tableBody = document.getElementById("status-table-body"); const lastUpdated = document.getElementById("last-updated"); if (!data.current) { tableBody.innerHTML = 'No data yet. Waiting for first check.'; return; } lastUpdated.textContent = `Last updated: ${data.current.checked_at}`; const rows = data.current.nodes .map((node) => rowForNode(node, data.history[node.ip] || [])) .join(""); tableBody.innerHTML = rows || 'No nodes discovered.'; } catch (error) { const tableBody = document.getElementById("status-table-body"); tableBody.innerHTML = `Failed to load status: ${error}`; } } refreshStatus(); setInterval(refreshStatus, refreshSeconds * 1000);