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

View File

@@ -315,38 +315,35 @@ function renderDisks(disks = []) {
function renderWatchtower(watchtower) { function renderWatchtower(watchtower) {
latestWatchtower = 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) { if (lastManualWatchtowerRun) {
const run = lastManualWatchtowerRun; const run = lastManualWatchtowerRun;
const session = run.session; const session = run.session;
const finished = run.finishedAt ? new Date(run.finishedAt).toLocaleString('de-DE') : 'laeuft noch'; const finished = run.finishedAt ? new Date(run.finishedAt).toLocaleString('de-DE') : 'laeuft noch';
const state = run.running ? 'Laeuft' : `Beendet (${run.exitCode ?? '--'})`; const state = run.running ? 'Laeuft' : `Beendet (${run.exitCode ?? '--'})`;
watchtowerStatus.innerHTML = ` 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>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>Fertig</strong><span>${finished}</span></div>
<div class="ops-line"><strong>Geprueft</strong><span>${session?.scanned ?? '--'}</span></div> <div class="ops-line"><strong>Manuell geprueft</strong><span>${session?.scanned ?? '--'}</span></div>
<div class="ops-line"><strong>Aktualisiert</strong><span>${session?.updated ?? '--'}</span></div> <div class="ops-line"><strong>Manuell 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>Manuelle Fehler</strong><span>${session?.failed ?? (run.exitCode ? 1 : 0)}</span></div>
`; `;
return; return;
} }
const session = watchtower?.lastSession; watchtowerStatus.innerHTML = officialHtml;
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>
`;
} }
function renderContainers(containers) { function renderContainers(containers) {
@@ -377,7 +374,7 @@ function renderContainers(containers) {
<div class="ops-line container-line" data-container="${container.name}"> <div class="ops-line container-line" data-container="${container.name}">
<strong>${container.name}</strong> <strong>${container.name}</strong>
<span>${container.status}</span> <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 Neustarten
</button> </button>
</div> </div>

View File

@@ -205,6 +205,15 @@ function canRestartContainer(container) {
return container.state === 'running' || container.State === 'running'; 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) { function dockerLogs(container, tail = 160) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const endpoint = `/containers/${encodeURIComponent(container)}/logs?stdout=1&stderr=1&tail=${tail}`; const endpoint = `/containers/${encodeURIComponent(container)}/logs?stdout=1&stderr=1&tail=${tail}`;
@@ -252,6 +261,7 @@ async function getContainers() {
status: container.Status, status: container.Status,
state: container.State, state: container.State,
restartable: canRestartContainer(container), restartable: canRestartContainer(container),
restartBlockReason: restartBlockReason(container),
ports: (container.Ports || []).map((port) => ({ ports: (container.Ports || []).map((port) => ({
privatePort: port.PrivatePort, privatePort: port.PrivatePort,
publicPort: port.PublicPort, publicPort: port.PublicPort,
@@ -400,6 +410,8 @@ async function readWatchtowerRunContainer(runId) {
session: parsed, session: parsed,
tail: Array.isArray(logs) ? logs.slice(-20) : logs tail: Array.isArray(logs) ? logs.slice(-20) : logs
}; };
return payload;
} }
async function monitorWatchtowerRun(runId) { async function monitorWatchtowerRun(runId) {

View File

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