mirror of
https://github.com/IS1DI/x0gp.git
synced 2026-07-16 20:47:55 +00:00
This commit captures the full server code accumulated across
several development sessions. It includes the following layers,
applied in roughly this order during development:
1. Base infrastructure
- cmd/poc-server HTTP+WebSocket server, /health, /stats
- internal/transport wire types (Envelope + all message types)
- internal/catalog, internal/realtime, internal/stats
- internal/storage/postgres with migrations 001-004
- .env, .env.example, docker-compose, Dockerfile, scripts/
2. Swagger documentation
- go get github.com/swaggo/http-swagger
- swag init produces docs/docs.go
- /swagger/index.html UI, /swagger/doc.json spec
- annotations on health/stats/ws and catalog handlers
3. Races API
- internal/races/{store,service,keyset,types}.go
- migration 005_races.sql: finished_races, race_plans, race_queue
- GET /api/races with keyset pagination, status filter
- GET /api/races/upcoming
- POST /api/races/queue/join, GET, DELETE
- POST/GET /api/races/plans, DELETE /{id}
- lobby.RaceMeta simplification (no host_id/host_name)
4. Races seeder
- internal/races/seed with deterministic generator
- --seed-races / --reset CLI flags in main.go
- 30 finished + 5 live + 5 plans + 4 queue entries
5. Drivers and clans
- internal/drivers, internal/clans (CRUD, validation)
- migration 008_drivers_clans.sql
- /api/clans and /api/drivers endpoints
- 3-letter uppercase nickname/tag validation
- lobby.DriverMeta: Nickname, AvatarURL, ClanID, ClanTag
6. Podium
- internal/transport.RacePodiumEntry
- lobby.SetDriverProfile for in-memory metadata sync
- migration 007_podium.sql (podium JSONB column)
- seeder populates top-3 per finished race
7. Live races persistence
- migration 009_live_persistence.sql: live_races,
live_race_drivers, lobby_drivers
- internal/races/live_store.go: LiveStore with write-side
mirror for lobby.Service mutations
- Service.collectLive and Upcoming read from Postgres
- main.go RestoreFromDB rehydrates lobby on startup
8. Unify live + finished into one races table
- migration 010_unify_races.sql: rename finished_races to
races, expand status CHECK, merge live rows
- PgStore now hosts both write paths; LiveStore is a
thin facade implementing lobby.Persistence
- seeder resetAll drops only finished/cancelled rows and
race_plans / race_queue / lobby_drivers
Each layer is consistent on its own; cross-layer changes are
visible in the file history. A future refactor may split this
commit into the per-stage boundaries listed above.
66 lines
2.8 KiB
SQL
66 lines
2.8 KiB
SQL
-- 010_unify_races.sql — unify finished_races and live_races into a single
|
|
-- `races` table with a 5-state status.
|
|
--
|
|
-- After this migration the `races` table holds both the live
|
|
-- surface (lobby | countdown | racing) and the finished archive
|
|
-- (finished | cancelled). live_race_drivers keeps the same shape
|
|
-- (one row per (race, driver)). The former `finished_races` columns
|
|
-- are kept verbatim; the former `live_races` columns are added
|
|
-- (started_ms is reused, created_ms was there too).
|
|
|
|
-- 1) Rename finished_races -> races.
|
|
ALTER TABLE finished_races RENAME TO races;
|
|
|
|
-- 2) Drop the old finished-only check and replace with a unified one.
|
|
ALTER TABLE races DROP CONSTRAINT IF EXISTS finished_races_status_check;
|
|
ALTER TABLE races ADD CONSTRAINT races_status_check
|
|
CHECK (status IN ('lobby', 'countdown', 'racing', 'finished', 'cancelled'));
|
|
|
|
-- 2a) Add the live-only columns to the (former) finished table.
|
|
ALTER TABLE races ADD COLUMN IF NOT EXISTS updated_ms BIGINT NOT NULL DEFAULT 0;
|
|
|
|
-- 3) Drop the old index that referenced the old table name and
|
|
-- recreate it. The new keyset index sorts on (status, finished_ms
|
|
-- DESC, id DESC) so the list view can read live+finished together
|
|
-- when needed.
|
|
DROP INDEX IF EXISTS finished_races_finished_ms_id_idx;
|
|
CREATE INDEX IF NOT EXISTS races_status_finished_idx
|
|
ON races (status, finished_ms DESC, id DESC);
|
|
DROP INDEX IF EXISTS finished_races_host_idx; -- host_id no longer exists, but the
|
|
-- index name may have survived. Defensive drop.
|
|
CREATE INDEX IF NOT EXISTS races_started_idx
|
|
ON races (status, started_ms DESC, id DESC);
|
|
|
|
-- 4) Move live rows from live_races into races BEFORE renaming
|
|
-- live_race_drivers, so the FK has somewhere to point.
|
|
INSERT INTO races
|
|
(id, name, track_id, max_cars, laps, time_limit_s, status,
|
|
created_ms, started_ms, finished_ms, updated_ms, podium)
|
|
SELECT
|
|
id, name, track_id, max_cars, laps, time_limit_s, status,
|
|
created_ms, started_ms, 0, updated_ms, '[]'::jsonb
|
|
FROM live_races
|
|
ON CONFLICT (id) DO UPDATE SET
|
|
name = EXCLUDED.name,
|
|
track_id = EXCLUDED.track_id,
|
|
max_cars = EXCLUDED.max_cars,
|
|
laps = EXCLUDED.laps,
|
|
time_limit_s = EXCLUDED.time_limit_s,
|
|
status = EXCLUDED.status,
|
|
created_ms = EXCLUDED.created_ms,
|
|
started_ms = EXCLUDED.started_ms,
|
|
updated_ms = EXCLUDED.updated_ms;
|
|
|
|
-- 5) live_race_drivers: rename to race_drivers and re-target the FK
|
|
-- at the unified races table.
|
|
ALTER TABLE live_race_drivers RENAME TO race_drivers;
|
|
ALTER TABLE race_drivers
|
|
DROP CONSTRAINT IF EXISTS live_race_drivers_race_id_fkey;
|
|
ALTER TABLE race_drivers
|
|
ADD CONSTRAINT race_drivers_race_id_fkey
|
|
FOREIGN KEY (race_id) REFERENCES races(id) ON DELETE CASCADE;
|
|
|
|
-- 6) Drop the now-redundant live tables.
|
|
DROP TABLE IF EXISTS live_race_drivers;
|
|
DROP TABLE IF EXISTS live_races;
|