- 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
- 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
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
- Add NOT NULL constraints to soundtrack.uuid, song.uuid, and song.soundtrack_uuid
- Add corresponding DROP NOT NULL in down migration
- Update header comments to reflect combined Phase 1 & 2
This eliminates the need for a separate migration 000007 while maintaining
proper rollback capability. The NOT NULL constraints are added AFTER
all UUID backfills are complete, ensuring data integrity.
Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
- 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>
- 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>
#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
Images should not be included in the database
Removes songs where the path doesn't work
Started working on adding cover images
Started adding vue directly in the application