v0.12.0 migrate sqlite3 to psql18, updated auth, deploy docs, account and replay updates
test / test (push) Canceled after 0s
test / test (push) Canceled after 0s
This commit is contained in:
1 parent
5d2be4abb2
commit
d0326b059e
66 files changed
+4016
-2433
No files matched your search
+34
-14
@@ -26,6 +26,10 @@ Creates new user account and returns JWT token.
|
||||
- `email` (string, optional): Valid email address
|
||||
- `password` (string, required): Minimum 8 characters, must contain letter and number
|
||||
|
||||
Accounts created here are identical to CLI-created accounts and do not
|
||||
expire. Returns 409 when the username or email is taken, and 503 when the
|
||||
registration limit (`-max-users`) or password-hashing capacity is exhausted.
|
||||
|
||||
**Response (201):**
|
||||
```json
|
||||
{
|
||||
@@ -101,7 +105,7 @@ Returns server and storage status.
|
||||
```
|
||||
|
||||
Storage states:
|
||||
- `"disabled"` - No storage path configured
|
||||
- `"disabled"` - No database configured (`-dsn` or `CHESS_DSN`)
|
||||
- `"ok"` - Database operational with auth enabled
|
||||
- `"degraded"` - A persistence write failed or the write queue filled; live games continue in memory, but durable history is no longer complete
|
||||
|
||||
@@ -177,7 +181,9 @@ Response includes all game data. Compare `moves` array length to detect changes.
|
||||
`GET /games/{gameId}/history`
|
||||
|
||||
Returns the persisted replay line even after the live game has been unloaded
|
||||
from memory or the server has restarted. History is public to anyone who knows
|
||||
from memory or the server has restarted. Games without a registered player
|
||||
are deleted 24 hours after their last activity; their history then returns
|
||||
404. History is public to anyone who knows
|
||||
the game ID, matching the existing public live-game read model. Persistent
|
||||
storage must be enabled.
|
||||
|
||||
@@ -193,7 +199,7 @@ client can replay the game without running a chess engine.
|
||||
"startTimeUtc": "2026-09-07T12:00:00Z",
|
||||
"endTimeUtc": "2026-09-07T12:15:00Z",
|
||||
"players": {
|
||||
"white": {"id": "user-id", "color": 1, "type": 1, "claimedBy": "user-id"},
|
||||
"white": {"id": "user-id", "color": 1, "type": 1, "claimedBy": "user-id", "name": "alice"},
|
||||
"black": {"id": "player-id", "color": 2, "type": 1}
|
||||
},
|
||||
"moves": [
|
||||
@@ -209,10 +215,12 @@ client can replay the game without running a chess engine.
|
||||
```
|
||||
|
||||
`result` is omitted while a game is ongoing. Persisted terminal values are
|
||||
`white_wins`, `black_wins`, `draw`, and `stalemate`.
|
||||
`white_wins`, `black_wins`, `draw`, and `stalemate`. A claimed player's `name`
|
||||
is their username when the claim was recorded; it survives later renames and
|
||||
account deletion, and is omitted for anonymous and computer players.
|
||||
|
||||
Returns 404 when the game has no durable record and 503 when persistence is
|
||||
disabled.
|
||||
Returns 400 for a non-canonical game ID, 404 when the game has no durable
|
||||
record, and 503 when persistence is disabled or degraded.
|
||||
|
||||
### List My Stored Games
|
||||
`GET /users/me/games?limit=50&offset=0`
|
||||
@@ -224,8 +232,11 @@ persistent storage.
|
||||
- `limit`: 1-100; defaults to 50
|
||||
- `offset`: 0-1,000,000; defaults to 0
|
||||
|
||||
Each item contains game ID, initial FEN, result/timestamps, players, and move
|
||||
count. `nextOffset` is present only when another page exists.
|
||||
Each item contains game ID, initial FEN, final FEN (after the last stored move,
|
||||
or the initial FEN when there is none), result/timestamps, players, and move
|
||||
count. `nextOffset` is present only when another page exists. A game belongs to
|
||||
a user who created it while authenticated or claimed a slot with a first move;
|
||||
the association survives later player reconfiguration.
|
||||
|
||||
**Response (200):**
|
||||
```json
|
||||
@@ -234,12 +245,13 @@ count. `nextOffset` is present only when another page exists.
|
||||
{
|
||||
"gameId": "a1b2c3d4-e5f6-7890-1234-567890abcdef",
|
||||
"initialFen": "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
|
||||
"finalFen": "r1bqkb1r/pppp1Qpp/2n2n2/4p3/2B1P3/8/PPPP1PPP/RNB1K1NR b KQkq - 0 4",
|
||||
"result": "white_wins",
|
||||
"startTimeUtc": "2026-09-07T12:00:00Z",
|
||||
"endTimeUtc": "2026-09-07T12:15:00Z",
|
||||
"moveCount": 41,
|
||||
"moveCount": 7,
|
||||
"players": {
|
||||
"white": {"id": "user-id", "color": 1, "type": 1, "claimedBy": "user-id"},
|
||||
"white": {"id": "user-id", "color": 1, "type": 1, "claimedBy": "user-id", "name": "alice"},
|
||||
"black": {"id": "player-id", "color": 2, "type": 1}
|
||||
}
|
||||
}
|
||||
@@ -314,7 +326,12 @@ Error codes:
|
||||
- Registration: 5 requests/minute/IP
|
||||
- Login: 10 requests/minute/IP
|
||||
|
||||
Exceeding limit returns 429 status.
|
||||
Exceeding limit returns 429 status. Behind a reverse proxy the client IP is the
|
||||
proxy's `X-Real-IP` header, trusted only from addresses listed in
|
||||
`-trusted-proxies`.
|
||||
|
||||
Password hashing is bounded to four concurrent operations. When all are busy
|
||||
for five seconds, registration and login return 503 with `RESOURCE_LIMIT`.
|
||||
|
||||
## JWT Token Format
|
||||
|
||||
@@ -323,6 +340,9 @@ Tokens are HS256-signed JWTs valid for 7 days. Include in Authorization header:
|
||||
Authorization: Bearer <token>
|
||||
```
|
||||
|
||||
Token claims include `sub` (user ID), `username`, `email`, `session_id`, and
|
||||
`exp` (expiration). Authentication requires the session to exist, be unexpired,
|
||||
and belong to the JWT subject.
|
||||
The scheme is case-insensitive and the token must use RFC 6750 syntax; a
|
||||
malformed header returns 401 even on endpoints where authentication is
|
||||
optional. Claims are `iss` (`chess-server`), `aud` (`chess-api`), `sub` (user
|
||||
ID), `iat`, `nbf`, `exp`, and `extra.session_id`. Tokens carry no profile data;
|
||||
use `/auth/me`. Authentication requires the session to exist, be unexpired, and
|
||||
belong to the JWT subject. Logging in again replaces the previous session.
|
||||
Reference in new issue
Block a user