Commit Graph
4 Commits
Author SHA1 Message Date
Sansan c62b65d7cd Add GET /api/v1/character/details with each image's change time
CI / integration (push) Successful in 21s
CI / test (push) Successful in 23s
CI / publish (push) Skipped
CI / docker (push) Successful in 48s
Returns every character image with when it was last changed, in Unix seconds,
so clients can cache the images and download only the ones that changed.
2026-10-07 21:57:26 +02:00
Sansan 7b144ae419 Rewrite tests to run without a database
- Unit tests use an in-memory fake database (internal/testutil/fakedb),
  temp folders and httptest; `go test ./...` needs no Docker or env vars
- Integration tests (build tag "integration") start PostgreSQL with
  testcontainers: migrations step by step, SQL dumps (skipped if
  missing) and a full sync against a real database
- Removed tests that only checked copies of the logic or had empty
  bodies, the broken root integration_test.go, compose.test.yaml and
  .env.test with hardcoded paths
- testMusic MP3s replaced with tiny placeholders (sync never decodes
  audio), 194 MB -> 168 KB
2026-10-06 15:20:31 +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 92b82da3af test: add simple unit tests for backend and logging packages
Build / build (push) Successful in 31s
2026-05-22 22:49:25 +02:00