document.addEventListener('DOMContentLoaded', () => { const nodeGridContainer = document.getElementById('node-grid-container'); const nodeCountSpan = document.getElementById('node-count'); const POLLING_INTERVAL_MS = 3000; // Poll every 3 seconds async function fetchNodeData() { try { const response = await fetch('/nodes/status'); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); renderNodeGrid(data.nodes); } catch (error) { console.error("Error fetching node data:", error); nodeGridContainer.innerHTML = '

Error loading node data. Please check server connection.

'; } } function renderNodeGrid(nodes) { nodeGridContainer.innerHTML = ''; // Clear existing nodes nodeCountSpan.textContent = nodes.length; // Update total node count if (nodes.length === 0) { nodeGridContainer.innerHTML = '

No nodes reporting yet. Start a client!

'; return; } nodes.forEach(node => { const nodeCell = document.createElement('div'); nodeCell.classList.add('node-cell'); nodeCell.classList.add(`node-${node.health_status}`); // Apply health color class // Truncate UUID for display const displayUuid = node.uuid.substring(0, 8) + '...'; nodeCell.innerHTML = `
${displayUuid}
Status: ${node.health_status.toUpperCase()}

UUID: ${node.uuid}

IP: ${node.ip}

Last Seen: ${new Date(node.last_seen).toLocaleTimeString()}

Uptime: ${node.uptime_seconds ? formatUptime(node.uptime_seconds) : 'N/A'}

Load Avg (1m, 5m, 15m): ${node.load_avg ? node.load_avg.join(', ') : 'N/A'}

Memory Usage: ${node.memory_usage_percent ? node.memory_usage_percent.toFixed(2) + '%' : 'N/A'}

`; nodeGridContainer.appendChild(nodeCell); }); } function formatUptime(seconds) { const days = Math.floor(seconds / (3600 * 24)); seconds %= (3600 * 24); const hours = Math.floor(seconds / 3600); seconds %= 3600; const minutes = Math.floor(seconds / 60); const remainingSeconds = Math.floor(seconds % 60); let parts = []; if (days > 0) parts.push(`${days}d`); if (hours > 0) parts.push(`${hours}h`); if (minutes > 0) parts.push(`${minutes}m`); if (remainingSeconds > 0 || parts.length === 0) parts.push(`${remainingSeconds}s`); // Ensure at least seconds are shown return parts.join(' '); } // Initial fetch and then set up polling fetchNodeData(); setInterval(fetchNodeData, POLLING_INTERVAL_MS); });