Skip to content

feat(ui): offline landing page for the installed PWA#258

Merged
geodro merged 4 commits intomainfrom
feat/offline-pwa
Apr 24, 2026
Merged

feat(ui): offline landing page for the installed PWA#258
geodro merged 4 commits intomainfrom
feat/offline-pwa

Conversation

@geodro
Copy link
Copy Markdown
Owner

@geodro geodro commented Apr 24, 2026

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:

  • Navigation requests: network-first; on fetch failure, returns the cached /offline.html.
  • Static assets (icons, manifest): cache-first, filled on first successful load.
  • /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.
  • Cache name is lerd-shell-<version>-<commit>; install pre-caches the offline page + icons + manifest, activate deletes any older lerd-shell-* caches. Each lerd update naturally invalidates the previous cache.

internal/ui/offline.html — dark-themed static page:

  • States "lerd-ui is not reachable" and clarifies that services themselves are unaffected.
  • Shows the restart command per-platform (systemctl on Linux, launchctl on macOS, detected via navigator.userAgentData.platform).
  • Probes /api/status every 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.
  • Retry button for manual reload.

internal/ui/server.go — two new routes:

  • /sw.js — served with Content-Type: application/javascript; charset=utf-8, Cache-Control: no-cache, and Service-Worker-Allowed: / for full-site scope. {{LERD_VERSION}} is substituted once at startup from version.Version+"-"+version.Commit so 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 on load so 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.

geodro added 4 commits April 24, 2026 19:38
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 geodro merged commit 9869d50 into main Apr 24, 2026
3 checks passed
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant