4 Commits

Author SHA1 Message Date
kami dea08f9c47 feat(mobile): install Muzick to the home screen, and get there quickly
Adds the manifest, icons and service worker that make the app
installable, and offers it as a toast once Chrome says it qualifies.
Declining snoozes the offer for a month; installing ends it.

A waiting service worker never activates on its own. Reloading the page
under a listener to swap in a new build would cut the song they are in
the middle of, so updates land on the next cold start instead. Audio is
kept out of the cache entirely: range requests and multi-megabyte bodies
do not belong in a shell cache. Artwork is cached, and the SPA
navigation fallback denies /api so it cannot swallow the event stream.

Installed on Android the app paints edge to edge, so the transport pads
itself past the gesture bar. MediaSession gains setPositionState, which
is what gives the notification shade a seek bar that moves.

Three things kept the bundle from ever being compressed, each hiding the
next: the nginx image ships with gzip off, gzip_proxied defaults to off
and skips anything carrying a Via header, and gzip_http_version defaults
to 1.1 while the host proxy speaks 1.0. With those fixed and the pages
split per route, the first load goes from 555KB to 60KB of app code plus
a vendor chunk that survives redeploys.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 23:28:19 +04:00
kami 4ead344aec feat(playback): move a session between devices
One device holds the audio; the rest watch the same session over an
event stream and act as remotes. Picking a device hands the audio over
at the position the previous one reported, and that device stops.

Also centre the command palette with margins instead of a translate:
animate-rise sets its own transform and dropped the offset on mobile.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 19:12:26 +04:00
kami 755de34501 fix: inject the admin key so the admin UI stops returning 403
The entire admin surface of the SPA had been dead since auth landed
(5ed8d9e / 3bc9f2d). nginx.conf.template injected only
`Authorization: Bearer ${MUZICK_API_KEY}` for all of /api, docker-compose
passed only MUZICK_API_KEY to the frontend container, and app.ts requires
token === adminKey for /api/admin/*. The two keys differ, and
services/api.ts sets no headers of its own.

All 10 admin call sites were affected: the Jobs page polled 403s every
3s/5s forever and rendered a blank Overview with no error state, and every
Settings library action (Scan, Reindex, Reprocess artists, Re-enrich,
Duplicates merge) silently failed.

Three changes, each necessary:
  - a `location /api/admin/` block injecting the admin key
  - the Dockerfile envsubst list widened to include MUZICK_ADMIN_KEY,
    without which the new variable substitutes to empty and the header
    becomes a bare "Bearer"
  - MUZICK_ADMIN_KEY passed to the frontend service in docker-compose

nginx selects the longest matching prefix regardless of block order;
verified empirically in a throwaway nginx:stable-alpine running the real
envsubst output against a stub that echoes $http_authorization:

    /api/admin/queue-stats       -> Bearer ADMINKEY456
    /api/admin/duplicates/merge  -> Bearer ADMINKEY456
    /api/tracks                  -> Bearer APIKEY123
    /api/health                  -> Bearer APIKEY123

All 10 call sites use /admin/... under the axios /api baseURL and none
request bare /api/admin without a trailing slash.

Also gives the Jobs page an error state: a banner that names a 401/403 as a
missing or wrong admin key, a Retry button, "Loading queue stats..." in
place of a blank Overview, and refetchInterval returning false once the
query has errored so it stops hammering a failing endpoint.

Deletes frontend/nginx.conf — unreferenced by the Dockerfile (confirmed by
grep) and the insecure variant of the template.

Worth noting and not addressed here: the outer LAN-only proxy already
forges credentials for everything reaching /api, so this key split buys no
real security while having cost the whole admin surface. Collapsing to one
key would be simpler.

REVIEW-2026-07-30.md finding 2.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 23:50:08 +04:00
kami 3bc9f2d303 muzick: nginx proxy Bearer auth, restart policies, env subst 2026-07-14 15:51:10 +04:00