Commit Graph
14 Commits
Author SHA1 Message Date
SansanandClaude Opus 5.5 f79f51ea4c Expose Prometheus metrics on a separate port
CI / integration (push) Failing after 14s
CI / docker (push) Failing after 20s
CI / test (push) Successful in 1m10s
CI / publish (push) Skipped
Metrics are served on METRICS_PORT (default 2112, "off" to disable), never on
the public port. Besides the Go runtime and process metrics this adds:

- http_requests_total and http_request_duration_seconds per route pattern,
  from an Echo middleware.
- musicserver_songs_requested_total and musicserver_songs_played_total by
  pick mode (random, low, classic). The mode is now stored with each queued
  song (migration 000006, not released yet).
- Library size, sync duration, last successful sync, broken songs from the
  last full sync, and the database connection pool.

Refs #35, #36, #37

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 21:57:58 +02:00
Sansan a705d7b476 Store each client's play queue in the database
The queue lived in memory and was shared by all clients, so a restart or a
finished sync cleared it. Every client now has its own queue in the new
queue and queue_song tables (added to the undeployed migration 000006).

The music endpoints return the queue ID in the X-Queue-Id header and expect
it back. A request without an ID, or with an ended one, starts a new queue.
/music/reset ends the queue and returns a new ID, and a sync ends all
running queues. Ended queues are kept as history.

The deprecated legacy routes without the /api/v1 prefix are removed.
2026-10-07 20:03:03 +02:00
Sansan 973ebf224b Generate the OpenAPI spec with Huma instead of swag
CI / integration (push) Successful in 49s
CI / docker (push) Successful in 1m0s
CI / test (push) Successful in 1m6s
CI / publish (push) Skipped
The spec is now built from the same calls that register the routes, so it
can't drift from the router like the swag comments did (all /api/v1
routes were missing).

- /api/v1 endpoints are typed Huma operations; the spec is OpenAPI 3.1 at
  /openapi.json (3.0 at /openapi-3.0.json, YAML too), docs at /docs.
  /openapi and /swagger/* redirect there
- Bearer token security is declared per operation; token creation and
  the new GET /api/v1/health are public
- Legacy routes use the same handlers, are hidden from the spec and keep
  the Deprecation header
- /sync/reset is hidden (debug only); the /sync/new, /sync/quick and
  /sync/new/full aliases are removed (use /sync and /sync/full)
- Invalid parameters return 422 (validation); every error is
  {"error": "..."}; empty lists are [] instead of null
- /sync/progress is documented as progress or result (oneOf)
- Tests validate the spec and check real responses of every documented
  operation against it (kin-openapi)
- Removed swag annotations, cmd/docs, echo-swagger and the swag tool
2026-10-06 16:31:06 +02:00
Sansan 2c3e97190d Prefer UUIDs in the API (Phase 3 of the UUID migration)
CI / docker (push) Successful in 46s
CI / integration (push) Successful in 23s
CI / test (push) Successful in 30s
CI / publish (push) Skipped
- New /api/v1 endpoints: GET /soundtracks, /soundtracks/{uuid},
  /songs/{uuid} and /songs/{uuid}/file (streams the song, Range support,
  locked during sync)
- UUIDs added to every response with soundtracks or songs: queue info and
  list (SoundtrackUuid, SongUuid) and statistics (soundtrack_uuid,
  song_uuid). Existing fields stay for backward compatibility
- Play counts and broken-song removal use song UUIDs instead of
  soundtrack ID + song name, so songs with the same name are counted
  separately
- Statistics return an empty song list for soundtracks without songs
  instead of a single empty entry
- The legacy .id file cleanup stays until Phase 4 (#31)

Closes #29
2026-10-06 15:54:41 +02:00
Sansan 090e794460 Restructure backend into services and fix security, crash and sync issues
Security
- Serve character images through os.Root so names like ../ can't escape
  the characters folder
- Sync start and /sync/reset are POST only (legacy and /api/v1)
- /api/v1/token/cleanup requires a token, password compared in constant
  time, tokens no longer logged
- Echo's rate limiter replaces the custom one; TRUSTED_PROXIES enables
  X-Forwarded-For for reverse proxies

Crashes and races
- Sync state lives in SyncService with atomics and a mutex instead of
  unsynchronised globals (fixes concurrent map writes during full sync)
- Only one sync can run; /api/v1 sync and music routes are locked during
  sync like the legacy ones
- No log.Fatal outside startup: errors become 4xx/5xx responses
- Search uses an in-order character match instead of unescaped regexes
- Empty queue / no songs return 404 instead of panicking or looping

Behaviour
- Sync result is computed once and stays the same between polls
- Swagger and /openapi serve the embedded spec
- Shutdown stops HTTP before closing the database
- Expired tokens are deleted with a context that outlives the request
- Files are served with Range support and correct content types; audio
  has no write deadline

Performance
- Migration 000006 adds soundtrack.fingerprint (names, sizes, mtimes) so
  unchanged folders are skipped without reading any files
- Songs are reconciled per soundtrack with one query plus needed writes;
  renames keep play counts
- Moved MUSIC_PATH updates soundtrack and song paths
- .id cleanup only runs for changed soundtracks or a full sync
- Classic random uses one SQL query; Gitea release lookups are cached
  with a timeout

Structure
- internal/config reads all environment variables once
- sqlc emits a Querier interface; backend globals become services
  injected into handlers
- One route registration shared by legacy and /api/v1
- Removed dead code (dbHelper.go, unused middleware, duplicate Gitea
  functions, embedded Swagger UI copy) and lib/pq
- templ and swag pinned as go tools
2026-10-06 15:20:14 +02:00
Sansan a8df738108 #9 Changed the term game to soundtrack everywhere
Build / build (push) Successful in 45s
2026-06-29 18:39:09 +02:00
SansanandMistral Vibe b0418b4f38 feat: Add id column to song table and prep for UUID migration
- Add id serial4 PK to song table (was composite PK)
- Update queries to use soundtrack_id + path
- Add UUID columns to soundtrack and song (nullable)
- Add migration tracking table

TODO: Run sqlc generate, then create backfill migration (000008)

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-06-08 20:36:12 +02:00
Sansan cec408187d feat: Rename game to soundtrack throughout codebase
- Database migration: rename game table to soundtrack
- Rename game_name to soundtrack_name, game_id to soundtrack_id
- Update all SQL queries in soundtrack.sql, song.sql, song_list.sql, statistics.sql
- Regenerate sqlc code (soundtrack.sql.go, song.sql.go, etc.)
- Update backend: music.go, sync.go, statistics.go
- Update server: musicHandler.go, syncHandler.go, routes.go
- Update frontend: hello.go
- Keep URL paths as /games for backward compatibility

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-06-08 20:36:12 +02:00
Sansan 06cbad708d feat: Remove global db.Dbpool with dependency injection (Phase 0)
- Add Database struct in internal/db/database.go with Pool, Ctx, and RunMigrations()
- Update server.go to use Database struct with NewServerInstance()
- Add backend.go with InitBackend(), BackendRepo(), BackendCtx(), BackendPool()
- Update music.go and sync.go to use BackendRepo() and BackendCtx() instead of db.Dbpool/db.Ctx
- Update token_handler.go to accept pool parameter
- Update routes.go to use s.db.Pool for middleware
- Update cmd/main.go to use NewServerInstance() and HTTPServer()
- Update test_helpers.go to initialize backend with test database
- Update test files to use backend.BackendPool() and backend.BackendCtx()

Benefits:
- Easier to mock database for unit tests
- Follows Go best practices (dependency injection)
- Better architecture with explicit dependencies
- RunMigrations() replaces old Migrate_db() function

Note: Global db.Dbpool and db.Ctx still exist in dbHelper.go for backward compatibility
with test_helpers.go, but production code no longer uses them.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-06-08 20:33:29 +02:00
Sansan 89c31c2856 Replace all log.Println and fmt.Printf with Zap structured logging 2026-05-21 23:24:55 +02:00
Sansan 61cab73ffc Small fix to update song played
Build / build (push) Successful in 50s
2025-10-26 20:40:48 +01:00
Sansan 806e88adeb #1 - Created request to check newest version of the app
Build / build (push) Successful in 2m35s
#2 - Added request to download the newest version of the app
#3 - Added request to check progress during sync
#4 - Now blocking all request while sync is in progress
#5 - Implemented ants for thread pooling
#6 - Changed the sync request to now only start the sync
2025-08-23 11:36:03 +02:00
Sansan d653463f58 Moved around more code. Implemented more sqlc. Added support to generate swagger.
Added support for profiling. Removed the pkg module altogether.
Everything except old sync is now using code generated by sqlc.
2025-01-15 16:04:14 +01:00
Sansan 5b640375c3 Reorganized the code, moved more things to the new part 2025-01-13 16:08:54 +01:00