Fix agents render: move miniBar outside setTimeout scope, add Array.isArray guard

This commit is contained in:
2026-05-30 05:02:03 +00:00
parent d38d66d147
commit 20c91671da
+26 -31
View File
@@ -827,53 +827,49 @@ function scanShell(tblWrapId, headers, titleEl, scanLabel) {
}
// ── AGENTS ────────────────────────────────────────────────────────────────────
const miniBar = (pct, warn=70, crit=85) => {
if (pct == null) return '—';
const c = pct>=crit?'var(--red)':pct>=warn?'var(--yellow)':'var(--green)';
return `<span style="color:${c}">${Math.round(pct)}%</span>`;
};
async function loadAgents() {
const tbl = document.getElementById('agents-tbl');
const tbl = document.getElementById('agents-tbl');
const title = document.getElementById('agents-title');
// Build empty table shell immediately — no waiting
tbl.innerHTML = `<table id="agents-table">
<thead><tr><th>HOSTNAME</th><th>STATUS</th><th>TYPE</th><th>IP</th><th>METRICS</th><th>LAST SEEN</th><th>REGISTERED</th><th></th></tr></thead>
<tbody id="agents-tbody"></tbody></table>`;
tbl.innerHTML = `<table><thead><tr>
<th>HOSTNAME</th><th>STATUS</th><th>TYPE</th><th>IP</th><th>METRICS</th><th>LAST SEEN</th><th>REGISTERED</th><th></th>
</tr></thead><tbody id="agents-tbody"></tbody></table>`;
title.innerHTML = 'AGENTS <span style="color:var(--dim);font-size:0.6rem;letter-spacing:2px">SCANNING...</span>';
const agents = await api('agents_list');
const tbody = document.getElementById('agents-tbody');
let agents;
try { agents = await api('agents_list'); }
catch(e) { tbl.innerHTML='<div class="empty">ERROR LOADING AGENTS</div>'; title.textContent='AGENTS'; return; }
if (!agents.length) {
if (!Array.isArray(agents) || !agents.length) {
tbl.innerHTML = '<div class="empty">NO AGENTS REGISTERED</div>';
title.textContent = 'AGENTS';
return;
}
// Reveal each agent row with a staggered delay
agents.forEach((a, i) => {
setTimeout(() => {
const tbody = document.getElementById('agents-tbody');
if (!tbody) return;
const m = a.metrics;
const online = a.status === 'online';
const lastSeen = a.last_seen ? (Date.now() - new Date(a.last_seen)) / 1000 : null;
const fresh = lastSeen !== null && lastSeen < 30;
// CPU/RAM mini bars
let meterCell = `<span style="color:var(--dim);font-size:0.65rem">no metrics</span>`;
if (m) {
function miniBar(pct, warn=70, crit=85) {
if (pct == null) return '—';
const c = pct>=crit?'var(--red)':pct>=warn?'var(--yellow)':'var(--green)';
return `<span style="color:${c}">${Math.round(pct)}%</span>`;
}
meterCell = `<span style="font-size:0.65rem">CPU ${miniBar(m.cpu_pct)} · RAM ${miniBar(m.mem_pct)} · DISK ${miniBar(m.disk_pct,80,90)}</span>`;
}
const meterCell = m
? `<span style="font-size:0.65rem">CPU ${miniBar(m.cpu_pct)} · RAM ${miniBar(m.mem_pct)} · DISK ${miniBar(m.disk_pct,80,90)}</span>`
: `<span style="color:var(--dim);font-size:0.65rem">no metrics</span>`;
const row = document.createElement('tr');
row.className = 'agent-row';
row.style.animationDelay = '0s';
row.innerHTML = `
<td>
<span class="dot ${online ? 'dot-green' : 'dot-red'}"></span>
<td><span class="dot ${online?'dot-green':'dot-red'}"></span>
<strong>${esc(a.hostname)}</strong>
${fresh && online ? '<span style="font-size:0.55rem;color:var(--green);margin-left:4px">● LIVE</span>' : ''}
${fresh&&online?'<span style="font-size:0.55rem;color:var(--green);margin-left:4px">● LIVE</span>':''}
</td>
<td>${statusBadge(a.status)}</td>
<td><span class="badge badge-cyan">${esc(a.agent_type||'linux').toUpperCase()}</span></td>
@@ -882,15 +878,14 @@ async function loadAgents() {
<td class="ts">${ago(a.last_seen)}</td>
<td class="ts">${ts(a.created_at)}</td>
<td><button class="btn btn-xs btn-red" onclick="delAgent('${esc(a.agent_id)}','${esc(a.hostname)}')">DEL</button></td>`;
tbody?.appendChild(row);
tbody.appendChild(row);
// Update title counter as agents appear
const found = i + 1;
const online_count = agents.slice(0, found).filter(x => x.status === 'online').length;
if (title) title.innerHTML = found < agents.length
const onlineCt = agents.slice(0, found).filter(x => x.status === 'online').length;
title.innerHTML = found < agents.length
? `AGENTS <span style="color:var(--dim);font-size:0.6rem;letter-spacing:2px">SCANNING... ${found}/${agents.length}</span>`
: `AGENTS <span style="color:var(--cyan);font-size:0.6rem;letter-spacing:2px">${online_count} ONLINE / ${agents.length} TOTAL</span>`;
}, i * 120); // 120ms stagger per agent
: `AGENTS <span style="color:var(--cyan);font-size:0.6rem;letter-spacing:2px">${onlineCt} ONLINE / ${agents.length} TOTAL</span>`;
}, i * 120);
});
}