import { $, safe } from './helpers.js'; import { api } from './api.js'; import { refs, setHealth, renderClusterCards, renderUnits, renderReplicationTable, renderHAResources, renderNonHA, renderNodesTable } from './tables.js'; import { renderVMAdmin } from './admin.js'; // ------ actions ------ async function callAction(act) { const node = refs.nodeInput.value || ''; const d = await api.action(act, node); alert(d.ok ? 'OK' : ('ERROR: ' + (d.error || 'unknown'))); } $('#btnEnable').onclick = () => callAction('enable'); $('#btnDisable').onclick = () => callAction('disable'); $('#btnToggleAll').onclick = () => { document.querySelectorAll('.accordion-collapse').forEach(el => { const bs = bootstrap.Collapse.getOrCreateInstance(el, { toggle: false }); el.classList.contains('show') ? bs.hide() : bs.show(); }); }; // ------ refresh control ------ let REF_TIMER = null; let ac = null; // AbortController dla równoległych fetchy async function doRefresh() { try { if (ac) ac.abort(); ac = new AbortController(); const node = refs.nodeInput.value || ''; // Minimalny szybki zestaw danych — równolegle: const [cluster, nodes, units, repl] = await Promise.allSettled([ api.clusterBrief(), api.nodesSummary(), api.units(node), api.replicationAll() ]); // render const vq = (cluster.value && cluster.value.votequorum) || {}; const unitsMap = (units.value && units.value.units) || {}; const allUnits = Object.values(unitsMap).every(v => v === 'active'); setHealth((vq.quorate === 'yes') && allUnits, vq, allUnits); const gl = document.getElementById('global-loading'); if (gl) gl.remove(); refs.qSummary.textContent = `Quorate: ${safe(vq.quorate)} | members: ${safe(vq.members)} | expected: ${safe(vq.expected)} | total: ${safe(vq.total)} | quorum: ${safe(vq.quorum)}`; renderClusterCards((cluster.value && cluster.value.cluster_status) || []); renderUnits(unitsMap); renderReplicationTable((repl.value || {jobs:[]})); renderHAResources((cluster.value && cluster.value.ha_resources) || []); renderNodesTable((nodes.value && nodes.value.nodes) || []); refs.pvecmPre.textContent = safe(cluster.value && cluster.value.pvecm); refs.cfgtoolPre.textContent = safe(cluster.value && cluster.value.cfgtool); refs.footer.textContent = `node_arg=${safe(node)} | host=${safe(cluster.value && cluster.value.hostname)} | ts=${new Date(((cluster.value && cluster.value.ts) || 0) * 1000).toLocaleString()}`; // pierwszy raz: dociągnij Non-HA + VM Admin w idle if (!doRefresh.didNonHA) { requestIdleCallback(() => renderNonHA().catch(console.error)); doRefresh.didNonHA = true; } if (!doRefresh.didAdmin) { requestIdleCallback(() => renderVMAdmin().catch(console.error)); doRefresh.didAdmin = true; } } catch (e) { console.error(e); } } $('#btnRefresh').onclick = doRefresh; $('#btnAuto').onclick = () => { if (REF_TIMER) { clearInterval(REF_TIMER); REF_TIMER = null; $('#btnAuto').textContent = 'OFF'; $('#btnAuto').classList.remove('btn-success'); $('#btnAuto').classList.add('btn-outline-success'); $('#selInterval').disabled = true; } else { const iv = parseInt($('#selInterval').value || '30000', 10); REF_TIMER = setInterval(doRefresh, iv); $('#btnAuto').textContent = 'ON'; $('#btnAuto').classList.remove('btn-outline-success'); $('#btnAuto').classList.add('btn-success'); $('#selInterval').disabled = false; } }; $('#selInterval').onchange = () => { if (REF_TIMER) { clearInterval(REF_TIMER); REF_TIMER = setInterval(doRefresh, parseInt($('#selInterval').value || '30000', 10)); } }; // initial one-shot load doRefresh().catch(console.error);