screentinker/server
Fabian Mendoza 34f1cb9e7c
Some checks failed
CI / Unit tests (node --test) (push) Has been cancelled
CI / OpenAPI spec lint (push) Has been cancelled
CI / Android unit tests (Kotlin schedule evaluator vectors) (push) Has been cancelled
CI / Boot smoke + version check (push) Has been cancelled
feat(dashboard): version indicator + GHCR update check (#165)
* feat(dashboard): version indicator + GHCR update check with admin panel

- Add server/lib/ghcr-check.js: GHCR tag poller (36h cache, semver filter)
- Extend /api/version with latest_version and update_available
- Add POST /api/admin/check-update (force GHCR poll)
- Add POST /api/admin/trigger-update (Docker compose or manual instructions)
- Sidebar footer: version label + amber badge when update available
- Admin > System: version comparison card with Check/Update buttons
- 14 new tests (10 unit + 4 integration), 68/68 passing

Closes #163

* fix(dashboard): gate trigger-update to platform-admin + add GHCR fetch timeout

Review follow-up on #165 (the two blockers):

- trigger-update runs `docker compose up -d` on the HOST via docker.sock
  (root-equivalent) but was behind requireAdmin, i.e. reachable by any
  workspace-level admin. On a multi-tenant host that's a customer, not the infra
  operator. Gate it with requirePlatformAdmin (DOCKER_UPDATE_ENABLED still gates
  it further). check-update stays requireAdmin — it's a read-only GHCR poll.

- ghcr-check.checkNow had no fetch timeout. Node's global fetch has no default
  timeout, so a hung GHCR connection never settled — leaving `inFlight` set
  forever (the finally never ran), which wedged the background poller AND hung
  any awaited checkNow (/api/admin/check-update). Add a 10s AbortController
  timeout on both requests so the try/catch/finally always fire.

All 405 server tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: ScreenTinker <hello@screentinker.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 22:40:06 -05:00
..
config PiP overlay MVP: push image/web overlays to a device or group (#109) (#127) 2026-06-18 14:54:44 -05:00
db feat(ota): self-update kill switch — global, per-device, and MDM auto-detect (#166) 2026-07-10 22:38:58 -05:00
lib feat(dashboard): version indicator + GHCR update check (#165) 2026-07-10 22:40:06 -05:00
middleware feat(#146): billing:read scoped token — dual-path auth for the Usage Report (Option C) 2026-07-01 21:16:21 -05:00
player fix(player): send device_id/token on reconnect before pairing (#164) 2026-07-10 13:04:18 -05:00
routes feat(dashboard): version indicator + GHCR update check (#165) 2026-07-10 22:40:06 -05:00
scripts feat(scheduling): per-item schedule blocks (#74 dayparting, #75 auto-expire) 2026-06-11 15:46:41 -05:00
services feat: app-ending signal (exit-signal contract v1) — server + APK + .wgt + /player 2026-07-08 15:32:40 -05:00
test feat(dashboard): version indicator + GHCR update check (#165) 2026-07-10 22:40:06 -05:00
ws Merge origin/main into feat/android-hidden-settings-menu 2026-07-09 20:09:58 -04:00
.gitignore feat(email): Microsoft Graph send + alert spam protection + preferences UI 2026-05-12 18:16:40 -05:00
config.js feat(dashboard): version indicator + GHCR update check (#165) 2026-07-10 22:40:06 -05:00
package-lock.json chore(release): v1.9.4 2026-07-10 11:57:43 -05:00
package.json chore(release): v1.9.4 2026-07-10 11:57:43 -05:00
server.js feat(dashboard): version indicator + GHCR update check (#165) 2026-07-10 22:40:06 -05:00
version.js chore(version): single-source VERSION, env-configurable data paths, bump tooling 2026-06-10 12:56:03 -05:00