Anyone authenticated can still read the shared platform list, but only an
admin may add, rename or delete from it.
- users.is_admin boolean, defaulting to false. The very first account ever
registered becomes an admin automatically (CreateUser's INSERT uses
NOT EXISTS (SELECT 1 FROM users) for is_admin), so there's always at
least one without manual setup; promoting anyone beyond that is a manual
UPDATE for now (documented in the migration's comment on the column).
- GetSessionAndUser, GetUserByEmail and GetUserByID now also select
is_admin, and it flows through to model.User (and so to
/api/auth/{register,login,me}'s response) and to the auth middleware's
authedUser.
- New requireAdminMiddleware + addAdminAuth, layered on top of the existing
auth middleware. Wired onto create/update/delete-platform; list/get stay
on plain addAuth, since reading the list only requires being logged in.
Verified end to end: the first registered user comes back is_admin:true,
the second false; the non-admin gets 403 on create/update/delete-platform
but 200 on list; the admin can do all of it.
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.