From 1460acb94120269fd2505af59f437f08beb63da3 Mon Sep 17 00:00:00 2001 From: librelad Date: Sun, 31 May 2026 23:54:21 +0100 Subject: [PATCH] feat(webui): add per-app Updates tab (version/CVEs/recovery/history) New 'Updates' tab in the app detail page, beside Backups. Reuses the headless UpdaterPage + renderAppDetail() scoped to the single app, so the per-app and fleet views share one data/render path. UpdaterPage is added to the apps script bundle so it's available on app pages; the tab is disabled while a task runs. Co-Authored-By: Claude Opus 4.8 --- .../apps/core/html/apps-unified-layout.html | 11 ++++ .../apps/core/js/app-tabbed-manager.js | 64 ++++++++++++++++++- .../components/apps/overview/css/overview.css | 17 +++++ .../frontend/core/boot/js/system-loader.js | 1 + 4 files changed, 91 insertions(+), 2 deletions(-) diff --git a/containers/libreportal/frontend/components/apps/core/html/apps-unified-layout.html b/containers/libreportal/frontend/components/apps/core/html/apps-unified-layout.html index c15f2fc..61cfeaa 100755 --- a/containers/libreportal/frontend/components/apps/core/html/apps-unified-layout.html +++ b/containers/libreportal/frontend/components/apps/core/html/apps-unified-layout.html @@ -119,6 +119,10 @@ + `; + return; + } + section.innerHTML = this.renderAppUpdaterHead(app) + this.appUpdater.renderAppDetail(app); + } + + renderAppUpdaterHead(a) { + const up = this.appUpdater; + const esc = (s) => up.escape(s); + const cur = esc(a.current_version || a.current_image || '—'); + const avail = a.update_available ? esc(a.available_version || a.available_image || 'newer') : null; + const badge = a.update_available + ? `update available` + : (a.scanned ? `up to date` : `unscanned`); + const updBtn = a.update_available + ? `` + : ''; + return `
+
${badge} ${cur}${avail ? ` ${avail}` : ''}
+
${updBtn}
+
`; + } + // Initialize the tabbed manager async initialize() { // Prevent double initialization @@ -849,7 +909,7 @@ class AppTabbedManager { // Disable config, services and backup tabs when task is running disableTabs() { - const tabs = ['config', 'services', 'tools', 'backups'] + const tabs = ['config', 'services', 'tools', 'backups', 'updater'] .map(name => document.querySelector(`.main-tab-button[data-tab="${name}"], .tab-button[data-tab="${name}"]`)) .filter(Boolean); @@ -864,7 +924,7 @@ class AppTabbedManager { // Enable config, services and backup tabs when task completes enableTabs() { - const tabs = ['config', 'services', 'tools', 'backups'] + const tabs = ['config', 'services', 'tools', 'backups', 'updater'] .map(name => document.querySelector(`.main-tab-button[data-tab="${name}"], .tab-button[data-tab="${name}"]`)) .filter(Boolean); diff --git a/containers/libreportal/frontend/components/apps/overview/css/overview.css b/containers/libreportal/frontend/components/apps/overview/css/overview.css index 110ca07..07fbc4f 100644 --- a/containers/libreportal/frontend/components/apps/overview/css/overview.css +++ b/containers/libreportal/frontend/components/apps/overview/css/overview.css @@ -172,3 +172,20 @@ border-bottom: 1px solid var(--border-color, rgba(255, 255, 255, .08)); } .ov-loc-row span:first-child { font-weight: 500; } + +/* ---- per-app Updates tab header ----------------------------------------- */ +.app-updater-section { padding: 4px 0; } +.app-updater-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + flex-wrap: wrap; + padding: 12px 14px; + border: 1px solid var(--border-color, rgba(255, 255, 255, .12)); + border-radius: 10px; + background: var(--input-bg, rgba(255, 255, 255, .03)); + margin-bottom: 4px; +} +.app-updater-head-main { display: flex; align-items: center; gap: 10px; } +.app-updater-head-actions { display: flex; gap: 8px; } diff --git a/containers/libreportal/frontend/core/boot/js/system-loader.js b/containers/libreportal/frontend/core/boot/js/system-loader.js index 1b794c9..2d13dfa 100755 --- a/containers/libreportal/frontend/core/boot/js/system-loader.js +++ b/containers/libreportal/frontend/core/boot/js/system-loader.js @@ -195,6 +195,7 @@ class SystemLoader { '/components/apps/port-manager/js/port-manager.js', '/core/tasks/js/task-manager.js', // Add TaskManager for backup functionality '/core/backup-card/js/backup-app-card.js', + '/components/updater/js/updater-page.js', // headless reuse: per-app Updates tab + fleet Overview '/components/apps/services/js/services-manager.js', '/components/apps/tools/js/tools-manager.js', '/components/apps/routing/js/routing-manager.js',