feat: web UI auth — login page, session store, Shell, TanStack Query #17

Merged
bvandeusen merged 262 commits from dev into main 2026-04-23 07:22:53 -04:00
bvandeusen commented 2026-04-23 07:04:35 -04:00 (Migrated from git.fabledsword.com)

Summary

  • Typed fetch layer (lib/api/client.ts): apiFetch + api.{get,post,del} with typed ApiError{code,message,status} and auto-logout on 401 via dynamic import (breaks the apiFetch ↔ auth/store cycle).
  • Rune-based auth store (lib/auth/store.svelte.ts): user, bootstrap, login, logout — bootstrapped once in +layout.ts so refresh never flashes the login screen.
  • TanStack Query installed and wired via <QueryClientProvider> at the root layout; queryClient is cleared on logout so the next user doesn't see stale data.
  • Persistent Shell (lib/components/Shell.svelte): header with user-menu logout + sidebar nav (Library / Search / Playlists). Sidebar hides below md:; mobile nav is a later plan.
  • Login page (/login): typed error UX — "Invalid username or password" vs "Sign-in unavailable" — with returnTo support that blocks open-redirect vectors (//evil.com, http://evil.com, /login, ../admin).
  • Route guard in +layout.svelte via $effect — unauthed users are sent to /login?returnTo=…, authed users on /login go to /.
  • Placeholder pages for /, /search, /playlists so the sidebar works end-to-end before the real feature plans land.
  • Dev infra: Testing Library + jest-dom wired into Vitest; renamed one route-colocated test to login.test.ts to avoid the +page.* SvelteKit route-walker collision.

Test Plan

  • go test -short -race ./... passes (unchanged; backend untouched)
  • golangci-lint run ./... clean
  • cd web && npm run check → 0 errors / 0 warnings
  • cd web && npm test → 29 tests across 5 files pass
  • cd web && npm run build succeeds (adapter-static emits web/build/)
  • docker build . builds; binary is -x in the final image
  • Manual: docker compose up -dPOST /api/auth/login returns 200 + Set-Cookie: minstrel_session=... (verified with seeded admin user)
  • Forgejo CI green on this PR
## Summary - **Typed fetch layer** (`lib/api/client.ts`): `apiFetch` + `api.{get,post,del}` with typed `ApiError{code,message,status}` and auto-logout on 401 via dynamic import (breaks the `apiFetch ↔ auth/store` cycle). - **Rune-based auth store** (`lib/auth/store.svelte.ts`): `user`, `bootstrap`, `login`, `logout` — bootstrapped once in `+layout.ts` so refresh never flashes the login screen. - **TanStack Query** installed and wired via `<QueryClientProvider>` at the root layout; queryClient is cleared on logout so the next user doesn't see stale data. - **Persistent Shell** (`lib/components/Shell.svelte`): header with user-menu logout + sidebar nav (Library / Search / Playlists). Sidebar hides below `md:`; mobile nav is a later plan. - **Login page** (`/login`): typed error UX — "Invalid username or password" vs "Sign-in unavailable" — with `returnTo` support that blocks open-redirect vectors (`//evil.com`, `http://evil.com`, `/login`, `../admin`). - **Route guard** in `+layout.svelte` via `$effect` — unauthed users are sent to `/login?returnTo=…`, authed users on `/login` go to `/`. - **Placeholder pages** for `/`, `/search`, `/playlists` so the sidebar works end-to-end before the real feature plans land. - **Dev infra**: Testing Library + jest-dom wired into Vitest; renamed one route-colocated test to `login.test.ts` to avoid the `+page.*` SvelteKit route-walker collision. ## Test Plan - [x] `go test -short -race ./...` passes (unchanged; backend untouched) - [x] `golangci-lint run ./...` clean - [x] `cd web && npm run check` → 0 errors / 0 warnings - [x] `cd web && npm test` → 29 tests across 5 files pass - [x] `cd web && npm run build` succeeds (adapter-static emits `web/build/`) - [x] `docker build .` builds; binary is `-x` in the final image - [x] Manual: `docker compose up -d` → `POST /api/auth/login` returns 200 + `Set-Cookie: minstrel_session=...` (verified with seeded admin user) - [ ] Forgejo CI green on this PR
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: bvandeusen/minstrel#17