Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 120 additions & 13 deletions clusters.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
<link rel="icon" href="/favicon.ico" type="image/x-icon">
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="Live cluster telemetry for Jarvis Discord Bot — uptime, system health, and shard status." />
<title>Jarvis Bot - Cluster Status</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.min.js"
integrity="sha384-vsrfeLOOY6KuIYKDlmVH5UiBmgIdB1oEf7p01YgWHuqmOHfZr374+odEv96n9tNC"
crossorigin="anonymous"></script>
<style>
:root {
color-scheme: dark;
Expand Down Expand Up @@ -536,6 +539,15 @@
text-align: center;
}

footer a {
color: var(--accent);
text-decoration: none;
}

footer a:hover {
text-decoration: underline;
}

@media (max-width: 640px) {
body {
padding-top: 24px;
Expand Down Expand Up @@ -651,14 +663,17 @@ <h2>Clusters</h2>
<div class="cluster-container" id="clusters"></div>
</section>

<footer>&copy; 2026 Jarvis Discord Bot | Live cluster telemetry</footer>
<footer>&copy; 2026 Jarvis Discord Bot | <a href="https://jarvisdiscordbot.net/">Back to home</a> | Live cluster
telemetry</footer>
</div>

<script>
const gauges = {};
const latencyCapMs = 300;
const tabStorageKey = 'clusterDashboard.activeTab';
const validTabs = ['overview', 'system', 'latency', 'clusters'];
let loadRequestId = 0;
let refreshIntervalId = null;

const gaugeNeedle = {
id: 'gaugeNeedle',
Expand Down Expand Up @@ -710,6 +725,38 @@ <h2>Clusters</h2>
return '';
}

function destroyGauges() {
Object.values(gauges).forEach(chart => chart.destroy());
Object.keys(gauges).forEach(key => delete gauges[key]);
}

function clearTelemetryDisplay(message = 'Telemetry unavailable') {
destroyGauges();

document.getElementById('api-version').innerText = '--';
document.getElementById('api-version-note').innerText = 'Payload version for this endpoint';
document.getElementById('bot-uptime').innerText = '--';
document.getElementById('server-uptime').innerText = '--';
document.getElementById('platform').innerText = '--';
document.getElementById('cpu-summary').innerText = 'CPU: --';
document.getElementById('cpu-source').innerText = 'Core source: --';
document.getElementById('bot-ram-val').innerText = '--';
document.getElementById('bot-ram-detail').innerText = 'Process RSS and configured limit';
document.getElementById('ram-val').innerText = '--';
document.getElementById('swap-val').innerText = 'Swap: --';
document.getElementById('total-servers').innerText = '--';
document.getElementById('total-users').innerText = 'Total users: --';
document.getElementById('cluster-count').innerText = '0 clusters';

updateMetricBar('bot-ram-bar', 0, 1);
updateMetricBar('ram-bar', 0, 1);

document.getElementById('system-details').innerHTML = '';
document.getElementById('gauge-area').innerHTML = '';
document.getElementById('clusters').innerHTML =
`<div class="empty-state">${escapeHtml(message)}</div>`;
}

function createGauge(id, name, color) {
const container = document.getElementById('gauge-area');
const box = document.createElement('div');
Expand Down Expand Up @@ -814,7 +861,7 @@ <h3 class="detail-card-title">${escapeHtml(title)}</h3>
addDetailRow(hostRows, 'OS', systemStats.os);
addDetailRow(hostRows, 'OS Release', systemStats.os_release);
addDetailRow(hostRows, 'OS Version', systemStats.os_version);
addDetailRow(hostRows, 'Platform', systemStats.platform || 'Linux x86_64');
addDetailRow(hostRows, 'Platform', systemStats.platform || 'Unknown');
addDetailRow(hostRows, 'Machine', systemStats.machine);
addDetailRow(hostRows, 'Processor', systemStats.processor);
addDetailRow(hostRows, 'Python Version', systemStats.python_version);
Expand Down Expand Up @@ -856,10 +903,6 @@ <h3 class="detail-card-title">${escapeHtml(title)}</h3>
container.appendChild(createDetailCard('Process', processRows, 'Bot process and runtime information.'));
}

function formatCoreSummary(systemStats) {
return getCoreSummaryInfo(systemStats).summary;
}

function getCoreSummaryInfo(systemStats) {
const toPositiveNumber = value => {
const numberValue = Number(value);
Expand Down Expand Up @@ -912,6 +955,7 @@ <h3 class="detail-card-title">${escapeHtml(title)}</h3>
tabs.forEach(tab => {
const isActive = tab.dataset.tab === resolvedTab;
tab.setAttribute('aria-selected', String(isActive));
tab.tabIndex = isActive ? 0 : -1;
});

panels.forEach(panel => {
Expand Down Expand Up @@ -942,7 +986,33 @@ <h3 class="detail-card-title">${escapeHtml(title)}</h3>
button.addEventListener('click', () => setActiveTab(button.dataset.tab));
});

document.querySelector('.tabs').addEventListener('keydown', event => {
const tabs = [...document.querySelectorAll('.tab-button')];
const currentIndex = tabs.findIndex(tab => tab.getAttribute('aria-selected') === 'true');
if (currentIndex === -1) {
return;
}

let nextIndex = currentIndex;
if (event.key === 'ArrowRight') {
nextIndex = (currentIndex + 1) % tabs.length;
} else if (event.key === 'ArrowLeft') {
nextIndex = (currentIndex - 1 + tabs.length) % tabs.length;
} else if (event.key === 'Home') {
nextIndex = 0;
} else if (event.key === 'End') {
nextIndex = tabs.length - 1;
} else {
return;
}

event.preventDefault();
setActiveTab(tabs[nextIndex].dataset.tab);
tabs[nextIndex].focus();
});

async function loadClusters() {
const requestId = ++loadRequestId;
const syncStatus = document.getElementById('sync-status');
const syncDetail = document.getElementById('sync-detail');

Expand All @@ -951,6 +1021,9 @@ <h3 class="detail-card-title">${escapeHtml(title)}</h3>
if (!res.ok) throw new Error(`Request failed: ${res.status}`);

const data = await res.json();
if (requestId !== loadRequestId) {
return;
}
const sys = data.system_stats || {};
const clusterList = Array.isArray(data.clusters) ? data.clusters : [];

Expand All @@ -967,7 +1040,7 @@ <h3 class="detail-card-title">${escapeHtml(title)}</h3>
document.getElementById('api-version-note').innerText = apiVersion ? 'Payload version for this endpoint' : 'Version field not present in payload';
document.getElementById('bot-uptime').innerText = data.bot_uptime || 'Unknown';
document.getElementById('server-uptime').innerText = data.server_uptime || 'Unknown';
document.getElementById('platform').innerText = sys.platform || 'Linux x86_64';
document.getElementById('platform').innerText = sys.platform || 'Unknown';
const coreInfo = getCoreSummaryInfo(sys);
document.getElementById('cpu-summary').innerText = `CPU: ${formatPercent(cpuPercent)} | ${coreInfo.summary}`;
document.getElementById('cpu-source').innerText = `Core source: ${coreInfo.source}${coreInfo.missing.length ? ` | missing: ${coreInfo.missing.join(', ')}` : ''}`;
Expand All @@ -985,6 +1058,7 @@ <h3 class="detail-card-title">${escapeHtml(title)}</h3>
let globalUsers = 0;
const clusterContainer = document.getElementById('clusters');
const gaugeArea = document.getElementById('gauge-area');
destroyGauges();
clusterContainer.innerHTML = '';
gaugeArea.innerHTML = '';

Expand All @@ -1004,9 +1078,14 @@ <h3 class="detail-card-title">${escapeHtml(title)}</h3>

const latency = Number(cluster.latency_ms ?? 0);
const gauge = gauges[shardId];
gauge.data.datasets[0].data = [Math.min(latency, latencyCapMs), Math.max(0, latencyCapMs - latency)];
gauge.update();
document.getElementById(`val-${shardId}`).innerText = `${formatNumber(latency, 0)} ms`;
if (gauge) {
gauge.data.datasets[0].data = [Math.min(latency, latencyCapMs), Math.max(0, latencyCapMs - latency)];
gauge.update();
}
const gaugeValue = document.getElementById(`val-${shardId}`);
if (gaugeValue) {
gaugeValue.innerText = `${formatNumber(latency, 0)} ms`;
}

const servers = Number(cluster.servers ?? 0);
const users = Number(cluster.users ?? 0);
Expand Down Expand Up @@ -1054,14 +1133,42 @@ <h3 class="cluster-name">${escapeHtml(clusterName)}</h3>
syncDetail.innerText = `Last refresh: ${new Date().toLocaleTimeString()}`;

} catch (err) {
if (requestId !== loadRequestId) {
return;
}

console.error('Failed to load cluster telemetry', err);
clearTelemetryDisplay('Unable to reach /clusters right now');
syncStatus.innerText = 'Telemetry unavailable';
syncDetail.innerText = 'Unable to reach /clusters right now';
}
}

loadClusters();
setInterval(loadClusters, 10000);
function startRefreshLoop() {
if (refreshIntervalId !== null) {
clearInterval(refreshIntervalId);
}

loadClusters();
refreshIntervalId = setInterval(loadClusters, 10000);
}

function stopRefreshLoop() {
if (refreshIntervalId !== null) {
clearInterval(refreshIntervalId);
refreshIntervalId = null;
}
}

startRefreshLoop();

document.addEventListener('visibilitychange', () => {
if (document.hidden) {
stopRefreshLoop();
} else {
startRefreshLoop();
}
});
</script>
</body>

Expand Down
Loading