Platforms no longer have a user_id: there's one global list, unique by name, that every authenticated user reads from and writes to. Games are unchanged - still private per user, still referencing platform_id. - Migration: dropped platform.user_id and its FK, UNIQUE(user_id, name) -> UNIQUE(name), index on change_seq alone instead of (user_id, change_seq). Amended the one init migration in place rather than stacking a second one, since nothing is deployed yet. - db/query.sql: platform queries no longer take a user_id parameter. DeletePlatform's NOT EXISTS guard already checked games from any user (it never filtered the subquery by user_id), so deleting a shared platform is correctly blocked by any user's games on it, not just the deleter's. - routes.go: platform handlers still require auth (the list isn't public to anonymous requests) but no longer scope by user. - Tests: updated the mocks for the new signatures, added TestListPlatformsIsSharedAcrossUsers. Verified against a real Postgres: user A creates "PC", user B sees it immediately, both can add games against it, each only sees their own games, deleting the platform is blocked while either user has a game on it, and the name stays globally unique. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Completed
A tracker for a video game collection: what you own, which platform it's on, your score for it, and whether (and when) you finished it.
Status
Each account has its own private collection. The API is usable; there's no GUI yet beyond two read-only clients (see below).
Running it
just docker-up # starts Postgres and the API on :8080
just docker-down # stops them
Or against a Postgres you already have running locally:
export DATABASE_URL="postgres://postgres:password@localhost:5432/gamedb?sslmode=disable"
just run-api
The API creates its database and runs migrations on startup if needed.
Browse http://localhost:8080/docs for the generated API docs.
Using the API
# Register (also logs you in)
curl -X POST localhost:8080/api/auth/register -H 'Content-Type: application/json' \
-d '{"email":"you@example.com","password":"a long password","name":"You"}'
# -> {"token": "...", "user": {...}}
TOKEN=... # the token from above
curl -X POST localhost:8080/api/platforms -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' -d '{"name":"PC"}'
curl localhost:8080/api/games -H "Authorization: Bearer $TOKEN"
A browser-based client can skip the Authorization header: register/login
also set an httpOnly session cookie.
Other clients
just run-tui— a read-only terminal viewer of/games(no login yet)just run-gui— a read-only raylib desktop viewer of/games(no login yet; planned to move to Fyne)
Development
just test # go test ./...
just sqlc # regenerate internal/db after changing db/query.sql or a migration
Schema changes go in db/migrations as a new <timestamp>_<name>.up.sql /
.down.sql pair; sqlc.yaml reads the schema from that directory directly,
so there's no separate schema file to keep in sync.