feat(ui): offline landing page for the installed PWA#258
Merged
Conversation
When lerd-ui is stopped or restarting, the installed dashboard (PWA)
previously showed the browser's generic "this site can't be reached"
error. Now it shows a small offline page with restart instructions
and auto-recovers when the backend comes back.
Adds a service worker at /sw.js with:
- navigation requests: network-first, falls back to /offline.html
- static assets (icons, manifest): cache-first, filled on first
successful load
- /api/* requests are bypassed entirely so the WebSocket and every
mutating call keep their normal error semantics
The SW cache name is versioned with the lerd build version +
commit, so each update invalidates the previous cache. /sw.js is
served with Service-Worker-Allowed: / for full-site scope.
The offline page probes /api/status every five seconds and reloads
the dashboard the moment lerd-ui returns, so the user rarely sees
the offline page for more than one tick. Restart command is shown
per-platform (systemctl on Linux, launchctl on macOS).
The lerd.localhost proxy exposes an explicit allowlist of paths and returns 444 for everything else, so the new service worker endpoints were silently dropped when the dashboard was accessed through lerd.localhost (as opposed to localhost:7073 direct). Add two location= blocks to both the Linux (unix-socket) and Darwin (TCP via host.containers.internal) branches of the vhost template so the service worker and offline page are reachable through the proxy the same way /icons/ and /manifest.webmanifest already are. Also surface registration errors to the browser console so a future silent failure is easier to diagnose than the empty catch block was.
Three small polish passes on the offline landing page: - Show the lerd logo at the top of the card (pre-cached by the SW). - Use `lerd start` as the unified restart command on both platforms instead of the platform-detected systemctl/launchctl one-liner. `lerd quit` stops the whole stack, not just lerd-ui, so `lerd start` is the right inverse and works everywhere. - Add a copy button next to the command with a 1.5s "Copied" confirm, with a document.execCommand fallback for Safari contexts where the async clipboard API is unavailable.
geodro
added a commit
that referenced
this pull request
Apr 24, 2026
Bundles four landed changes since beta.6: - #252 Add memcached, rabbitmq, elasticsearch service presets. - #256 Fix check-upstream-php workflow dispatch (was silently broken). - #257 Stream per-phase progress during preset install in the Web UI. - #258 Offline landing page for the installed PWA with lerd start + copy button; nginx vhost allowlists the new SW routes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
When lerd-ui is stopped or restarting, the installed dashboard (PWA) previously showed the browser's generic "this site can't be reached" error. This change ships a small service worker that swaps that in for a dedicated offline landing page, and the dashboard auto-recovers the moment the backend comes back.
Changes
internal/ui/sw.js— new service worker:/offline.html./api/*requests are bypassed entirely — the WebSocket and every mutating call keep their normal error semantics so a stopped backend surfaces honestly everywhere except on top-level navigation.lerd-shell-<version>-<commit>;installpre-caches the offline page + icons + manifest,activatedeletes any olderlerd-shell-*caches. Each lerd update naturally invalidates the previous cache.internal/ui/offline.html— dark-themed static page:navigator.userAgentData.platform)./api/statusevery 5 s; when the call succeeds it flips the status dot green and reloads automatically, so the user typically sees the offline page for less than one probe interval.internal/ui/server.go— two new routes:/sw.js— served withContent-Type: application/javascript; charset=utf-8,Cache-Control: no-cache, andService-Worker-Allowed: /for full-site scope.{{LERD_VERSION}}is substituted once at startup fromversion.Version+"-"+version.Commitso the SW bytes change on every release, triggering a real install cycle in the browser./offline.html— static HTML.internal/ui/index.html— SW registration under'serviceWorker' in navigator, fires onloadso it doesn't race the main app boot.Activation
Existing browsers installing the PWA after this change ships will register the SW on the next page load. The SW takes control on the subsequent navigation (standard SW lifecycle —
clients.claim()is called at activate but navigation requests already in-flight use the previous controller). No action required on the user side.