Skip to content

Commit 4bae521

Browse files
committed
fix(updates): surface Docker Dash app updates in System → Updates page
The v7.3.0 update notifier was reachable from the sidebar badge and System Settings → General — but not from System → Updates, which is the page users naturally check for "is there an update for X?". The existing endpoint only reported the running version with no GitHub comparison. - src/routes/system.js: GET /api/system/check-updates now also returns app.{current, latest, updateAvailable, releaseUrl, publishedAt, lastChecked, enabled}. Forces a live refresh first (throttled internally) so the user sees fresh data after clicking the button. - public/js/pages/system.js: app row shows the same badge logic as the sidebar (Update available / Up to date / Disabled). Update available badge is clickable → opens the update modal. Release: v7.3.3
1 parent aa2eaf8 commit 4bae521

7 files changed

Lines changed: 65 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@
22

33
All notable changes to Docker Dash are documented here.
44

5+
## [7.3.3] - 2026-04-25 — System → Updates surfaces app updates too
6+
7+
The v7.3.0 update notifier was reachable from the sidebar badge and System Settings → General — but **not** from System → Updates, which is the page users naturally reach for "is there an update for X?" That page only checked Docker Engine + OS updates, with the Docker Dash row showing only the running version (no comparison to GitHub latest).
8+
9+
### Fixed
10+
11+
- **`GET /api/system/check-updates`** — the existing endpoint now also surfaces the Docker Dash app update status (current, latest, updateAvailable, releaseUrl, publishedAt, lastChecked, enabled). Calls `updateCheck.refresh({ force: true })` first so the user sees fresh data after clicking the *Check Updates* button. Refresh is internally throttled per-instance, so spamming the button is safe. ([src/routes/system.js:354-407](src/routes/system.js#L354-L407))
12+
13+
- **System → Updates page** — the Docker Dash app row now shows the same badge logic the sidebar uses: **Update available** when `updateAvailable=true`, **Up to date** when caught up, **Disabled** when the operator has turned the feature off in System Settings. The "Update available" badge is a clickable link that opens the update notifier modal (release notes + admin upgrade command). UpdateNotifier re-fetches before opening so the modal reflects the freshly-checked cache. ([public/js/pages/system.js:295-314](public/js/pages/system.js#L295-L314))
14+
515
## [7.3.1] - 2026-04-25 — Smoother session-expiry recovery
616

717
When a session expired, the UX collapsed: every parallel in-flight API call (containers list, stats, alerts, notifications, host overview…) returned 401 and each one independently:

docker-compose.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ services:
44
context: .
55
dockerfile: Dockerfile
66
args:
7-
APP_VERSION: "${APP_VERSION:-7.3.1}"
8-
image: docker-dash:${APP_VERSION:-7.3.1}
7+
APP_VERSION: "${APP_VERSION:-7.3.3}"
8+
image: docker-dash:${APP_VERSION:-7.3.3}
99
container_name: docker-dash
1010
restart: unless-stopped
1111
env_file:
@@ -54,7 +54,7 @@ services:
5454
dd-egress-filter:
5555
build:
5656
context: ./docker/egress-filter
57-
image: docker-dash-egress-filter:${APP_VERSION:-7.3.1}
57+
image: docker-dash-egress-filter:${APP_VERSION:-7.3.3}
5858
container_name: dd-egress-filter
5959
restart: unless-stopped
6060
# Uses the default bridge so target containers on the default bridge can

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "docker-dash",
3-
"version": "7.3.1",
3+
"version": "7.3.3",
44
"description": "Full-featured Docker management dashboard",
55
"main": "src/server.js",
66
"scripts": {

public/js/pages/system.js

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,11 +293,30 @@ const SystemPage = {
293293
</tr>
294294
<tr>
295295
<td><i class="fas fa-code-branch" style="margin-right:6px"></i> ${i18n.t('pages.system.appVersionLabel')}</td>
296-
<td><span class="mono">v${Utils.escapeHtml(app.version || '?')}</span></td>
296+
<td>
297+
<span class="mono">v${Utils.escapeHtml(app.current || app.version || '?')}</span>
298+
${app.latest ? `<span class="text-dim text-sm" style="margin-left:8px">(${i18n.t('pages.system.latest')}: ${Utils.escapeHtml(app.latest)})</span>` : ''}
299+
<span style="margin-left:8px">${
300+
app.updateAvailable
301+
? `<a href="#" id="app-update-modal-link" class="badge badge-warning" style="text-decoration:none"><i class="fas fa-arrow-up"></i> ${i18n.t('pages.system.updateAvailable')}</a>`
302+
: (app.enabled === false
303+
? `<span class="badge" style="background:var(--surface2);color:var(--text-dim)"><i class="fas fa-pause"></i> ${i18n.t('common.disabled')}</span>`
304+
: `<span class="badge badge-running"><i class="fas fa-check"></i> ${i18n.t('pages.system.upToDate')}</span>`)
305+
}</span>
306+
</td>
297307
</tr>
298308
</table>
299309
${osPackageList}
300310
`;
311+
312+
// Open the update-notifier modal when the user clicks the "Update available" badge.
313+
// Re-init() first so the modal reflects the freshly-fetched cache (the
314+
// /check-updates endpoint just forced a refresh).
315+
el.querySelector('#app-update-modal-link')?.addEventListener('click', async (e) => {
316+
e.preventDefault();
317+
try { await window.UpdateNotifier?.init(); } catch { /* fall through to openModal anyway */ }
318+
window.UpdateNotifier?.openModal();
319+
});
301320
} catch (err) {
302321
el.innerHTML = `<div class="text-muted text-sm"><i class="fas fa-exclamation-triangle" style="color:var(--yellow)"></i> ${i18n.t('pages.system.updatesError', { message: err.message })}</div>`;
303322
}

public/js/pages/whatsnew.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,15 @@ const WhatsNewPage = {
99
// Add new releases at the TOP of this array.
1010
// Types: feature, fix, improvement, security, breaking
1111
_releases: [
12+
{
13+
version: '7.3.3',
14+
date: '2026-04-25',
15+
title: 'System → Updates surfaces app updates too',
16+
changes: [
17+
{ type: 'fix', text: 'The v7.3.0 update notifier was reachable from the sidebar badge + System Settings → General, but not from System → Updates — which is the page users naturally reach for "is there an update for X?". Fixed by extending GET /api/system/check-updates to include the same data the sidebar uses (current, latest, updateAvailable, releaseUrl, publishedAt, lastChecked, enabled). The endpoint also forces a live refresh so the user sees fresh data after clicking *Check Updates*.' },
18+
{ type: 'fix', text: 'System → Updates page now shows a clickable "Update available" badge on the Docker Dash app row. Click → opens the same update modal as the sidebar badge (release notes + admin-only upgrade command). When the feature is disabled in Settings, the row shows a "Disabled" badge instead.' },
19+
],
20+
},
1221
{
1322
version: '7.3.1',
1423
date: '2026-04-25',

src/routes/system.js

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,27 @@ router.get('/check-updates', requireAuth, async (req, res) => {
398398
}
399399

400400
// ── Docker Dash app version ──
401-
result.app = { version: require('../version') };
401+
// v7.3.2: surface the same data the sidebar badge uses, so System →
402+
// Updates and System Settings → General agree.
403+
try {
404+
const updateCheck = require('../services/update-check');
405+
// Best-effort live refresh so the user sees fresh data after clicking
406+
// "Check Updates"; throttled internally so spamming the button is safe.
407+
await updateCheck.refresh({ force: true });
408+
const status = updateCheck.getStatus();
409+
result.app = {
410+
version: status.current,
411+
current: status.current,
412+
latest: status.latest,
413+
updateAvailable: status.hasUpdate,
414+
releaseUrl: status.releaseUrl,
415+
publishedAt: status.publishedAt,
416+
lastChecked: status.lastChecked,
417+
enabled: status.enabled,
418+
};
419+
} catch {
420+
result.app = { version: require('../version') };
421+
}
402422

403423
res.json(result);
404424
} catch (err) {

src/version.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
// Single source of truth for the application version.
33
// Updated automatically by: npm version X.Y.Z (via scripts/sync-version.js)
44
// server.js reads this to inject into index.html at startup — no build step needed.
5-
module.exports = '7.3.1';
5+
module.exports = '7.3.3';

0 commit comments

Comments
 (0)