Sync from main server - 2026-05-05 00:20:15
This commit is contained in:
@@ -15,22 +15,22 @@
|
||||
<div class="metrics-row">
|
||||
<div class="metric-card cpu">
|
||||
<div class="metric-label">CPU USAGE</div>
|
||||
<div class="metric-value" id="m-cpu">{{ system.cpu_pct }}<span>%</span></div>
|
||||
<div class="gauge-bar"><div class="gauge-fill" id="g-cpu" style="width:{{ system.cpu_pct }}%"></div></div>
|
||||
<div class="metric-value" id="m-cpu">{{ system.cpu_pct or '…' }}<span>%</span></div>
|
||||
<div class="gauge-bar"><div class="gauge-fill" id="g-cpu" style="width:{{ system.cpu_pct or 0 }}%"></div></div>
|
||||
</div>
|
||||
<div class="metric-card mem">
|
||||
<div class="metric-label">MEMORY</div>
|
||||
<div class="metric-value" id="m-mem" style="font-size:16px;">{{ system.memory }}</div>
|
||||
<div class="gauge-bar"><div class="gauge-fill" id="g-mem" style="width:{{ system.mem_pct }}%"></div></div>
|
||||
<div class="metric-value" id="m-mem" style="font-size:16px;">{{ system.memory or '…' }}</div>
|
||||
<div class="gauge-bar"><div class="gauge-fill" id="g-mem" style="width:{{ system.mem_pct or 0 }}%"></div></div>
|
||||
</div>
|
||||
<div class="metric-card disk">
|
||||
<div class="metric-label">DISK /</div>
|
||||
<div class="metric-value" id="m-disk" style="font-size:16px;">{{ system.disk }}</div>
|
||||
<div class="gauge-bar"><div class="gauge-fill" id="g-disk" style="width:{{ system.disk_pct }}%"></div></div>
|
||||
<div class="metric-value" id="m-disk" style="font-size:16px;">{{ system.disk or '…' }}</div>
|
||||
<div class="gauge-bar"><div class="gauge-fill" id="g-disk" style="width:{{ system.disk_pct or 0 }}%"></div></div>
|
||||
</div>
|
||||
<div class="metric-card load">
|
||||
<div class="metric-label">LOAD AVG</div>
|
||||
<div class="metric-value" id="m-load" style="font-size:16px;">{{ system.load }}</div>
|
||||
<div class="metric-value" id="m-load" style="font-size:16px;">{{ system.load or '…' }}</div>
|
||||
<div class="gauge-bar"><div class="gauge-fill" id="g-load" style="width:10%"></div></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -38,7 +38,7 @@
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-title"><i class="fas fa-chart-line"></i> Overview</div>
|
||||
<span class="card-meta">Docker {{ system.docker_v }} · {{ main_server }}</span>
|
||||
<span class="card-meta" id="overview-meta">Docker {{ system.docker_v or '…' }} · {{ main_server }}</span>
|
||||
</div>
|
||||
<div class="stat-row">
|
||||
<div class="stat-card"><div class="stat-number" id="stat-total">{{ containers|length }}</div><div class="stat-label">App Containers</div></div>
|
||||
@@ -99,10 +99,135 @@
|
||||
<td><div class="action-btns">{{ ctr_actions(c.name) }}</div></td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr><td colspan="9"><div class="empty-state"><i class="fas fa-inbox"></i>No containers</div></td></tr>
|
||||
<tr id="empty-row"><td colspan="9"><div class="empty-state"><i class="fas fa-inbox"></i>No containers</div></td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% if not running_on_main %}
|
||||
<script>
|
||||
(function () {
|
||||
// ── Helper: build a container row identical to the Jinja template ──
|
||||
function buildRow(c) {
|
||||
const isUp = c.status && c.status.includes('Up');
|
||||
const badge = isUp
|
||||
? `<span class="badge badge-run">Running</span>`
|
||||
: `<span class="badge badge-stop">Stopped</span>`;
|
||||
|
||||
return `
|
||||
<tr data-ctr="${c.name}">
|
||||
<td class="ct-name">${c.name}</td>
|
||||
<td class="ctr-status-cell" data-ctr="${c.name}">${badge}</td>
|
||||
<td><span class="stat-pct" data-ctr="${c.name}" data-stat="cpu">—</span></td>
|
||||
<td>
|
||||
<div class="stat-bar-wrap">
|
||||
<div class="stat-bar-bg">
|
||||
<div class="stat-bar-fill" data-ctr="${c.name}" data-stat="mem_bar" style="width:0%"></div>
|
||||
</div>
|
||||
<span class="stat-pct" data-ctr="${c.name}" data-stat="mem_pct">—</span>
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="stat-pct" data-ctr="${c.name}" data-stat="net" style="color:var(--cyan)">—</span></td>
|
||||
<td class="col-extra app-extra" style="display:none;">
|
||||
<span class="stat-pct" data-ctr="${c.name}" data-stat="block" style="color:var(--yellow)">—</span>
|
||||
</td>
|
||||
<td class="col-extra app-extra ct-image" style="display:none;">${c.image || ''}</td>
|
||||
<td class="col-extra app-extra ct-ports" style="display:none;">${c.ports || '—'}</td>
|
||||
<td><div class="action-btns">
|
||||
<button class="ctr-action-btn restart" title="Restart" onclick="ctrAction('${c.name}','restart',this)">
|
||||
<i class="fas fa-rotate-right"></i>
|
||||
</button>
|
||||
<button class="ctr-action-btn stop" title="Stop" onclick="ctrAction('${c.name}','stop',this)">
|
||||
<i class="fas fa-stop"></i>
|
||||
</button>
|
||||
<button class="ctr-action-btn start" title="Start" onclick="ctrAction('${c.name}','start',this)">
|
||||
<i class="fas fa-play"></i>
|
||||
</button>
|
||||
</div></td>
|
||||
</tr>`;
|
||||
}
|
||||
|
||||
// ── Populate metrics cards ──
|
||||
function applySystem(s) {
|
||||
if (!s) return;
|
||||
|
||||
const cpu = parseFloat(s.cpu_pct) || 0;
|
||||
document.getElementById('m-cpu').innerHTML = `${cpu.toFixed(1)}<span>%</span>`;
|
||||
document.getElementById('m-mem').textContent = s.memory || '—';
|
||||
document.getElementById('m-disk').textContent = s.disk || '—';
|
||||
document.getElementById('m-load').textContent = s.load || '—';
|
||||
|
||||
document.getElementById('g-cpu').style.width = `${Math.min(cpu, 100)}%`;
|
||||
document.getElementById('g-mem').style.width = `${Math.min(parseFloat(s.mem_pct) || 0, 100)}%`;
|
||||
document.getElementById('g-disk').style.width = `${Math.min(parseFloat(s.disk_pct) || 0, 100)}%`;
|
||||
|
||||
if (s.docker_v) {
|
||||
const meta = document.getElementById('overview-meta');
|
||||
if (meta) meta.textContent = `Docker ${s.docker_v} · {{ main_server }}`;
|
||||
}
|
||||
}
|
||||
|
||||
// ── Populate overview stat numbers ──
|
||||
function applyStats(data) {
|
||||
const set = (id, val) => {
|
||||
const el = document.getElementById(id);
|
||||
if (el && val !== undefined && val !== null) el.textContent = val;
|
||||
};
|
||||
set('stat-total', data.containers ? data.containers.length : undefined);
|
||||
set('stat-running', data.running_count);
|
||||
set('stat-users', data.user_count);
|
||||
set('stat-local-bk', data.local_backups);
|
||||
set('stat-vm-bk', data.vm_backups);
|
||||
}
|
||||
|
||||
// ── Populate the containers table ──
|
||||
function applyContainers(containers) {
|
||||
if (!containers || !containers.length) return;
|
||||
|
||||
const tbody = document.getElementById('app-containers-body');
|
||||
if (!tbody) return;
|
||||
|
||||
tbody.innerHTML = containers.map(buildRow).join('');
|
||||
|
||||
// Re-apply column visibility in case "Show more" was already toggled
|
||||
const extras = tbody.querySelectorAll('.app-extra');
|
||||
const btn = document.getElementById('app-toggle-btn');
|
||||
if (btn && btn.dataset.expanded === 'true') {
|
||||
extras.forEach(el => el.style.display = '');
|
||||
}
|
||||
|
||||
// Kick stats refresh if a global function exists (from base template)
|
||||
if (typeof refreshContainerStats === 'function') {
|
||||
refreshContainerStats();
|
||||
}
|
||||
}
|
||||
|
||||
// ── Main async loader ──
|
||||
async function loadDashboardAsync() {
|
||||
try {
|
||||
const res = await fetch('/api/dashboard');
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||
const data = await res.json();
|
||||
|
||||
applySystem(data.system);
|
||||
applyStats(data);
|
||||
applyContainers(data.containers);
|
||||
|
||||
} catch (err) {
|
||||
console.error('[dashboard] async load failed:', err);
|
||||
}
|
||||
}
|
||||
|
||||
// Run immediately on DOM ready
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', loadDashboardAsync);
|
||||
} else {
|
||||
loadDashboardAsync();
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user