Commit Graph

171 Commits

Author SHA1 Message Date
bvandeusen 5e4c45932c Merge pull request 'feat: web UI scaffold with embedded SPA and multi-stage Docker build' (#16) from dev into main 2026-04-22 18:23:01 +00:00
bvandeusen 207798b667 docs: document two-process dev workflow with Vite proxy
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 12:59:22 -04:00
bvandeusen 0ee4dfcf42 ci: install Node + build web before Go steps
Go's //go:embed needs web/build/ to exist at compile time. CI now
runs 'npm ci && npm run build && npm test' ahead of the Go steps
so the embed directive sees real, freshly-built assets.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 12:59:07 -04:00
bvandeusen d9f55df347 build: multi-stage Dockerfile with node->go->slim for embedded SPA
Web assets are built in the node stage, copied into the go stage before
go build so //go:embed picks them up, then the minimal slim runtime
carries only the final binary and ffmpeg.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 12:58:29 -04:00
bvandeusen 75ce65c80e feat(server): serve embedded SPA at root with /api,/rest carve-out
- NotFound wrapper routes unknown /api/* and /rest/* to a JSON 404
- Everything else falls through to the embedded web handler, which
  resolves static assets or returns index.html for SPA deep links
2026-04-22 10:31:30 -04:00
bvandeusen 8fe4a5f578 refactor(web): move embed package to web/ so go:embed reaches build/
go:embed paths are relative to the source file and cannot reach parent
directories, so internal/web/embed.go could not see web/build/ without
duplicating the placeholder. Relocating to web/embed.go lets the
directive resolve to the real SvelteKit build output with no copy step.
2026-04-22 10:29:31 -04:00
bvandeusen d5692a73ca feat(web): embed SvelteKit build output with SPA fallback handler
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 10:26:36 -04:00
bvandeusen 8d88a76dbf test(web): add Vitest smoke test and jsdom config 2026-04-22 09:30:08 -04:00
bvandeusen 86b024dc47 feat(web): scaffold SvelteKit SPA with Tailwind + adapter-static
- SvelteKit 2.x + Svelte 5 + TypeScript + Vite 5
- adapter-static with fallback:'index.html' for SPA deep-link routing
- Tailwind configured with dark-only palette matching spec
- Placeholder landing page at /
- Committed web/build/index.html placeholder (via .gitignore negation) so
  go:embed works before the SvelteKit build has been run
2026-04-22 09:26:36 -04:00
bvandeusen feb3f5bbd8 Merge pull request 'feat: /api/* library reads, search, and media endpoints' (#15) from dev into main 2026-04-22 03:02:28 +00:00
bvandeusen 8600b253fd style: gofmt fixes across api package
CI's golangci-lint run flagged three files; two pre-existed this
branch but the Plan 3 seedTrackWithFile struct-literal alignment
is new. Applying gofmt across all three keeps the lint baseline clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 23:02:20 -04:00
bvandeusen 63d3e234d9 feat(api): register cover + stream routes under RequireUser 2026-04-21 22:48:45 -04:00
bvandeusen 3656461b20 feat(api): GET /api/tracks/{id}/stream with Range support
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 22:44:48 -04:00
bvandeusen 945b773cbd feat(api): GET /api/albums/{id}/cover with sidecar fallback 2026-04-21 22:37:53 -04:00
bvandeusen 5af6d7fac6 docs(api): document intentional audioContentType divergence from subsonic 2026-04-21 22:35:29 -04:00
bvandeusen c799e5c1c9 feat(api): scaffold media helpers for cover + stream endpoints 2026-04-21 22:30:25 -04:00
bvandeusen 8da51a1d13 docs: add Plan 3 web UI media endpoints implementation plan
Four-task TDD plan for /api/albums/{id}/cover and /api/tracks/{id}/stream:
scaffold media.go helpers (Task 1), cover handler (Task 2), stream handler
(Task 3), production Mount wiring + route-registration regression (Task 4).
Uses seedTrackWithFile helper to materialize real bytes on disk so Range /
If-Modified-Since tests hit http.ServeContent's real code path.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 22:02:55 -04:00
bvandeusen 20c948a346 docs: clarify sidecar ordering and test helper extension in Plan 3 spec
Self-review fixups: make "first track" explicit (by disc/track order) and
replace the awkward "so existing tests continue to compile" phrasing with a
clearer description of why newLibraryRouter needs extending.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 21:52:07 -04:00
bvandeusen 7fcee05c1e docs: add Plan 3 web UI media endpoints spec
Covers /api/albums/{id}/cover and /api/tracks/{id}/stream — the byte-serving
endpoints Plan 2's cover_url / stream_url forward references point at.
Duplicates protocol-agnostic helpers locally per subsonic-is-legacy direction
rather than extracting a shared package.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 21:51:29 -04:00
bvandeusen b94b7514f9 fix(api): clamp negative offset instead of erroring + clarify duration_sec=0
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 19:27:21 -04:00
bvandeusen db2ee3b21f feat(api): register library + search routes under RequireUser 2026-04-21 08:34:12 -04:00
bvandeusen 683c11661b feat(api): GET /api/search with three paged facets
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 08:29:28 -04:00
bvandeusen 8fb2f4e184 feat(api): GET /api/artists with alpha/newest sort and paging
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 08:06:03 -04:00
bvandeusen f37fd782af feat(api): GET /api/artists/{id} with nested albums 2026-04-21 08:01:25 -04:00
bvandeusen c0487ecddb feat(api): GET /api/albums/{id} with nested tracks
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 07:43:39 -04:00
bvandeusen 8c18e44d5b feat(api): GET /api/tracks/{id} handler with parent metadata
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 07:12:11 -04:00
bvandeusen 98b4755b49 test(api): add library seed fixtures 2026-04-21 00:16:24 -04:00
bvandeusen 3855166a09 feat(api): add dbq→ref projection helpers 2026-04-21 00:13:06 -04:00
bvandeusen e43b6f2761 feat(api): add conversion helpers for library DTOs 2026-04-21 00:09:30 -04:00
bvandeusen c5af7bb53a feat(api): add DTOs for library reads and search 2026-04-21 00:04:51 -04:00
bvandeusen fb49a39492 feat(db): add paged + count queries for library reads 2026-04-21 00:01:15 -04:00
bvandeusen 62e48642e2 docs(plan): add implementation plan for library reads + search
12 TDD-style tasks covering SQL additions, DTOs, conversion helpers,
per-endpoint handlers (tracks, albums, artist detail, artist list, search),
route wiring, and an end-to-end smoke + PR. Follows the approved design at
docs/superpowers/specs/2026-04-20-web-ui-library-reads-design.md.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-20 23:42:47 -04:00
bvandeusen 14aa9d7fba docs(spec): add design for library reads + search endpoints
Plan 2 of the web UI rollout. Covers GET /api/artists (paged, two sort
modes), artist/album/track detail, and unified search with enveloped
pagination and forward-reference stream/cover URLs.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-20 23:33:51 -04:00
bvandeusen 728c852ba1 Merge pull request 'feat: web UI server auth foundation (/api/* login, logout, me)' (#14) from dev into main 2026-04-21 02:51:45 +00:00
bvandeusen 2324c5d576 fix(auth): silence unused-parameter lint in RequireUser test
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-20 22:43:22 -04:00
bvandeusen 1e5c8b5ab7 fix(api): trim whitespace from bearer token in logout
sessionTokenFromHTTP now matches extractBearerToken's trimming
behavior. Without this, a bearer header with trailing whitespace
(e.g. a buggy client or proxy) authenticates via RequireUser but
hashes the padded value on logout, silently no-oping and leaving
the server-side session alive.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-20 22:32:12 -04:00
bvandeusen 06a1fe16e0 feat(server): mount /api/* (login, logout, me)
Wires the native JSON surface alongside the existing /api/admin
and /rest/* routes. Subsonic compatibility remains untouched.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-20 22:19:59 -04:00
bvandeusen 2adf41604f feat(api): GET /api/me
Returns the authenticated user (id/username/is_admin). Shape
matches UserView used in the login response so SPA stores stay
typed against one interface.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-20 22:11:02 -04:00
bvandeusen 8537821d29 feat(api): POST /api/auth/logout
Deletes the session row keyed by the cookie/bearer token and
clears the cookie on the client. Best-effort DB delete — logout
still succeeds for the client if the row's already gone.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-20 22:07:16 -04:00
bvandeusen 2f2dfa86df feat(api): POST /api/auth/login
Verifies bcrypt password hash, mints a session token, stores its
sha256 in the sessions table, and returns the token in both the
response body (for Flutter) and an httpOnly/SameSite=Strict
cookie (for the web SPA).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-20 21:25:52 -04:00
bvandeusen dc43921bcc feat(api): package skeleton + error envelope
Introduces internal/api with Mount(), the {error:{code,message}}
response shape, and stubbed login/logout/me so later tasks can
land one handler at a time without breaking the build.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-20 20:57:09 -04:00
bvandeusen 8cdaf8f0f1 feat(auth): RequireUser middleware (cookie or bearer)
Resolves /api/* callers from session cookie first, Authorization
bearer second. Touches last_seen on success for future active-
sessions UI. Adds GetUserByID query used by the middleware.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-20 20:53:11 -04:00
bvandeusen ff4d443269 feat(auth): session token + password verification helpers
Shared primitives for /api/* auth: mint a url-safe opaque token,
hash it for storage, verify a bcrypt password hash.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-20 20:24:09 -04:00
bvandeusen c8a4a930ea feat(dbq): generate session queries
Adds Insert/Get/Touch/Delete helpers over the sessions table.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-20 20:16:48 -04:00
bvandeusen 1741b57a0b feat(db): add sessions table for /api/* auth
Stores sha256(token) plus user_agent + last_seen_at so future
active-sessions UI doesn't need another migration.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-20 19:37:26 -04:00
bvandeusen 9b0433ad9b docs: add web UI server auth foundation plan
10-task plan for the first implementation slice of the web UI
scaffold: sessions table, /api/auth/{login,logout}, /api/me, and
the RequireUser middleware (cookie + bearer). Stops short of the
library read endpoints and the SvelteKit project, which become
their own follow-up plans.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-20 19:30:24 -04:00
bvandeusen cb3df51f08 docs: add web UI scaffold design spec
First pass of the web UI design doc from the M1 close-out brainstorm.
Covers the stack (SvelteKit static + Go embed), auth model
(cookie + bearer from one endpoint), /api/* surface, shell layout
(sidebar + bottom player), first-cut feature scope, and how later
web UI work threads into M2–M5 alongside each backend milestone
instead of landing as a post-hoc M6.

Also ignores the local .superpowers/ brainstorming companion cache.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-20 19:18:04 -04:00
bvandeusen cd535b9de6 Merge pull request 'feat(library): extract track duration via ffprobe' (#13) from dev into main 2026-04-20 12:11:09 +00:00
bvandeusen 4a9193bc52 feat(library): extract track duration via ffprobe
Scrubbing/seeking in clients was a no-op because every track shipped
with duration_ms=0. Shell out to ffprobe (already in the image) per
file during scan and record the parsed duration. ffprobe failures are
warned + recorded as 0 so a single bad file doesn't sink the scan.

Tightened the incremental skip to also require duration_ms > 0 so
existing libraries get backfilled on the next rescan instead of
needing a wipe.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-20 08:02:37 -04:00
bvandeusen be92087ba1 Merge pull request 'fix(library): drop out-of-range release years instead of failing the album' (#12) from dev into main 2026-04-20 04:22:18 +00:00