another huge bugfix push, working releast

This commit is contained in:
2026-04-19 20:52:32 -07:00
parent 18ed5bd3a2
commit 71e0df2db0
4 changed files with 37 additions and 63 deletions

View File

@ -155,8 +155,8 @@ if (!in_array($mode, $validModes, true)) {
/* ---------- API configuration per mode ---------- */ /* ---------- API configuration per mode ---------- */
$apiConfig = [ $apiConfig = [
'cosmostat' => ['bind' => '10.200.27.20', 'port' => '5000'], 'cosmostat' => ['bind' => '10.200.27.20', 'port' => '5000'],
/*'gali' => ['bind' => '10.200.27.20', 'port' => '5000'], // same as cosmostat*/ /*'gali' => ['bind' => '10.200.27.20', 'port' => '5000'], // space filler */
'drive_health' => ['bind' => '172.25.1.18', 'port' => '5001'], // new API 'drive_health' => ['bind' => '0.0.0.0', 'port' => '5001'], // drive health API
]; ];
/* ---------- Helper: fetch client details ---------- */ /* ---------- Helper: fetch client details ---------- */
@ -251,7 +251,7 @@ function renderSidebar(string $mode){
?> ?>
<nav class="sidebar"> <nav class="sidebar">
<form method="get" id="modeForm"> <form method="get" id="modeForm">
<label for="modeSelect">Mode:</label> <label for="modeSelect"></label>
<select class="select-dark" name="mode" id="modeSelect" onchange="this.form.submit()"> <select class="select-dark" name="mode" id="modeSelect" onchange="this.form.submit()">
<?php foreach ($modes as $key => $label): ?><option value="<?= h($key) ?>" <?= $mode === $key ? 'selected' : '' ?>><?= h($label) ?></option> <?php foreach ($modes as $key => $label): ?><option value="<?= h($key) ?>" <?= $mode === $key ? 'selected' : '' ?>><?= h($label) ?></option>
<?php endforeach; ?></select> <?php endforeach; ?></select>
@ -297,6 +297,13 @@ function renderSidebar(string $mode){
<?php endif; ?> <?php endif; ?>
<?php endif; ?> <?php endif; ?>
<?php if ($mode == 'cosmostat'): ?>
<input type="hidden" name="host" value="<?= h($selectedId) ?>">
<h3>Endpoints</h3>
<ol id="endpointList"></ol>
<?php endif; ?>
<?php if ($mode == 'gali'): ?> <?php if ($mode == 'gali'): ?>
<h3>Shuttle Gali</h3> <h3>Shuttle Gali</h3>
<?php endif; ?> <?php endif; ?>
@ -473,7 +480,6 @@ function renderMainContent(string $mode){
</div> <!-- /wrapper --> </div> <!-- /wrapper -->
<?php if ($mode != 'drive_health'): ?>
<!-- cosmostat javascript --> <!-- cosmostat javascript -->
<script src="socket.io/socket.io.js"></script> <script src="socket.io/socket.io.js"></script>
<script src="src/system_metrics.js"></script> <script src="src/system_metrics.js"></script>
@ -488,9 +494,7 @@ function renderMainContent(string $mode){
help.style.display = help.style.display === 'none' || help.style.display === '' ? 'block' : 'none'; help.style.display = help.style.display === 'none' || help.style.display === '' ? 'block' : 'none';
}); });
</script> </script>
<?php endif; ?>
<?php if ($mode == 'drive_health'): ?>
<!-- drive health javascript --> <!-- drive health javascript -->
<script> <script>
// Removal Handler // Removal Handler
@ -503,7 +507,6 @@ function renderMainContent(string $mode){
document.getElementById('remove_hosts_input').value = ids.join(','); document.getElementById('remove_hosts_input').value = ids.join(',');
}); });
</script> </script>
<?php endif; ?>
</body> </body>
</html> </html>

View File

@ -61,13 +61,17 @@ a:hover { text-decoration: underline; }
/* optional: keep it above other content */ /* optional: keep it above other content */
z-index: 1000; z-index: 1000;
} }
.sidebar h3 { margin: 0 0 .4rem 0; font-size: 1.1rem; } .sidebar h3 { margin: 0 0 .4rem 0; font-size: 1.3rem; }
.sidebar ul { list-style: none; padding: 0; margin: 0; } .sidebar ul { list-style: none; padding: 0; margin: 0; }
.sidebar ol { list-style: none; padding: 0; margin: 0; } .sidebar ol { list-style: none; padding: 0; margin: 0; }
.sidebar li { margin-bottom: .4rem; } .sidebar li { margin-bottom: .4rem; font-size: 1.1rem; }
.sidebar a { color: var(--clr-accent); } .sidebar a { color: var(--clr-accent); }
.sidebar a.active { font-weight: bold; } .sidebar a.active { font-weight: bold; }
/*
.endpointList {
}
*/
.main{ .main{
flex: 1; flex: 1;
padding: 1rem; padding: 1rem;

View File

@ -1,10 +1,7 @@
/* ============================================================== /* system_metrics.js */
system_metrics.js
============================================================== */
(() => { (() => {
/* ========================================================== /* Socket.IO setup */
Socket.IO setup
========================================================== */
const socket = io({ const socket = io({
transports: ['websocket', 'polling'], transports: ['websocket', 'polling'],
reconnection: true, reconnection: true,
@ -15,15 +12,11 @@
pingTimeout: 5000, pingTimeout: 5000,
pingInterval: 25000, pingInterval: 25000,
}); });
/* ========================================================== /* Color constants */
Color constants
========================================================== */
const GREEN = [ 39, 174, 96]; // #27ae60 const GREEN = [ 39, 174, 96]; // #27ae60
const YELLOW = [243, 156, 18]; // #f39c12 const YELLOW = [243, 156, 18]; // #f39c12
const RED = [192, 57, 43]; // #c0392b const RED = [192, 57, 43]; // #c0392b
/* ========================================================== /* Helpers */
Helpers
========================================================== */
const hostTimestamps = {}; // keyed by short_id const hostTimestamps = {}; // keyed by short_id
const toRgb = (r, g, b) => `rgb(${r},${g},${b})`; const toRgb = (r, g, b) => `rgb(${r},${g},${b})`;
const T20 = 20 * 1000; const T20 = 20 * 1000;
@ -53,15 +46,11 @@
const el = document.getElementById(id); const el = document.getElementById(id);
if (el) el.textContent = txt; if (el) el.textContent = txt;
} }
/* ========================================================== /* Get the short_id from the query string */
Get the short_id from the query string
========================================================== */
function getSelectedId() { function getSelectedId() {
return new URLSearchParams(window.location.search).get('host') || ''; return new URLSearchParams(window.location.search).get('host') || '';
} }
/* ========================================================== /* Sidebar building - uses short_id for status key */
Sidebar building - uses short_id for status key
========================================================== */
function buildList(systemList) { function buildList(systemList) {
const ul = document.getElementById('endpointList'); const ul = document.getElementById('endpointList');
@ -71,9 +60,7 @@
return; return;
} }
/* ──────────────────────────────────────── /* Sort: servers first, then by IP */
* Sort: servers first, then by IP
* ──────────────────────────────────────── */
const toInt = ip => { const toInt = ip => {
// guard against undefined / empty string // guard against undefined / empty string
if (!ip) return 0; if (!ip) return 0;
@ -81,12 +68,12 @@
}; };
const sorted = [...systemList].sort((a, b) => { const sorted = [...systemList].sort((a, b) => {
// a. Servers go before nonservers // a. Servers go before non-servers
const aServer = !!a.is_server; const aServer = !!a.is_server;
const bServer = !!b.is_server; const bServer = !!b.is_server;
if (aServer !== bServer) return aServer ? -1 : 1; // true < false if (aServer !== bServer) return aServer ? -1 : 1; // true < false
// b. Same “is_server” status fall back to IP sorting // b. Same “is_server” status - fall back to IP sorting
const aIp = a.active_ip ?? ''; const aIp = a.active_ip ?? '';
const bIp = b.active_ip ?? ''; const bIp = b.active_ip ?? '';
@ -96,16 +83,12 @@
return toInt(aIp) - toInt(bIp); return toInt(aIp) - toInt(bIp);
}); });
/* ──────────────────────────────────────── /* Bail if nothing actually changed */
* Bail if nothing actually changed
* ──────────────────────────────────────── */
const current = Array.from(ul.children).map(li => li.dataset.id); const current = Array.from(ul.children).map(li => li.dataset.id);
const newIds = sorted.map(s => s.short_id); const newIds = sorted.map(s => s.short_id);
if (arraysEqual(current, newIds)) return; // no visual change needed if (arraysEqual(current, newIds)) return; // no visual change needed
/* ──────────────────────────────────────── /* Build the DOM */
* Build the DOM
* ──────────────────────────────────────── */
const selected = getSelectedId().toLowerCase(); const selected = getSelectedId().toLowerCase();
ul.innerHTML = ''; // reset ul.innerHTML = ''; // reset
@ -117,7 +100,7 @@
status.className = 'host-status'; status.className = 'host-status';
status.dataset.id = item.short_id; status.dataset.id = item.short_id;
// • Link display hostname, encode short_id in URL // • Link - display hostname, encode short_id in URL
const a = document.createElement('a'); const a = document.createElement('a');
a.href = '?host=' + encodeURIComponent(item.short_id); a.href = '?host=' + encodeURIComponent(item.short_id);
a.textContent = item.hostname; a.textContent = item.hostname;
@ -130,9 +113,7 @@
}); });
} }
/* ========================================================== /* Update status colors every second */
Update status colors every second
========================================================== */
function updateStatusColors() { function updateStatusColors() {
const nowSec = Date.now() / 1000; const nowSec = Date.now() / 1000;
Object.entries(hostTimestamps).forEach(([id, ts]) => { Object.entries(hostTimestamps).forEach(([id, ts]) => {
@ -145,9 +126,7 @@
}); });
} }
setInterval(updateStatusColors, 1000); setInterval(updateStatusColors, 1000);
/* ========================================================== /* Utility helpers */
Utility helpers
========================================================== */
function arraysEqual(a, b) { function arraysEqual(a, b) {
if (a.length !== b.length) return false; if (a.length !== b.length) return false;
for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false; for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
@ -225,9 +204,7 @@
}); });
return table; return table;
} }
/* ========================================================== /* Handle incoming data */
Handle incoming data
========================================================== */
let lastUpdate = Date.now(); let lastUpdate = Date.now();
function handleSummary(raw) { function handleSummary(raw) {
lastUpdate = Date.now(); // reset watchdog lastUpdate = Date.now(); // reset watchdog
@ -260,9 +237,7 @@
: []; : [];
renderGenericTable('host_metrics', hostData, 'No Stats available'); renderGenericTable('host_metrics', hostData, 'No Stats available');
} }
/* ========================================================== /* Socket event wiring */
Socket event wiring
========================================================== */
socket.on('client_summary', handleSummary); socket.on('client_summary', handleSummary);
socket.on('connect', () => { socket.on('connect', () => {
safeSetText('client_summary', 'Connected'); safeSetText('client_summary', 'Connected');
@ -275,16 +250,12 @@
safeSetText('client_summary', `Re-connected (attempt ${attempt})`); safeSetText('client_summary', `Re-connected (attempt ${attempt})`);
requestSummary(); requestSummary();
}); });
/* ========================================================== /* Request logic */
Request logic
========================================================== */
function requestSummary() { function requestSummary() {
if (!socket.connected) return; // guard against stale emits if (!socket.connected) return; // guard against stale emits
socket.emit('get_client_summary'); // server will reply via client_summary socket.emit('get_client_summary'); // server will reply via client_summary
} }
/* ========================================================== /* Recursive polling */
Recursive polling
========================================================== */
let pollTimer = null; let pollTimer = null;
function pollLoop() { function pollLoop() {
if (!socket.connected) return; if (!socket.connected) return;
@ -294,9 +265,7 @@
socket.on('connect', () => { socket.on('connect', () => {
if (!pollTimer) pollLoop(); if (!pollTimer) pollLoop();
}); });
/* ========================================================== /* Watchdog - force reconnect if no data for 15s */
Watchdog - force reconnect if no data for 15s
========================================================== */
function watchdog() { function watchdog() {
if (Date.now() - lastUpdate > 15000 && socket.connected) { if (Date.now() - lastUpdate > 15000 && socket.connected) {
safeSetText('client_summary', 'No updates - reconnecting...'); safeSetText('client_summary', 'No updates - reconnecting...');
@ -305,9 +274,7 @@
setTimeout(watchdog, 5000); setTimeout(watchdog, 5000);
} }
watchdog(); watchdog();
/* ========================================================== /* Keep the 'active' link in sync when the URL changes */
Keep the 'active' link in sync when the URL changes
========================================================== */
window.addEventListener('popstate', () => { window.addEventListener('popstate', () => {
const selected = getSelectedId().toLowerCase(); const selected = getSelectedId().toLowerCase();
document.querySelectorAll('#endpointList a').forEach(a => document.querySelectorAll('#endpointList a').forEach(a =>