Backend README (GAME_CLOCK_MS), AGENTS.md notes, TODO items for the open points and the mobile release order; game-history R3.3 and R3.4 marked as replaced. Checked end to end in Chrome: moves, a backend restart, a timeout and the replay.
83 lines
3.3 KiB
Markdown
83 lines
3.3 KiB
Markdown
# Mancala Backend
|
||
|
||
This is the backend code for the Mancala project, written in Express.js.
|
||
|
||
## Getting Started
|
||
|
||
### Install Packages
|
||
|
||
```bash
|
||
yarn install
|
||
```
|
||
|
||
### Start in Development Mode
|
||
|
||
```bash
|
||
yarn dev
|
||
```
|
||
|
||
### Start in Release Mode
|
||
|
||
```bash
|
||
yarn build
|
||
yarn start
|
||
```
|
||
|
||
### Development Dependencies
|
||
|
||
This package links the local packages `@mancala/core` and `mancala.js`. Build them first with `yarn build:packages` in the repo root.
|
||
|
||
## Authentication
|
||
|
||
Clients log in with Keycloak (see `apps/keycloak`), either as a guest or with an account, and send the access token
|
||
over the WebSocket right after connecting:
|
||
|
||
```json
|
||
{ "channel": "auth", "message": { "token": "<Keycloak access token>" } }
|
||
```
|
||
|
||
The server answers on `on_auth` with the user (`{ id, name, isGuest }`) or on `on_auth_error` and closes the socket
|
||
with code 4001. Other messages are ignored until the socket is authenticated, and connections that do not
|
||
authenticate within 5 seconds are closed. Send `auth` again with the new token after refreshing it.
|
||
User ids of Keycloak users are the token subject prefixed with `kc:`.
|
||
|
||
| Variable | Default | Purpose |
|
||
|---|---|---|
|
||
| `KEYCLOAK_ISSUER` | `http://localhost:8080/realms/mancala` | Token issuer; must match the `iss` claim exactly |
|
||
| `KEYCLOAK_JWKS_URL` | issuer + `/protocol/openid-connect/certs` | Where to fetch signing keys, if the backend reaches Keycloak at a different address |
|
||
| `KEYCLOAK_CLIENT_IDS` | `mancala-web` | Comma-separated clients whose tokens are accepted |
|
||
| `LEGACY_USERKEY_AUTH` | `true` | Also accept old clients that connect with `?userKey=<uuid>` and the `/register` endpoint. Turn off once no released client uses it, because a userKey is not a secret the server can check |
|
||
|
||
## Saved games and live games
|
||
|
||
Finished games are saved in Postgres; live games are copied to Valkey after every change and restored when the
|
||
backend starts, so a restart or deploy does not end them. The design is in `docs/specs/game-history`.
|
||
|
||
| Variable | Default | Purpose |
|
||
|---|---|---|
|
||
| `DATABASE_URL` | `postgres://mancala:mancala@localhost:5433/mancala` | Saved games; migrations run on startup |
|
||
| `VALKEY_URL` | `redis://localhost:6379` | Live game state and short-lived caches |
|
||
|
||
The backend keeps running when either is down: games are still played, finished games are saved once Postgres is
|
||
back, and lists are served without the missing data.
|
||
|
||
## Game clock
|
||
|
||
Each player has a clock for the whole game; only the clock of the player whose turn it is runs, also while they are
|
||
offline. A move that arrives after the mover's time has run out is refused, and `GameClockScheduler` ends games whose
|
||
running clock reaches zero once a second, so the opponent wins on time even if nobody is connected. Time while the
|
||
backend is down is not charged. The design is in `docs/specs/game-clock`.
|
||
|
||
| Variable | Default | Purpose |
|
||
|---|---|---|
|
||
| `GAME_CLOCK_MS` | `300000` | Time each player has for a game; a short value such as `40000` helps when testing by hand |
|
||
|
||
| Route | Auth | Response |
|
||
|---|---|---|
|
||
| `GET /game/:id` | – | A live game, or `{ status: "finished", record }` |
|
||
| `GET /games/popular?limit=5` | – | `{ kind: "live" \| "finished", games }` |
|
||
| `GET /games/me/active` | Bearer token | `{ game }` or `{ game: null }` |
|
||
| `GET /games/me?limit=3` | Bearer token | `{ games }`, newest first |
|
||
|
||
Tests use Docker (testcontainers) for Postgres and Valkey.
|