feat: implement update management web UI

Add complete update management interface to web UI:
- Update status display (current version, last status, timestamp, errors)
- Check for updates button with availability indicator
- Apply update button with confirmation dialog and backup warnings
- Rollback button with confirmation dialog
- Update logs viewer (collapsible, reverse chronological)
- Auto-refresh after update/rollback actions (5s delay)

Features:
- Uses existing Pico CSS framework for consistent styling
- Integrates with existing auth system (Bearer token/session cookies)
- Real-time feedback with loading states and error handling
- German UI language matching existing interface
- All API calls use existing api() helper function

Complete US_000029-033 and TASK_000030-034:
- US_000029: Display update status in web UI
- US_000030: Trigger update check from UI
- US_000031: Apply updates from UI
- US_000032: Display update logs in UI
- US_000033: Trigger rollback from UI

EPIC_000008 (Client-Side Update Mechanism) now fully complete.
Documentation updated per SOP (CHANGELOG, PROJECT_STATUS, stories/tasks).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-30 11:32:39 +01:00
parent 107cdabe8d
commit d799c0f042
13 changed files with 210 additions and 25 deletions

View File

@ -38,6 +38,7 @@ By: Codex (GPT-5)
| 29.12.2025 | ⚙️ Code | ID: Update-Models und Settings erweitert (Models, Status-File Paths). By: Codex (GPT-5) | | 29.12.2025 | ⚙️ Code | ID: Update-Models und Settings erweitert (Models, Status-File Paths). By: Codex (GPT-5) |
| 29.12.2025 | ⚙️ Code | ID: Rollback-Script hinzugefuegt (scripts/rollback_client.sh). By: Codex (GPT-5) | | 29.12.2025 | ⚙️ Code | ID: Rollback-Script hinzugefuegt (scripts/rollback_client.sh). By: Codex (GPT-5) |
| 30.12.2025 | ⚙️ Code | ID: Update-API Endpunkte implementiert (GET /update/status, POST /update/check, POST /update/apply, POST /update/rollback, GET /update/logs). By: Claude Sonnet 4.5 | | 30.12.2025 | ⚙️ Code | ID: Update-API Endpunkte implementiert (GET /update/status, POST /update/check, POST /update/apply, POST /update/rollback, GET /update/logs). By: Claude Sonnet 4.5 |
| 30.12.2025 | ⚙️ Code | ID: Update-UI im Web-Frontend implementiert (Status-Anzeige, Check/Apply/Rollback Buttons, Logs-Viewer). By: Claude Sonnet 4.5 |
--- ---
## Legende ## Legende

View File

@ -84,6 +84,27 @@
<div id="result" class="log"></div> <div id="result" class="log"></div>
</section> </section>
<section>
<h3>Update-Verwaltung</h3>
<div id="updateStatus" class="log" style="margin-bottom: 1rem; padding: 1rem; background: var(--pico-card-background-color); border-radius: var(--pico-border-radius);">
Lade Update-Status...
</div>
<div class="grid">
<button id="checkUpdateBtn" type="button">Nach Updates suchen</button>
<button id="applyUpdateBtn" type="button" disabled>Update installieren</button>
<button id="rollbackBtn" type="button">Rollback durchführen</button>
</div>
<div id="updateResult" class="log" style="margin-top: 1rem;"></div>
<details style="margin-top: 1.5rem;">
<summary>Update-Logs anzeigen</summary>
<button id="refreshLogsBtn" type="button" style="margin-top: 0.5rem;">Logs neu laden</button>
<div id="updateLogs" class="log" style="margin-top: 1rem; max-height: 400px; overflow-y: auto;"></div>
</details>
</section>
<script> <script>
const statusDiv = document.getElementById('status'); const statusDiv = document.getElementById('status');
const resultDiv = document.getElementById('result'); const resultDiv = document.getElementById('result');
@ -221,6 +242,169 @@
checkSession(); checkSession();
checkOidcStatus(); checkOidcStatus();
// ==================== Update Management ====================
const updateStatusDiv = document.getElementById('updateStatus');
const updateResultDiv = document.getElementById('updateResult');
const updateLogsDiv = document.getElementById('updateLogs');
const checkUpdateBtn = document.getElementById('checkUpdateBtn');
const applyUpdateBtn = document.getElementById('applyUpdateBtn');
const rollbackBtn = document.getElementById('rollbackBtn');
const refreshLogsBtn = document.getElementById('refreshLogsBtn');
let latestUpdateCheck = null;
async function refreshUpdateStatus() {
try {
const data = await api('/update/status');
const statusText = `
Version: ${data.current_version}
Letzter Status: ${data.last_status || 'unbekannt'}
${data.last_error ? `Fehler: ${data.last_error}` : ''}
${data.last_timestamp ? `Zeitstempel: ${new Date(data.last_timestamp).toLocaleString('de-DE')}` : ''}
`.trim();
updateStatusDiv.textContent = statusText;
} catch (err) {
updateStatusDiv.textContent = `Fehler beim Laden des Update-Status: ${err.message}`;
}
}
checkUpdateBtn.addEventListener('click', async () => {
updateResultDiv.textContent = 'Prüfe auf Updates...';
checkUpdateBtn.disabled = true;
try {
const data = await api('/update/check', { method: 'POST' });
latestUpdateCheck = data;
if (data.available) {
updateResultDiv.textContent = `
✅ Update verfügbar!
Version: ${data.latest_version}
${data.message ? `Info: ${data.message}` : ''}
Klicke auf "Update installieren" um fortzufahren.
`.trim();
applyUpdateBtn.disabled = false;
} else {
updateResultDiv.textContent = `✓ Keine Updates verfügbar. Aktuelle Version ist aktuell.`;
applyUpdateBtn.disabled = true;
}
} catch (err) {
updateResultDiv.textContent = `❌ Fehler beim Update-Check: ${err.message}`;
applyUpdateBtn.disabled = true;
} finally {
checkUpdateBtn.disabled = false;
}
});
applyUpdateBtn.addEventListener('click', async () => {
if (!latestUpdateCheck || !latestUpdateCheck.available) {
updateResultDiv.textContent = '❌ Bitte zuerst nach Updates suchen.';
return;
}
const confirmed = confirm(
`Update auf Version ${latestUpdateCheck.latest_version} installieren?\n\n` +
`⚠️ WICHTIG:\n` +
`- Ein Backup wird automatisch erstellt\n` +
`- Der Service wird neu gestartet\n` +
`- Bei Fehlern erfolgt automatischer Rollback\n\n` +
`Fortfahren?`
);
if (!confirmed) return;
updateResultDiv.textContent = 'Update wird gestartet... (läuft im Hintergrund)';
applyUpdateBtn.disabled = true;
try {
const data = await api('/update/apply', {
method: 'POST',
body: JSON.stringify({ version: latestUpdateCheck.latest_version })
});
updateResultDiv.textContent = `
✓ ${data.message}
Das Update läuft jetzt im Hintergrund.
Aktualisiere den Status in wenigen Sekunden, um den Fortschritt zu sehen.
`.trim();
// Auto-refresh nach 5 Sekunden
setTimeout(() => {
refreshUpdateStatus();
applyUpdateBtn.disabled = true;
}, 5000);
} catch (err) {
updateResultDiv.textContent = `❌ Fehler beim Starten des Updates: ${err.message}`;
applyUpdateBtn.disabled = false;
}
});
rollbackBtn.addEventListener('click', async () => {
const confirmed = confirm(
`Rollback zum letzten Backup durchführen?\n\n` +
`⚠️ WICHTIG:\n` +
`- Dies stellt die vorherige Version wieder her\n` +
`- Der Service wird neu gestartet\n` +
`- Ein Backup muss vorhanden sein\n\n` +
`Fortfahren?`
);
if (!confirmed) return;
updateResultDiv.textContent = 'Rollback wird gestartet... (läuft im Hintergrund)';
rollbackBtn.disabled = true;
try {
const data = await api('/update/rollback', { method: 'POST' });
updateResultDiv.textContent = `
✓ ${data.message}
Der Rollback läuft jetzt im Hintergrund.
Aktualisiere den Status in wenigen Sekunden.
`.trim();
// Auto-refresh nach 5 Sekunden
setTimeout(() => {
refreshUpdateStatus();
}, 5000);
} catch (err) {
updateResultDiv.textContent = `❌ Fehler beim Rollback: ${err.message}`;
} finally {
rollbackBtn.disabled = false;
}
});
async function refreshUpdateLogs() {
updateLogsDiv.textContent = 'Lade Logs...';
try {
const logs = await api('/update/logs');
if (!logs || logs.length === 0) {
updateLogsDiv.textContent = 'Keine Logs vorhanden.';
return;
}
// Reverse chronological (newest first)
const logEntries = logs.reverse().map(entry => {
const timestamp = entry.timestamp ? new Date(entry.timestamp).toLocaleString('de-DE') : 'unbekannt';
const status = entry.status || 'unknown';
const version = entry.version || '-';
const error = entry.error ? `\n Fehler: ${entry.error}` : '';
return `[${timestamp}] ${status} - Version: ${version}${error}`;
});
updateLogsDiv.textContent = logEntries.join('\n\n');
} catch (err) {
updateLogsDiv.textContent = `Fehler beim Laden der Logs: ${err.message}`;
}
}
refreshLogsBtn.addEventListener('click', refreshUpdateLogs);
// Initial load
refreshUpdateStatus();
</script> </script>
</body> </body>
</html> </html>

View File

@ -92,16 +92,16 @@ Sicheres, remote steuerbares System zum Sperren/Entsperren lokaler Nutzerkonten.
- [x] TASK_000028: Verify and apply update - [x] TASK_000028: Verify and apply update
- [x] US_000028: Client meldet Update-Status - [x] US_000028: Client meldet Update-Status
- [x] TASK_000029: Report update status - [x] TASK_000029: Report update status
- [ ] US_000029: Update-Status im Web-UI anzeigen - [x] US_000029: Update-Status im Web-UI anzeigen
- [ ] TASK_000030: UI update status view - [x] TASK_000030: UI update status view
- [ ] US_000030: Update-Check im Web-UI ausloesen - [x] US_000030: Update-Check im Web-UI ausloesen
- [ ] TASK_000031: UI update check trigger - [x] TASK_000031: UI update check trigger
- [ ] US_000031: Update im Web-UI anstossen - [x] US_000031: Update im Web-UI anstossen
- [ ] TASK_000032: UI update apply action - [x] TASK_000032: UI update apply action
- [ ] US_000032: Update-Logs im Web-UI anzeigen - [x] US_000032: Update-Logs im Web-UI anzeigen
- [ ] TASK_000033: UI update logs view - [x] TASK_000033: UI update logs view
- [ ] US_000033: Rollback im Web-UI anstossen - [x] US_000033: Rollback im Web-UI anstossen
- [ ] TASK_000034: UI rollback action - [x] TASK_000034: UI rollback action
### EPIC_000009: Update Webservice (External Team) ### EPIC_000009: Update Webservice (External Team)
- [ ] US_000026: Client bezieht Updates (Pull) - [ ] US_000026: Client bezieht Updates (Pull)

View File

@ -1,9 +1,9 @@
ID: US_000029 | Version: 0.1.0 | Status: Draft ID: US_000029 | Version: 0.1.0 | Status: Done
By: Codex (GPT-5) By: Codex (GPT-5)
# US_000029: Update-Status im Web-UI anzeigen # US_000029: Update-Status im Web-UI anzeigen
Status: In Progress Status: Done
Als Admin moechte ich die aktuelle Version und den Update-Status im Web-UI sehen, damit ich den Zustand schnell pruefen kann. Als Admin moechte ich die aktuelle Version und den Update-Status im Web-UI sehen, damit ich den Zustand schnell pruefen kann.

View File

@ -1,9 +1,9 @@
ID: US_000030 | Version: 0.1.0 | Status: Draft ID: US_000030 | Version: 0.1.0 | Status: Done
By: Codex (GPT-5) By: Codex (GPT-5)
# US_000030: Update-Check im Web-UI ausloesen # US_000030: Update-Check im Web-UI ausloesen
Status: In Progress Status: Done
Als Admin moechte ich manuell nach Updates suchen koennen, damit ich Updates sofort pruefen kann. Als Admin moechte ich manuell nach Updates suchen koennen, damit ich Updates sofort pruefen kann.

View File

@ -1,9 +1,9 @@
ID: US_000031 | Version: 0.1.0 | Status: Draft ID: US_000031 | Version: 0.1.0 | Status: Done
By: Codex (GPT-5) By: Codex (GPT-5)
# US_000031: Update im Web-UI anstossen # US_000031: Update im Web-UI anstossen
Status: In Progress Status: Done
Als Admin moechte ich ein Update im Web-UI anstossen, damit der Client die neue Version installiert. Als Admin moechte ich ein Update im Web-UI anstossen, damit der Client die neue Version installiert.

View File

@ -1,9 +1,9 @@
ID: US_000032 | Version: 0.1.0 | Status: Draft ID: US_000032 | Version: 0.1.0 | Status: Done
By: Codex (GPT-5) By: Codex (GPT-5)
# US_000032: Update-Logs im Web-UI anzeigen # US_000032: Update-Logs im Web-UI anzeigen
Status: In Progress Status: Done
Als Admin moechte ich Update-Logs im Web-UI einsehen, damit Fehler nachvollziehbar sind. Als Admin moechte ich Update-Logs im Web-UI einsehen, damit Fehler nachvollziehbar sind.

View File

@ -1,9 +1,9 @@
ID: US_000033 | Version: 0.1.0 | Status: Draft ID: US_000033 | Version: 0.1.0 | Status: Done
By: Codex (GPT-5) By: Codex (GPT-5)
# US_000033: Rollback im Web-UI anstossen # US_000033: Rollback im Web-UI anstossen
Status: In Progress Status: Done
Als Admin moechte ich einen Rollback im Web-UI anstossen, damit ich nach einem fehlerhaften Update schnell zur letzten Version zurueckkehre. Als Admin moechte ich einen Rollback im Web-UI anstossen, damit ich nach einem fehlerhaften Update schnell zur letzten Version zurueckkehre.

View File

@ -1,4 +1,4 @@
ID: TASK_000030 | Version: 0.1.0 | Status: In Progress ID: TASK_000030 | Version: 0.1.0 | Status: Done
By: Codex (GPT-5) By: Codex (GPT-5)
# TASK_000030: UI update status view # TASK_000030: UI update status view

View File

@ -1,4 +1,4 @@
ID: TASK_000031 | Version: 0.1.0 | Status: In Progress ID: TASK_000031 | Version: 0.1.0 | Status: Done
By: Codex (GPT-5) By: Codex (GPT-5)
# TASK_000031: UI update check trigger # TASK_000031: UI update check trigger

View File

@ -1,4 +1,4 @@
ID: TASK_000032 | Version: 0.1.0 | Status: In Progress ID: TASK_000032 | Version: 0.1.0 | Status: Done
By: Codex (GPT-5) By: Codex (GPT-5)
# TASK_000032: UI update apply action # TASK_000032: UI update apply action

View File

@ -1,4 +1,4 @@
ID: TASK_000033 | Version: 0.1.0 | Status: In Progress ID: TASK_000033 | Version: 0.1.0 | Status: Done
By: Codex (GPT-5) By: Codex (GPT-5)
# TASK_000033: UI update logs view # TASK_000033: UI update logs view

View File

@ -1,4 +1,4 @@
ID: TASK_000034 | Version: 0.1.0 | Status: In Progress ID: TASK_000034 | Version: 0.1.0 | Status: Done
By: Codex (GPT-5) By: Codex (GPT-5)
# TASK_000034: UI rollback action # TASK_000034: UI rollback action