sync for dev 033026

This commit is contained in:
2026-03-30 19:59:39 -07:00
parent ba849b150c
commit 36917ffa39
18 changed files with 317 additions and 84 deletions

View File

@ -6,7 +6,7 @@ function h(string $s): string
}
// Load API data
$raw_api_settings = file('/opt/api_settings/cosmostat_settings.yaml',
$raw_api_settings = file('/app/cosmostat_settings.yaml',
FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
$api_settings = [];

View File

@ -15,25 +15,20 @@
pingTimeout: 5000,
pingInterval: 25000,
});
/* ==========================================================
Color constants
========================================================== */
const GREEN = [ 39, 174, 96]; // #27ae60
const YELLOW = [243, 156, 18]; // #f39c12
const RED = [192, 57, 43]; // #c0392b
/* ==========================================================
Helpers
========================================================== */
const hostTimestamps = {}; // keyed by short_id
const toRgb = (r, g, b) => `rgb(${r},${g},${b})`;
const T20 = 20 * 1000;
const T40 = 40 * 1000;
const T60 = 60 * 1000;
function getFreshnessColor(ageMs) {
if (ageMs <= T20) {
return toRgb(...GREEN);
@ -54,19 +49,16 @@
}
return toRgb(...RED);
}
function safeSetText(id, txt) {
const el = document.getElementById(id);
if (el) el.textContent = txt;
}
/* ==========================================================
Get the short_id from the query string
========================================================== */
function getSelectedId() {
return new URLSearchParams(window.location.search).get('host') || '';
}
/* ==========================================================
Sidebar building - uses short_id for status key
========================================================== */
@ -75,30 +67,24 @@
const current = Array.from(ul.children).map(li => li.dataset.id);
const newIds = systemList.map(s => s.short_id);
if (arraysEqual(current, newIds)) return;
const selected = getSelectedId().toLowerCase();
ul.innerHTML = ''; // reset list
systemList.forEach(item => {
const li = document.createElement('li');
// status dot - keyed by short_id
const status = document.createElement('span');
status.className = 'host-status';
status.dataset.id = item.short_id;
// link - display hostname, encode short_id in URL
const a = document.createElement('a');
a.href = '?host=' + encodeURIComponent(item.short_id);
a.textContent = item.hostname;
if (item.short_id.toLowerCase() === selected) a.classList.add('active');
li.appendChild(status);
li.appendChild(a);
ul.appendChild(li);
});
}
/* ==========================================================
Update status colors every second
========================================================== */
@ -113,9 +99,7 @@
if (span) span.style.backgroundColor = color;
});
}
setInterval(updateStatusColors, 1000);
/* ==========================================================
Utility helpers
========================================================== */
@ -124,7 +108,6 @@
for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
return true;
}
function renderGenericTable(containerId, data, emptyMsg) {
const container = document.getElementById(containerId);
if (!Array.isArray(data) || !data.length) {
@ -138,7 +121,6 @@
container.innerHTML = '';
container.appendChild(table);
}
function mergeRowsByName(rows) {
const groups = {}; // { Source: { Metric: [], Data: [] } }
rows.forEach(r => {
@ -156,7 +138,6 @@
Data: g.Data,
}));
}
function orderRows(rows) {
const priority = ['System', 'CPU', 'RAM'];
const map = {};
@ -167,7 +148,6 @@
return ai - bi;
});
}
function buildTable(rows) {
const cols = ['Source', 'Metric', 'Data'];
const table = document.createElement('table');
@ -200,12 +180,10 @@
});
return table;
}
/* ==========================================================
Handle incoming data
========================================================== */
let lastUpdate = Date.now();
function handleSummary(raw) {
lastUpdate = Date.now(); // reset watchdog
let payload;
@ -215,25 +193,20 @@
return;
}
} else payload = raw;
if (!Array.isArray(payload) || !payload.length) {
safeSetText('client_summary', 'No data available');
return;
}
// Build the list first (so <span> elements exist)
buildList(payload);
// Store the timestamp for every short_id
payload.forEach(hostObj => {
if (hostObj.short_id && hostObj.data_timestamp) {
hostTimestamps[hostObj.short_id] = hostObj.data_timestamp; // seconds
}
});
// Immediately update colors for the current view
updateStatusColors();
// Metric table for selected host
const selectedId = getSelectedId();
const hostObj = payload.find(h => h.short_id === selectedId) || payload[0];
@ -242,7 +215,6 @@
: [];
renderGenericTable('host_metrics', hostData, 'No Stats available');
}
/* ==========================================================
Socket event wiring
========================================================== */
@ -258,7 +230,6 @@
safeSetText('client_summary', `Re-connected (attempt ${attempt})`);
requestSummary();
});
/* ==========================================================
Request logic
========================================================== */
@ -266,7 +237,6 @@
if (!socket.connected) return; // guard against stale emits
socket.emit('get_client_summary'); // server will reply via client_summary
}
/* ==========================================================
Recursive polling
========================================================== */
@ -279,7 +249,6 @@
socket.on('connect', () => {
if (!pollTimer) pollLoop();
});
/* ==========================================================
Watchdog - force reconnect if no data for 15s
========================================================== */
@ -291,7 +260,6 @@
setTimeout(watchdog, 5000);
}
watchdog();
/* ==========================================================
Keep the 'active' link in sync when the URL changes
========================================================== */