The game is getting a crazy mode that drops extra balls, and scores
reached with it should still enter the shared table but be visibly
labelled. The service therefore has to persist and return that label.
API:
- POST /api/highscores accepts an optional "crazy" boolean. Missing
means false, so cached clients from before crazy mode keep working.
- GET responses include "crazy" on every entry. Older clients ignore
unknown fields (serde default behaviour), so the new response shape is
safe for them too.
Database:
- New databases get a `crazy INTEGER NOT NULL DEFAULT 0 CHECK (crazy IN
(0, 1))` column.
- Existing databases are migrated on open: if pragma_table_info shows no
`crazy` column, ALTER TABLE adds it with the same definition. SQLite
fills existing rows with the default, so every historic score stays a
normal score. No data is rewritten and the migration is idempotent.
Ordering and the top-ten pruning are unchanged; the flag is not part of
the ranking.
Test Plan:
- cargo test --manifest-path highscore-server/Cargo.toml
- pre_crazy_mode_database_is_migrated_in_place: builds the old schema
with a row, opens it, submits a crazy score, checks both rows come
back with the right flag, and reopens the migrated file.
- api_accepts_crazy_flag_and_defaults_it_for_old_clients: one request
without the field, one with "crazy": true.
- Deploy: restart the service against the production database and check
GET /api/highscores lists the old entries with "crazy": false.
42 lines
1.5 KiB
Markdown
42 lines
1.5 KiB
Markdown
# TDK high-score service
|
|
|
|
This small Axum service stores the shared top-ten table in SQLite. It exposes:
|
|
|
|
```text
|
|
GET /healthz
|
|
GET /api/highscores
|
|
POST /api/highscores
|
|
```
|
|
|
|
The POST body is JSON with a 21-character maximum name, a `u32` score, and an
|
|
optional `crazy` boolean (default `false`) marking a crazy-mode score. The
|
|
response is the canonical top-ten JSON array, each entry carrying `crazy`.
|
|
|
|
Databases created before crazy mode gain the `crazy` column automatically on
|
|
startup; existing rows become normal scores.
|
|
|
|
A newly created database starts with the original distributed demo table. An
|
|
existing database, including an existing empty database, is left unchanged.
|
|
|
|
Submissions are anonymous and intentionally trust the browser's score. Add rate
|
|
limiting, moderation, or server-side run verification if the table needs to
|
|
resist forged scores.
|
|
|
|
Run it from `tdkpin-rs` with:
|
|
|
|
```sh
|
|
TDKPIN_HIGHSCORE_BIND=127.0.0.1:3000 \
|
|
TDKPIN_HIGHSCORE_DB=/var/lib/tdkpin/highscores.sqlite3 \
|
|
cargo run --manifest-path highscore-server/Cargo.toml
|
|
```
|
|
|
|
Copy the rate and connection zone declarations from
|
|
[nginx.conf.example](./nginx.conf.example) into the existing `http` block, then
|
|
place its two `location` blocks inside the public site's `server` block. The
|
|
example bounds per-client and aggregate API traffic, request bodies, and proxy
|
|
waits. The browser client expects the API at `/api/highscores` on the same
|
|
origin as the game.
|
|
|
|
The crate inherits the parent [`rustfmt.toml`](../rustfmt.toml); run
|
|
`just fmt-highscore-server` when formatting it directly.
|