Release: Quarkus backend, Keycloak login, game history, clock, bot, tutorial and rematch #3
@ -21,7 +21,8 @@ Online Mancala (the Turkish *Mangala* rules) with a web app, an Android/iOS app
|
|||||||
| `docs/specs/<feature>` | Specs: `requirements.md`, `design.md`, `tasks.md` |
|
| `docs/specs/<feature>` | Specs: `requirements.md`, `design.md`, `tasks.md` |
|
||||||
| `TODO.md` | Open work, known bugs and deployment steps; read it before starting |
|
| `TODO.md` | Open work, known bugs and deployment steps; read it before starting |
|
||||||
|
|
||||||
Specs that exist: `keycloak-auth` (login, guests, tokens) and `game-history` (saved games, live games, replay).
|
Specs that exist: `keycloak-auth` (login, guests, tokens), `game-history` (saved games, live games, replay) and
|
||||||
|
`game-clock` (5 minutes per player, ending games on time).
|
||||||
|
|
||||||
## Running locally
|
## Running locally
|
||||||
|
|
||||||
@ -66,6 +67,8 @@ There is no automated end-to-end suite. Changes to the game flow were checked by
|
|||||||
**Packages and builds**
|
**Packages and builds**
|
||||||
- The apps use `packages/core` and `packages/mancala.js` through `link:` dependencies, i.e. their built `dist`.
|
- The apps use `packages/core` and `packages/mancala.js` through `link:` dependencies, i.e. their built `dist`.
|
||||||
After changing a package, run `yarn build` in it (or `yarn dev` to rebuild on change), or the apps use stale code.
|
After changing a package, run `yarn build` in it (or `yarn dev` to rebuild on change), or the apps use stale code.
|
||||||
|
Running dev servers do not notice a rebuilt package: restart the backend, and restart Parcel after deleting
|
||||||
|
`apps/frontend/.parcel-cache`.
|
||||||
- Backend and frontend Docker images are built from the repo root: `docker build -f apps/backend/Dockerfile .`.
|
- Backend and frontend Docker images are built from the repo root: `docker build -f apps/backend/Dockerfile .`.
|
||||||
- The mobile app uses `RTMTWS` from core (its own `src/rtmt` copy is unused), so changing the core WebSocket client
|
- The mobile app uses `RTMTWS` from core (its own `src/rtmt` copy is unused), so changing the core WebSocket client
|
||||||
changes mobile too. Metro needs `watchFolders` and `nodeModulesPaths` (already in `metro.config.js`).
|
changes mobile too. Metro needs `watchFolders` and `nodeModulesPaths` (already in `metro.config.js`).
|
||||||
@ -76,6 +79,8 @@ There is no automated end-to-end suite. Changes to the game flow were checked by
|
|||||||
- Live games: memory is the source of truth, Valkey is a write-through copy restored on startup; the backend
|
- Live games: memory is the source of truth, Valkey is a write-through copy restored on startup; the backend
|
||||||
assumes a single instance. Finished games are written to Postgres once, with retry when it is down.
|
assumes a single instance. Finished games are written to Postgres once, with retry when it is down.
|
||||||
- Postgres or Valkey being down must not stop games; reads fall back, writes are retried.
|
- Postgres or Valkey being down must not stop games; reads fall back, writes are retried.
|
||||||
|
- The game clock is stored as each player's time at the start of the turn plus `turnStartedAt`; compute the time
|
||||||
|
left with `getClockTimes`, never store a ticking value. `GAME_CLOCK_MS=40000` makes games short for manual tests.
|
||||||
- `RTMT.listenOnClientConnectionChange` adds a listener (several are allowed).
|
- `RTMT.listenOnClientConnectionChange` adds a listener (several are allowed).
|
||||||
- Jest 27 lacks Node's web globals that testcontainers needs; `tests/support/NodeEnvironment.js` copies them in.
|
- Jest 27 lacks Node's web globals that testcontainers needs; `tests/support/NodeEnvironment.js` copies them in.
|
||||||
- A Valkey client pointed at a dead port keeps Jest alive for `disconnectTimeout`; tests pass a short one.
|
- A Valkey client pointed at a dead port keeps Jest alive for `disconnectTimeout`; tests pass a short one.
|
||||||
|
|||||||
10
TODO.md
10
TODO.md
@ -44,6 +44,14 @@ The web app has game history (see `docs/specs/game-history`); the mobile app nee
|
|||||||
before running more than one
|
before running more than one
|
||||||
- [ ] If Valkey is unreachable when the backend starts, live games are not restored until the next restart
|
- [ ] If Valkey is unreachable when the backend starts, live games are not restored until the next restart
|
||||||
|
|
||||||
|
## Game clock
|
||||||
|
|
||||||
|
See `docs/specs/game-clock`.
|
||||||
|
|
||||||
|
- [ ] Other time controls or time added per move; a long move animates on the next player's time
|
||||||
|
- [ ] Clocks in the home page's live game previews and in the ongoing-game banner
|
||||||
|
- [ ] Try the mobile clocks on a device; only the type check and the bundle were run
|
||||||
|
|
||||||
## Deployment
|
## Deployment
|
||||||
|
|
||||||
- [ ] Build the backend and frontend images from the repo root in CI:
|
- [ ] Build the backend and frontend images from the repo root in CI:
|
||||||
@ -52,6 +60,8 @@ The web app has game history (see `docs/specs/game-history`); the mobile app nee
|
|||||||
- [ ] Deploy Keycloak (`apps/.env` with `KEYCLOAK_DB_PASSWORD`, `KEYCLOAK_ADMIN_PASSWORD`,
|
- [ ] Deploy Keycloak (`apps/.env` with `KEYCLOAK_DB_PASSWORD`, `KEYCLOAK_ADMIN_PASSWORD`,
|
||||||
`KEYCLOAK_HOSTNAME=https://auth.playmancala.app`) and reload nginx with the new `auth.playmancala.app` block
|
`KEYCLOAK_HOSTNAME=https://auth.playmancala.app`) and reload nginx with the new `auth.playmancala.app` block
|
||||||
- [ ] Deploy the backend before the web app; the new web app needs token auth on the server
|
- [ ] Deploy the backend before the web app; the new web app needs token auth on the server
|
||||||
|
- [ ] Release the mobile update with the game clock before or with the backend: older app versions do not show the
|
||||||
|
clock and do not handle `on_game_end`, so a game they lose on time does not end for them
|
||||||
- [ ] Back up the Postgres volume (Keycloak and saved games), e.g. a daily `pg_dumpall`
|
- [ ] Back up the Postgres volume (Keycloak and saved games), e.g. a daily `pg_dumpall`
|
||||||
- [ ] Set `MANCALA_DB_PASSWORD` and `POSTGRES_PASSWORD` in `apps/.env` on the server; the `postgres` volume replaces
|
- [ ] Set `MANCALA_DB_PASSWORD` and `POSTGRES_PASSWORD` in `apps/.env` on the server; the `postgres` volume replaces
|
||||||
`keycloak-db`, so Keycloak starts with a fresh database there
|
`keycloak-db`, so Keycloak starts with a fresh database there
|
||||||
|
|||||||
@ -61,6 +61,17 @@ backend starts, so a restart or deploy does not end them. The design is in `docs
|
|||||||
The backend keeps running when either is down: games are still played, finished games are saved once Postgres is
|
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.
|
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 |
|
| Route | Auth | Response |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `GET /game/:id` | – | A live game, or `{ status: "finished", record }` |
|
| `GET /game/:id` | – | A live game, or `{ status: "finished", record }` |
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
# Game clock: requirements
|
# Game clock: requirements
|
||||||
|
|
||||||
Status: proposed.
|
Status: implemented on branch `feature/game-clock` (based on `feature/game-history`).
|
||||||
Scope: backend, web app and mobile app.
|
Scope: backend, web app and mobile app.
|
||||||
|
|
||||||
## Goals
|
## Goals
|
||||||
|
|||||||
@ -17,5 +17,5 @@ Work through the tasks in order; each ends with its tests passing and a commit.
|
|||||||
`UserConnectionInfo.offlineSince` and `OpponentOfflineCountdown` from core (the web app uses them until then).
|
`UserConnectionInfo.offlineSince` and `OpponentOfflineCountdown` from core (the web app uses them until then).
|
||||||
- [x] 6. **Web app, replay**: clocks in `ReplayView` from `getClockAtMove`.
|
- [x] 6. **Web app, replay**: clocks in `ReplayView` from `getClockAtMove`.
|
||||||
- [x] 7. **Mobile app**: clocks in `GameScreen`; type check and bundle.
|
- [x] 7. **Mobile app**: clocks in `GameScreen`; type check and bundle.
|
||||||
- [ ] 8. **End to end and docs**: Chrome tests from `design.md`; README, `AGENTS.md` and `TODO.md` (release the mobile
|
- [x] 8. **End to end and docs**: Chrome tests from `design.md`; README, `AGENTS.md` and `TODO.md` (release the mobile
|
||||||
update before or with the backend; the out of scope items); mark `game-history` R3.3 and R3.4 as replaced.
|
update before or with the backend; the out of scope items); mark `game-history` R3.3 and R3.4 as replaced.
|
||||||
|
|||||||
@ -38,7 +38,10 @@ from the home page, and step through the moves of any game, finished or in progr
|
|||||||
- R3.2 **Left**: a player who leaves the game loses; the other player wins.
|
- R3.2 **Left**: a player who leaves the game loses; the other player wins.
|
||||||
- R3.3 **Timeout**: if the player whose turn it is has been offline for 5 minutes while the opponent is online,
|
- R3.3 **Timeout**: if the player whose turn it is has been offline for 5 minutes while the opponent is online,
|
||||||
the opponent wins. While the opponent is offline on their turn, the waiting player sees a countdown.
|
the opponent wins. While the opponent is offline on their turn, the waiting player sees a countdown.
|
||||||
|
*Replaced by the game clock (`docs/specs/game-clock`): "timeout" now means the player to move ran out of time.*
|
||||||
- R3.4 **Inactive**: if both players have been offline for 30 minutes, the game ends without a winner.
|
- R3.4 **Inactive**: if both players have been offline for 30 minutes, the game ends without a winner.
|
||||||
|
*Replaced by the game clock, which keeps running while players are offline; saved games may still have this
|
||||||
|
reason.*
|
||||||
|
|
||||||
### R4. Home page: live and popular games
|
### R4. Home page: live and popular games
|
||||||
- R4.1 The home page shows up to 5 live games, most spectated first, each as a preview of the real board with the
|
- R4.1 The home page shows up to 5 live games, most spectated first, each as a preview of the real board with the
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user