import { safe, ensureArr, badge, rowHTML, humanBytes, kvGrid, fmtSeconds, pick } from './helpers.js';
export function renderNodeDetailCard(d) {
const st = d.status || {};
const ver = d.version || {};
const tm = d.time || {};
const netcfg = ensureArr(d.network_cfg);
const disks = ensureArr(d.disks);
const subscription = d.subscription || {};
// online detect
const isOn = /online|running/i.test(st.status || '') ||
/online/i.test(st.hastate || '') || (st.uptime > 0) ||
(st.cpu != null && st.maxcpu != null) || (st.memory && st.memory.total > 0);
const statusTxt = isOn ? 'online' : (st.status || 'offline');
const sB = isOn ? badge(statusTxt, 'ok') : badge(statusTxt, 'err');
const mem = st.memory || {};
const root = st.rootfs || {};
const load = Array.isArray(st.loadavg) ? st.loadavg.join(' ') : (st.loadavg || '');
const cpuinfo = st.cpuinfo || {};
const boot = st['boot-info'] || st.boot_info || {};
const curKernel = st['current-kernel'] || st.current_kernel || {};
const ramStr = (mem.used != null && mem.available != null && mem.total != null)
? `${humanBytes(mem.used)} used / ${humanBytes(mem.available)} free / ${humanBytes(mem.total)} total`
: (mem.total != null ? humanBytes(mem.total) : '—');
const tech = {
'PVE version': pick(st.pveversion, ver.pvemanager, ver['pve-manager']),
'Kernel': pick(st.kversion, curKernel.release, ver.kernel, ver.release),
'CPU model': pick(cpuinfo.model, st['cpu-model'], ver['cpu-model'], ver.cpu),
'Architecture': pick(curKernel.machine, ver.arch, st.architecture, st.arch),
'RAM': ramStr,
'Boot mode': pick(boot.mode) ? String(boot.mode).toUpperCase() : '—',
'Secure Boot': (boot.secureboot === 1 || boot.secureboot === '1') ? 'enabled' :
(boot.secureboot === 0 || boot.secureboot === '0') ? 'disabled' : '—'
};
const top = `
${safe(d.node)}
${sB}
CPU: ${safe(((st.cpu??null)!==null)?(st.cpu*100).toFixed(1)+'%':'—')}
Load: ${safe(load)}
Uptime: ${fmtSeconds(st.uptime)}
`;
const memCard = `
Memory
${(mem.used != null && mem.total != null) ? `${humanBytes(mem.used)} / ${humanBytes(mem.total)} (${((mem.used/mem.total)*100).toFixed(1)}%)` : '—'}
RootFS
${(root.used != null && root.total != null) ? `${humanBytes(root.used)} / ${humanBytes(root.total)} (${((root.used/root.total)*100).toFixed(1)}%)` : '—'}
Kernel / QEMU
${safe(tech['Kernel'])} / ${safe(pick(ver.qemu, ver['running-qemu']))}
Time
${safe(tm.localtime)} ${tm.timezone ? `(${tm.timezone})` : ''}
`;
const sysDetails = kvGrid(tech, Object.keys(tech), {
'PVE version': 'PVE version','Kernel':'Kernel version','CPU model':'CPU model',
'Architecture':'Arch','RAM':'RAM (used/free/total)','Boot mode':'Boot mode','Secure Boot':'Secure Boot'
});
const netRows = ensureArr(netcfg).map(n => rowHTML([safe(n.iface||n.ifname), safe(n.type), safe(n.method||n.autostart), safe(n.bridge_ports||n.address||'—'), safe(n.cidr||n.netmask||'—'), safe(n.comments||'')]));
const netCfgTable = `
IF | Type | Method | Ports/Address | Netmask/CIDR | Comment |
${netRows.length ? netRows.join('') : rowHTML(['—','—','—','—','—','—'])}
`;
const diskRows = ensureArr(disks).map(dv => rowHTML([safe(dv.devpath||dv.kname||dv.dev), safe(dv.model), safe(dv.size?humanBytes(dv.size):'—'), safe(dv.health||dv.wearout||'—'), safe(dv.serial||'—')]));
const diskTable = `
Device | Model | Size | Health | Serial |
${diskRows.length ? diskRows.join('') : rowHTML(['—','—','—','—','—'])}
`;
// Subscription: ukryj, gdy notfound/No subscription key
const subTxt = (subscription.message||'') + ' ' + (subscription.status||'');
const hideSub = /notfound/i.test(subTxt) || /no subscription key/i.test(subTxt);
const subBox = hideSub ? '' : `
Subscription
Status: ${badge(safe(subscription.status||'unknown'), /active|valid/i.test(subscription.status||'') ? 'ok':'warn')}
${subscription.productname ? `
Product: ${safe(subscription.productname)}
` : ''}
${subscription.message ? `
${safe(subscription.message)}
` : ''}
`;
const rawId = `raw-node-${safe(d.node)}`;
const rawBtn = ``;
const rawBox = `${JSON.stringify(d, null, 2)}
`;
return `${top}${memCard}
System details
${sysDetails}
Network (config)
${netCfgTable}
${subBox}
${rawBtn}${rawBox}`;
}