Initial commit

This commit is contained in:
Kendel Christoph
2025-12-27 20:13:26 +01:00
commit 90d55cad7e
24 changed files with 1355 additions and 0 deletions

115
app/public/js/status.js Executable file
View File

@@ -0,0 +1,115 @@
// Simple status dashboard
const DATA_URL = 'data/status.json';
let autoRefresh = true;
let refreshTimer = null;
function formatMB(mb) {
return `${mb.toFixed(2)} MB`;
}
function formatPercent(p) {
return `${p.toFixed(2)}%`;
}
function setRing(el, percent) {
const deg = Math.min(100, Math.max(0, percent)) * 3.6; // 100% -> 360deg
el.style.setProperty('--deg', `${deg}deg`);
}
function renderSystem(system) {
const cpuPercent = system?.cpu?.percent ?? 0;
const ring = document.getElementById('cpu-ring');
setRing(ring, cpuPercent);
document.getElementById('cpu-percent').textContent = formatPercent(cpuPercent);
const la = system?.cpu?.load_avg ?? {};
document.getElementById('load-1m').textContent = (la['1m'] ?? '');
document.getElementById('load-5m').textContent = (la['5m'] ?? '');
document.getElementById('load-15m').textContent = (la['15m'] ?? '');
const mem = system?.memory ?? {};
const pct = mem.percent ?? 0;
document.getElementById('mem-bar').style.width = `${pct}%`;
document.getElementById('mem-percent').textContent = formatPercent(pct);
document.getElementById('mem-used').textContent = formatMB(mem.used_mb ?? 0);
document.getElementById('mem-total').textContent = formatMB(mem.total_mb ?? 0);
}
function portBadge(p) {
const host = p.host_ip ? `${p.host_ip}` : '';
const hostPort = p.host_port ? `:${p.host_port}` : '';
const container = p.container_port || '';
const text = `${host}${hostPort}${container}`.trim();
return `<span class="port">${text || '—'}</span>`;
}
function renderContainers(list) {
const q = document.getElementById('search').value.toLowerCase();
const root = document.getElementById('container-list');
root.innerHTML = '';
const filtered = list.filter(c => {
const s = `${c.name} ${c.image}`.toLowerCase();
return s.includes(q);
});
document.getElementById('container-count').textContent = `${filtered.length} / ${list.length} shown`;
filtered.forEach(c => {
const statusClass = (c.status === 'running') ? 'status' : 'status stopped';
const ports = (c.openPorts && c.openPorts.length) ? c.openPorts.map(portBadge).join('') : '<span class="muted">No published ports</span>';
const cpu = c.resources?.cpu_percent ?? 0;
const memMb = c.resources?.memory_mb ?? 0;
const memPct = c.resources?.memory_percent ?? 0;
const row = document.createElement('div');
row.className = 'row';
row.innerHTML = `
<div class="name">${c.name}<div class="muted">${c.image}</div></div>
<div><span class="${statusClass}">${c.status}</span></div>
<div class="ports">${ports}</div>
<div>
<span class="badge">CPU: ${formatPercent(cpu)}</span>
<span class="badge">Mem: ${formatMB(memMb)} (${formatPercent(memPct)})</span>
</div>
<div class="muted small">${c.id.slice(0,12)}</div>
`;
root.appendChild(row);
});
}
async function loadStatus() {
try {
const res = await fetch(`${DATA_URL}?t=${Date.now()}`, { cache: 'no-store' });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
document.getElementById('last-update').textContent = new Date(data.timestamp).toLocaleString();
renderSystem(data.system);
renderContainers(data.containers || []);
} catch (err) {
console.error('Failed to load status:', err);
document.getElementById('last-update').textContent = 'failed';
}
}
function startAutoRefresh() {
if (refreshTimer) clearInterval(refreshTimer);
refreshTimer = setInterval(() => {
if (autoRefresh) loadStatus();
}, 5000);
}
// Events
window.addEventListener('DOMContentLoaded', () => {
document.getElementById('toggle-refresh').addEventListener('click', () => {
autoRefresh = !autoRefresh;
document.getElementById('toggle-refresh').textContent = autoRefresh ? 'Pause' : 'Resume';
document.getElementById('refresh-state').textContent = autoRefresh ? 'on' : 'paused';
if (autoRefresh) loadStatus();
});
document.getElementById('search').addEventListener('input', () => {
// Re-render list using current data in memory by fetching again (cheap)
loadStatus();
});
loadStatus();
startAutoRefresh();
});