another huge bugfix push, working releast
This commit is contained in:
Binary file not shown.
@ -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>
|
||||||
@ -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;
|
||||||
|
|||||||
@ -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 non‑servers
|
// 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 15 s */
|
||||||
Watchdog - force reconnect if no data for 15 s
|
|
||||||
========================================================== */
|
|
||||||
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 =>
|
||||||
|
|||||||
Reference in New Issue
Block a user