Fix admin ops status refresh
All checks were successful
Build & Push Docker Image to Gitea Registry / build-and-push (push) Successful in 12s

This commit is contained in:
Kroonk
2026-05-21 17:16:58 +02:00
parent 5b252f17fd
commit d62224366b
4 changed files with 38 additions and 23 deletions

View File

@@ -6,7 +6,7 @@
<title>MischLabs Admin</title>
<meta name="description" content="MischLabs dashboard administration.">
<meta name="theme-color" content="#0f172a">
<link rel="stylesheet" href="/style.css?v=15">
<link rel="stylesheet" href="/style.css?v=16">
</head>
<body>
<header class="shell hero admin-hero">
@@ -81,6 +81,6 @@
</section>
</main>
<script src="/admin.js?v=8" defer></script>
<script src="/admin.js?v=9" defer></script>
</body>
</html>

View File

@@ -315,38 +315,35 @@ function renderDisks(disks = []) {
function renderWatchtower(watchtower) {
latestWatchtower = watchtower;
const officialSession = watchtower?.lastSession;
const officialHtml = officialSession
? `
<div class="ops-line"><strong>Offizieller Lauf</strong><span>${officialSession.finishedAt ? new Date(officialSession.finishedAt).toLocaleString('de-DE') : '--'}</span></div>
<div class="ops-line"><strong>Geprueft</strong><span>${officialSession.scanned ?? '--'}</span></div>
<div class="ops-line"><strong>Aktualisiert</strong><span>${officialSession.updated ?? '--'}</span></div>
<div class="ops-line"><strong>Fehler</strong><span>${officialSession.failed ?? '--'}</span></div>
`
: `<div class="ops-line"><strong>Offizieller Lauf</strong><span>${watchtower?.tail?.slice(-2)?.join(' | ') || 'Noch nicht gefunden'}</span></div>`;
if (lastManualWatchtowerRun) {
const run = lastManualWatchtowerRun;
const session = run.session;
const finished = run.finishedAt ? new Date(run.finishedAt).toLocaleString('de-DE') : 'laeuft noch';
const state = run.running ? 'Laeuft' : `Beendet (${run.exitCode ?? '--'})`;
watchtowerStatus.innerHTML = `
<div class="ops-line"><strong>Manueller Lauf</strong><span>${state}</span></div>
${officialHtml}
<div class="ops-divider"></div>
<div class="ops-line"><strong>Manuell</strong><span>${state}</span></div>
<div class="ops-line"><strong>Gestartet</strong><span>${run.startedAt ? new Date(run.startedAt).toLocaleString('de-DE') : '--'}</span></div>
<div class="ops-line"><strong>Fertig</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 ?? (run.exitCode ? 1 : 0)}</span></div>
<div class="ops-line"><strong>Manuell geprueft</strong><span>${session?.scanned ?? '--'}</span></div>
<div class="ops-line"><strong>Manuell aktualisiert</strong><span>${session?.updated ?? '--'}</span></div>
<div class="ops-line"><strong>Manuelle Fehler</strong><span>${session?.failed ?? (run.exitCode ? 1 : 0)}</span></div>
`;
return;
}
const session = watchtower?.lastSession;
if (!session) {
const tail = watchtower?.tail?.slice(-4)?.join(' | ');
watchtowerStatus.textContent = tail
? `Kein Lauf geparst. Letzte Logs: ${tail}`
: '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>
`;
watchtowerStatus.innerHTML = officialHtml;
}
function renderContainers(containers) {
@@ -377,7 +374,7 @@ function renderContainers(containers) {
<div class="ops-line container-line" data-container="${container.name}">
<strong>${container.name}</strong>
<span>${container.status}</span>
<button class="ghost-button mini restart-container" type="button" ${container.restartable ? '' : 'disabled'}>
<button class="ghost-button mini restart-container" type="button" title="${container.restartable ? 'Container neu starten' : (container.restartBlockReason || 'Neustart gesperrt')}" ${container.restartable ? '' : 'disabled'}>
Neustarten
</button>
</div>

View File

@@ -205,6 +205,15 @@ function canRestartContainer(container) {
return container.state === 'running' || container.State === 'running';
}
function restartBlockReason(container) {
const name = normalizeContainerName(container.name || container.Names?.[0]);
if (!name) return 'Kein Containername.';
if (RESTART_BLOCKLIST.has(name)) return 'Geschuetzter Systemdienst.';
if (name.endsWith('_db') || name.includes('-db')) return 'Datenbank-Container.';
if (container.state !== 'running' && container.State !== 'running') return 'Container laeuft nicht.';
return null;
}
function dockerLogs(container, tail = 160) {
return new Promise((resolve, reject) => {
const endpoint = `/containers/${encodeURIComponent(container)}/logs?stdout=1&stderr=1&tail=${tail}`;
@@ -252,6 +261,7 @@ async function getContainers() {
status: container.Status,
state: container.State,
restartable: canRestartContainer(container),
restartBlockReason: restartBlockReason(container),
ports: (container.Ports || []).map((port) => ({
privatePort: port.PrivatePort,
publicPort: port.PublicPort,
@@ -400,6 +410,8 @@ async function readWatchtowerRunContainer(runId) {
session: parsed,
tail: Array.isArray(logs) ? logs.slice(-20) : logs
};
return payload;
}
async function monitorWatchtowerRun(runId) {

View File

@@ -573,6 +573,12 @@ main {
color: #fde68a;
}
.ops-divider {
height: 1px;
margin: 2px 0;
background: var(--border);
}
.usage-bar {
height: 7px;
overflow: hidden;