// Peers UI — flat admin layout with overview stats and connection details let peerChart = null; async function renderPeers() { if (window.genericFetch) { await window.genericFetch('peers', true); } renderPeerGraph(); } function filterPeers() { if (window.genericFilter) { window.genericFilter('peers'); } } function setPeerStat(id, value) { const el = document.getElementById(id); if (el) el.textContent = value; } function escapePeerHtml(value) { return window.escapeHtml ? window.escapeHtml(value) : String(value); } async function showPeerDetails(peerId) { try { const [peerRes, historyRes] = await Promise.all([ fetch(`/api/peers/${encodeURIComponent(peerId)}`), fetch(`/api/peers/${encodeURIComponent(peerId)}/history`) ]); if (!peerRes.ok || !historyRes.ok) { throw new Error('Failed to fetch peer details'); } const peer = await peerRes.json(); const history = await historyRes.json(); const modal = document.getElementById('peerDetailsModal'); if (!modal) return; const content = document.getElementById('peer-details-content'); if (!content) return; const uptime = peer.uptime ? (window.formatUptime ? window.formatUptime(peer.uptime) : `${Math.floor(peer.uptime / 1000)}s`) : 'N/A'; const connectTime = peer.connectTime ? new Date(peer.connectTime).toLocaleString() : 'N/A'; const lastSeen = peer.metrics?.lastSeen ? new Date(peer.metrics.lastSeen).toLocaleString() : 'N/A'; const totalDuration = peer.metrics?.totalDuration ? (window.formatDuration ? window.formatDuration(peer.metrics.totalDuration) : `${Math.floor(peer.metrics.totalDuration / 1000)}s`) : '0s'; const avgDuration = peer.metrics?.avgDuration ? (window.formatDuration ? window.formatDuration(peer.metrics.avgDuration) : `${Math.floor(peer.metrics.avgDuration / 1000)}s`) : 'N/A'; const statusBadge = peer.connected ? 'Connected' : 'Disconnected'; const blockedBadge = peer.isBlocked ? 'Blocked' : 'Not blocked'; content.innerHTML = `
${escapePeerHtml(peer.id)}No history available.
' : history.slice(-50).reverse().map((event) => `