Add NAS status proxy
All checks were successful
Build & Push Docker Image to Gitea Registry / build-and-push (push) Successful in 33s
All checks were successful
Build & Push Docker Image to Gitea Registry / build-and-push (push) Successful in 33s
This commit is contained in:
104
admin.js
104
admin.js
@@ -18,6 +18,10 @@ const checkAllButton = document.querySelector('#checkAllButton');
|
||||
const saveLocalButton = document.querySelector('#saveLocalButton');
|
||||
const resetLocalButton = document.querySelector('#resetLocalButton');
|
||||
const downloadConfigButton = document.querySelector('#downloadConfigButton');
|
||||
const refreshOpsButton = document.querySelector('#refreshOpsButton');
|
||||
const diskStatus = document.querySelector('#diskStatus');
|
||||
const watchtowerStatus = document.querySelector('#watchtowerStatus');
|
||||
const containerStatus = document.querySelector('#containerStatus');
|
||||
|
||||
let config = null;
|
||||
let tokenSet = null;
|
||||
@@ -271,6 +275,104 @@ function downloadConfig() {
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
function formatBytes(bytes) {
|
||||
if (!Number.isFinite(bytes)) return '--';
|
||||
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
let value = bytes;
|
||||
let unit = 0;
|
||||
while (value >= 1024 && unit < units.length - 1) {
|
||||
value /= 1024;
|
||||
unit += 1;
|
||||
}
|
||||
return `${value.toFixed(value >= 10 || unit === 0 ? 0 : 1)} ${units[unit]}`;
|
||||
}
|
||||
|
||||
function renderDisks(disks = []) {
|
||||
diskStatus.innerHTML = disks.map((disk) => {
|
||||
if (disk.error) {
|
||||
return `<div class="ops-line"><strong>${disk.label}</strong><span>${disk.error}</span></div>`;
|
||||
}
|
||||
|
||||
const level = disk.usedPercent >= 90 ? 'danger' : disk.usedPercent >= 80 ? 'warn' : 'ok';
|
||||
return `
|
||||
<div class="ops-line">
|
||||
<strong>${disk.label}</strong>
|
||||
<span>${formatBytes(disk.used)} / ${formatBytes(disk.total)} (${disk.usedPercent}%)</span>
|
||||
</div>
|
||||
<div class="usage-bar" data-level="${level}">
|
||||
<span style="width:${Math.min(disk.usedPercent, 100)}%"></span>
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function renderWatchtower(watchtower) {
|
||||
const session = watchtower?.lastSession;
|
||||
if (!session) {
|
||||
watchtowerStatus.textContent = 'Kein Watchtower-Lauf gefunden.';
|
||||
return;
|
||||
}
|
||||
|
||||
const finished = session.finishedAt ? new Date(session.finishedAt).toLocaleString('de-DE') : '--';
|
||||
watchtowerStatus.innerHTML = `
|
||||
<div class="ops-line"><strong>Letzter Lauf</strong><span>${finished}</span></div>
|
||||
<div class="ops-line"><strong>Geprueft</strong><span>${session.scanned ?? '--'}</span></div>
|
||||
<div class="ops-line"><strong>Aktualisiert</strong><span>${session.updated ?? '--'}</span></div>
|
||||
<div class="ops-line"><strong>Fehler</strong><span>${session.failed ?? '--'}</span></div>
|
||||
`;
|
||||
}
|
||||
|
||||
function renderContainers(containers) {
|
||||
if (!Array.isArray(containers)) {
|
||||
containerStatus.textContent = containers?.error || 'Containerdaten nicht verfuegbar.';
|
||||
return;
|
||||
}
|
||||
|
||||
const important = [
|
||||
'mischlabs',
|
||||
'watchtower',
|
||||
'gitea',
|
||||
'gitea-runner',
|
||||
'auth',
|
||||
'nextcloud_app',
|
||||
'vaultwarden',
|
||||
'photography-website',
|
||||
'michess',
|
||||
'calibre-web',
|
||||
'audiobookshelf'
|
||||
];
|
||||
|
||||
const rows = containers
|
||||
.filter((container) => important.includes(container.name))
|
||||
.sort((a, b) => important.indexOf(a.name) - important.indexOf(b.name));
|
||||
|
||||
containerStatus.innerHTML = rows.map((container) => `
|
||||
<div class="ops-line">
|
||||
<strong>${container.name}</strong>
|
||||
<span>${container.status}</span>
|
||||
</div>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
async function refreshOps() {
|
||||
diskStatus.textContent = 'Lade...';
|
||||
watchtowerStatus.textContent = 'Lade...';
|
||||
containerStatus.textContent = 'Lade...';
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/status', { cache: 'no-store' });
|
||||
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
||||
const status = await response.json();
|
||||
renderDisks(status.disks);
|
||||
renderWatchtower(status.watchtower);
|
||||
renderContainers(status.containers);
|
||||
} catch (error) {
|
||||
diskStatus.textContent = `Statusproxy nicht erreichbar: ${error.message}`;
|
||||
watchtowerStatus.textContent = 'Nicht verfuegbar.';
|
||||
containerStatus.textContent = 'Nicht verfuegbar.';
|
||||
}
|
||||
}
|
||||
|
||||
async function boot() {
|
||||
await handleCallback();
|
||||
tokenSet = getStoredTokens();
|
||||
@@ -293,6 +395,7 @@ async function boot() {
|
||||
logoutButton.hidden = false;
|
||||
adminUser.textContent = `Angemeldet als ${claims.preferred_username || claims.email}`;
|
||||
renderEditor();
|
||||
refreshOps();
|
||||
}
|
||||
|
||||
loginButton.addEventListener('click', login);
|
||||
@@ -322,6 +425,7 @@ resetLocalButton.addEventListener('click', () => {
|
||||
});
|
||||
|
||||
downloadConfigButton.addEventListener('click', downloadConfig);
|
||||
refreshOpsButton.addEventListener('click', refreshOps);
|
||||
|
||||
boot().catch((error) => {
|
||||
loginMessage.textContent = `Adminseite konnte nicht starten: ${error.message}`;
|
||||
|
||||
Reference in New Issue
Block a user