Files
funman300 93182fa251 feat(server): replay upload + fetch endpoints
API surface for the web replay viewer to come:

- `POST /api/replays`        — auth required; persists the JSON body
  verbatim, mints a server-side UUID, returns `{id}`. Three columns
  (final_score, time_seconds, recorded_at) are projected out of the
  payload at insert time so list endpoints don't have to scan blobs.
- `GET  /api/replays/recent` — public; returns the N most-recent
  replays across users (limit defaults to 20, capped at 50). Joins
  the username so the feed reads as "AliceWon · 2:14 win".
- `GET  /api/replays/:id`    — public; returns the full replay JSON
  the desktop client uploaded.

Migration `002_replays.sql` adds the `replays` table with indexes
on `received_at DESC` (recent feed) and `user_id` (per-user views).

Schema-version compatibility is the playback side's responsibility,
matching the desktop's existing `schema_version` gate — the server
just stores and serves whatever JSON came in.

`AppError::NotFound` added so `GET /api/replays/:id` can return a
proper 404 instead of an internal-server-error.

`.sqlx` cache regenerated for the new `query!` invocations.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 18:50:25 +00:00
..

Database Migrations

Migrations are run automatically at server startup via sqlx::migrate!("./migrations").

Naming convention

NNN_description.sql
  • NNN — zero-padded three-digit sequence number (001, 002, …)
  • description — snake_case description of what the migration does

Examples:

001_initial.sql
002_add_user_display_name.sql
003_weekly_goals_table.sql

sqlx tracks which migrations have run in the _sqlx_migrations table and only applies new ones. Never edit or delete an existing migration file after it has been applied to any database — add a new migration instead.

Adding a migration

  1. Create migrations/NNN_description.sql where NNN is the next available number.
  2. Write idempotent SQL (CREATE TABLE IF NOT EXISTS, ALTER TABLE … ADD COLUMN IF NOT EXISTS, etc.) where possible.
  3. Update the sqlx offline query cache so the server builds without a live DB:
    export DATABASE_URL=sqlite://solitaire.db
    sqlx database create
    sqlx migrate run --source solitaire_server/migrations
    cargo sqlx prepare --workspace
    
  4. Commit both the migration file and the updated .sqlx/ query cache together.

Current schema

See 001_initial.sql for the full initial schema: users, sync_state, daily_challenges, leaderboard.