|
2 | 2 |
|
3 | 3 | All notable changes to Docker Dash are documented here. |
4 | 4 |
|
| 5 | +## [7.3.0] - 2026-04-25 — "Update Notifications" |
| 6 | + |
| 7 | +Periodic, opt-out check for new Docker Dash releases on GitHub. Solves the "user cloned the repo a week ago and has no idea v7.3.0 shipped" gap. Designed to be **quiet**: a tiny pulsing ↑ badge next to the sidebar version, click-to-open modal with the full release notes (rendered from the GitHub Release `body`), and a one-click "show upgrade command" for admins. |
| 8 | + |
| 9 | +### Added — Backend |
| 10 | + |
| 11 | +- **`src/services/update-check.js`** (~165 LOC) — polls `https://api.github.com/repos/<owner>/<repo>/releases/latest`, semver-compares against `src/version.js`, caches the result in the `settings` table. Configurable owner/repo via `DD_UPDATE_CHECK_OWNER` / `DD_UPDATE_CHECK_REPO` env vars (defaults: `bogdanpricop/docker-dash`). 5s timeout, no redirects, custom User-Agent. Network failures are caught + logged + leave the existing cache untouched. |
| 12 | + |
| 13 | +- **`src/routes/update-check.js`** mounted at `/api/system/update-check` (before `/api/system` so it bypasses that router's host-extraction middleware): |
| 14 | + - `GET /` — read current status. Returns `{ current, latest, hasUpdate, releaseNotes, releaseUrl, publishedAt, lastChecked, enabled }`. Auth required, no role gate (sidebar badge needs to work for operators + viewers). |
| 15 | + - `POST /refresh` — admin-only force refresh. Service has a 60s anti-abuse throttle. |
| 16 | + - `POST /setting` — admin-only enable/disable toggle. Audited. |
| 17 | + |
| 18 | +- **Background job** in `src/jobs/index.js` — cron `17 */12 * * *` (every 12h) + a one-shot 60s post-boot run so the badge can light up on first login without waiting half a day. Both gated on `cluster.isLeader()` so HA replicas don't N× the GitHub call. Service short-circuits when disabled (cheap settings.get on each tick). |
| 19 | + |
| 20 | +### Added — Frontend |
| 21 | + |
| 22 | +- **`public/js/update-notifier.js`** (~190 LOC) — self-contained module. Fetches status at app start, renders a 14×14 pulsing ↑ badge inside `#sidebar-version` when an update is available. Click → modal with: |
| 23 | + - Header: `current → latest` with publish date + last-checked timestamp. |
| 24 | + - Release notes: minimal markdown→HTML renderer (no external deps) handling headings, **bold**, *italic*, `inline code`, fenced code blocks, lists, links. All inputs HTML-escaped first. |
| 25 | + - Admin-only `<details>`: copy-pasteable `git pull && APP_VERSION=X.Y.Z docker compose up -d --build app` with a "back up /data first" warning. |
| 26 | + - Footer: link to the release page on GitHub + Close. |
| 27 | + |
| 28 | +- **System Settings → General** — new card with the toggle, last-checked status, and a "Check now" button (forwards to `POST /refresh`). |
| 29 | + |
| 30 | +- **CSS** in `public/css/app.css` — `.update-badge` with subtle pulse animation. Hidden when sidebar is collapsed (inherits from existing `.sidebar.collapsed .sidebar-version` rule). |
| 31 | + |
| 32 | +- **i18n** — new `updates:` block in EN + RO with 18 keys covering badge tooltip, modal labels, settings card, status messages. Other 9 languages fall back to EN via `_fallback`. |
| 33 | + |
| 34 | +### Privacy + air-gap |
| 35 | + |
| 36 | +- One outbound HTTPS call every 12h, total — not per user. Configurable poll interval is fixed at 12h (no operator footgun). |
| 37 | +- `User-Agent: docker-dash/<version>` — no IP/hostname/install ID leaked beyond what the TCP connection inherently exposes. |
| 38 | +- **Disable in System Settings → General** for fully air-gapped deployments. When disabled: zero outbound calls, badge never appears, modal still opens manually if cached data exists (so operators can still read the last release notes they fetched before air-gapping). |
| 39 | +- Audit log: `update_check_enabled` / `update_check_disabled` on toggle. |
| 40 | + |
| 41 | +### Tests |
| 42 | + |
| 43 | +- **`src/__tests__/update-check.test.js`** — 24 tests covering semver parse/compare, enable/disable round-trip, getStatus state machine (empty cache, equal version, newer version, disabled bypass, corrupt JSON), refresh HTTP behavior (success path, disabled short-circuit, network error preserves cache, non-200, force bypass, missing body field). |
| 44 | + |
| 45 | +- **Suite: 907 → 931 passing / 60 suites.** Lint clean, npm audit clean. |
| 46 | + |
| 47 | +### Files touched |
| 48 | + |
| 49 | +- `src/services/update-check.js` (new) |
| 50 | +- `src/routes/update-check.js` (new) |
| 51 | +- `src/server.js` — mount route before `/api/system` |
| 52 | +- `src/jobs/index.js` — 12h cron + one-shot post-boot refresh (leader-gated) |
| 53 | +- `public/js/update-notifier.js` (new) |
| 54 | +- `public/index.html` — script tag for `update-notifier.js` |
| 55 | +- `public/js/app.js` — `UpdateNotifier.init()` after auth |
| 56 | +- `public/js/pages/settings.js` — General tab gets the toggle card |
| 57 | +- `public/css/app.css` — badge styling + pulse animation |
| 58 | +- `public/js/i18n/en.js` + `ro.js` — `updates:` block |
| 59 | +- `src/__tests__/update-check.test.js` (new) |
| 60 | + |
5 | 61 | ## [7.2.1] - 2026-04-23 — Bug fixes |
6 | 62 |
|
7 | 63 | ### Fixed |
|
0 commit comments