/* ------------------------------------------------------------ 1. Socket-IO connection & helper functions (unchanged) ------------------------------------------------------------ */ const socket = io(); socket.on('client_summary', renderStatsTable); socket.on('connect_error', err => { safeSetText('client_summary', `Could not connect to server - ${err.message}`); }); socket.on('reconnect', attempt => { safeSetText('client_summary', `Re-connected (attempt ${attempt})`); }); function safeSetText(id, txt) { const el = document.getElementById(id); if (el) el.textContent = txt; } /* ------------------------------------------------------------ 2. Render the table for the *selected* host ------------------------------------------------------------ */ function renderStatsTable(raw) { // Raw may be a string (from Redis) or already parsed by socket.io let payload; if (typeof raw === 'string') { try { payload = JSON.parse(raw); } catch (e) { safeSetText('client_summary', 'Invalid data received'); return; } } else { payload = raw; } if (!Array.isArray(payload) || !payload.length) { safeSetText('client_summary', 'No data available'); return; } /* --------------------------------------------- 2a. Determine the hostname to display --------------------------------------------- */ const urlParams = new URLSearchParams(window.location.search); const selectedHost = urlParams.get('host'); /* --------------------------------------------- 2b. Find the host object in the payload --------------------------------------------- */ const hostObj = payload.find(item => item.hostname === selectedHost) || payload[0]; /* --------------------------------------------- 2c. Extract the Redis data for that host --------------------------------------------- */ const hostData = hostObj && Array.isArray(hostObj.redis_data) ? hostObj.redis_data : []; /* --------------------------------------------- 2d. Pass the host-specific data to the generic renderer --------------------------------------------- */ renderGenericTable('host_metrics', hostData, 'No Stats available'); } /* ------------------------------------------------------------ 3. Table rendering - unchanged from original ------------------------------------------------------------ */ function renderGenericTable(containerId, data, emptyMsg) { const container = document.getElementById(containerId); if (!Array.isArray(data) || !data.length) { container.textContent = emptyMsg; return; } // Merge rows by source name const mergedData = mergeRowsByName(data); // Order the merged rows - priority first const orderedData = orderRows(mergedData); // Build the table from the ordered data const table = buildTable(orderedData); table.id = 'host_metrics_table'; container.innerHTML = ''; container.appendChild(table); } /* ------------------------------------------------------------ 4. Merge rows by source name ------------------------------------------------------------ */ function mergeRowsByName(data) { const groups = {}; // { source: { Metric: [], Data: [] } } data.forEach(row => { const source = row.Source; if (!source) return; if (!groups[source]) { groups[source] = { Metric: [], Data: [] }; } if ('Metric' in row && 'Data' in row) { groups[source].Metric.push(row.Metric); groups[source].Data.push(row.Data); } }); const merged = []; Object.entries(groups).forEach(([source, grp]) => { merged.push({ Source: source, Metric: grp.Metric, Data: grp.Data, }); }); return merged; } /* ------------------------------------------------------------ 5. Order rows - put “System”, “CPU”, “RAM” first ------------------------------------------------------------ */ function orderRows(rows) { const priority = ['System', 'CPU', 'RAM']; const priorityMap = {}; priority.forEach((src, idx) => { priorityMap[src] = idx; }); return [...rows].sort((a, b) => { const aIdx = priorityMap.hasOwnProperty(a.Source) ? priorityMap[a.Source] : Infinity; const bIdx = priorityMap.hasOwnProperty(b.Source) ? priorityMap[b.Source] : Infinity; return aIdx - bIdx; }); } /* ------------------------------------------------------------ 6. Build an HTML table from an array of objects ------------------------------------------------------------ */ function buildTable(data) { const cols = ['Source', 'Metric', 'Data']; const table = document.createElement('table'); // Header const thead = table.createTHead(); const headerRow = thead.insertRow(); cols.forEach(col => { const th = document.createElement('th'); th.textContent = col; headerRow.appendChild(th); }); // Body const tbody = table.createTBody(); data.forEach(item => { const tr = tbody.insertRow(); cols.forEach(col => { const td = tr.insertCell(); const val = item[col]; if (Array.isArray(val)) { val.forEach((v, idx) => { td.id = 'host_metrics_column'; const span = document.createElement('span'); span.textContent = v; td.appendChild(span); if (idx < val.length - 1) td.appendChild(document.createElement('br')); }); } else { td.textContent = val !== undefined ? val : ''; } }); }); return table; }