mirror of
https://github.com/IS1DI/x0gp.git
synced 2026-07-18 13:27:03 +00:00
Add graphify, tests and supabase migrations & config
This commit is contained in:
@@ -11,3 +11,5 @@ server/poc-server.exe
|
||||
.grepai/
|
||||
*.blend1
|
||||
*.blend2
|
||||
node_modules/
|
||||
|
||||
|
||||
@@ -43,6 +43,17 @@
|
||||
|
||||
- Go 1.26, бинарь `cmd/poc-server`. Сборка: `go build ./...` или `go build -o poc-server.exe ./cmd/poc-server`.
|
||||
- HTTP+WebSocket, роутер `http.NewServeMux`. Запуск требует `DATABASE_URL` (см. `.env`).
|
||||
- **База данных (Supabase)**:
|
||||
- Используется self-hosted Supabase на порту `3402` (хост: `80.234.93.161:3402`).
|
||||
- Расширение `timescaledb` отключено (закомментировано в `001_init.sql`), так как Supabase не поставляет его из коробки.
|
||||
- Нативная папка `supabase/` содержит `config.toml`, объединенный дамп схемы `supabase/migrations/20260717000000_schema.sql` и каталог семян `supabase/seed.sql` (включает рескейл Монако).
|
||||
- Объединенный дамп схемы автоматически заполняет таблицу `schema_migrations`, чтобы предотвратить ошибки повторного применения миграций Go-сервером на старте.
|
||||
- **Абстракция данных (Store)**:
|
||||
- Пакеты `clans` и `drivers` используют интерфейсы `Store` вместо прямого импорта `*PgStore`.
|
||||
- Это позволяет писать юнит-тесты сервисов без подключения к реальной БД (через `memoryStore`).
|
||||
- **Слепок структуры (Graphify)**:
|
||||
- Утилита `scripts/graphify/main.go` собирает описание всех экспортируемых пакетов, структур, интерфейсов и функций в компактный файл [server/CODEBASE.md](file:///d:/x0gp/server/CODEBASE.md).
|
||||
- Рекомендуется запускать `go run ./scripts/graphify/main.go` после значимых изменений API для поддержания карты проекта в актуальном состоянии и экономии токенов при чтении агентом.
|
||||
- **Swagger UI**: `GET /swagger/index.html`, спека `GET /swagger/doc.json` (14 paths, 29 definitions).
|
||||
- Генерация спеки: `swag init -g cmd/poc-server/main.go -o docs --parseInternal` из `D:/x0gp/server`. CLI ставится через `go install github.com/swaggo/swag/cmd/swag@v1.16.4`.
|
||||
- Аннотации swag живут над хендлерами в `cmd/poc-server/main.go`, `catalog_handlers.go`, `races_handlers.go`. При добавлении нового REST-эндпоинта — добавить `@Router`/`@Param`/`@Success` и перегенерить `docs/`.
|
||||
|
||||
@@ -0,0 +1,736 @@
|
||||
# x0gp Go Codebase Map (Generated)
|
||||
|
||||
This file contains a compiled map of all packages, structures, interfaces, and public functions in the `server` directory. It is optimized for token-efficiency so AI agents and humans can understand the codebase layout without reading all files.
|
||||
|
||||
## Package `config` (`internal/config`)
|
||||
|
||||
Package config loads runtime configuration from environment variables.
|
||||
|
||||
On startup Load() reads a .env file (if present) from the current
|
||||
working directory and merges its values into the process environment.
|
||||
Existing process env vars take precedence over .env so deployments
|
||||
can override .env without modifying the file.
|
||||
|
||||
### Structs
|
||||
- **struct Config** { HTTPAddr, TickRate, TickInterval, LogLevel, MaxClients, SnapshotRate, DevMode, DatabaseURL, UDPVideoAddr, ESPCmdPort }
|
||||
|
||||
### Functions
|
||||
- func Load() (*Config, error)
|
||||
|
||||
---
|
||||
|
||||
## Package `postgres` (`internal/storage/postgres`)
|
||||
|
||||
Package postgres owns the persistence layer of the catalog.
|
||||
|
||||
It exposes a *pgxpool.Pool plus a small set of repository helpers used by
|
||||
internal/catalog. Migrations are applied at startup from the SQL files in
|
||||
the migrations/ subdirectory.
|
||||
|
||||
### Structs
|
||||
- **struct PgStore**
|
||||
- **struct Config** { URL, Host, Port, User, Password, Database, SSLMode, MaxConns, MinConns, ConnectTimeout }
|
||||
|
||||
### Functions
|
||||
- func NewPgStore(*pgxpool.Pool) *PgStore
|
||||
- func Open(context.Context, Config) (*pgxpool.Pool, error)
|
||||
- func Migrate(context.Context, *pgxpool.Pool) error
|
||||
|
||||
### Methods
|
||||
- func (*PgStore) Load(context.Context) ([]catalog.TrackMeta, []catalog.CarMeta, error)
|
||||
- func (*PgStore) UpsertTrack(context.Context, catalog.TrackMeta) error
|
||||
- func (*PgStore) DeleteTrack(context.Context, string) error
|
||||
- func (*PgStore) UpsertCar(context.Context, catalog.CarMeta) error
|
||||
- func (*PgStore) DeleteCar(context.Context, string) error
|
||||
- func (*PgStore) UpdateCarStats(context.Context, string, func) error
|
||||
- func (*PgStore) GetTrackCalendar(context.Context) ([]catalog.TrackCalendarEntry, error)
|
||||
- func (*PgStore) CreateTrackCalendar(context.Context, catalog.TrackCalendarEntry) (catalog.TrackCalendarEntry, error)
|
||||
- func (*PgStore) DeleteTrackCalendar(context.Context, int) error
|
||||
- func (Config) DSN() (string, error)
|
||||
|
||||
---
|
||||
|
||||
## Package `seed` (`internal/races/seed`)
|
||||
|
||||
### Structs
|
||||
- **struct DefaultCounts** { Finished, Live, Plans }
|
||||
- **struct Options** { Counts, Seed, Reset, Now }
|
||||
- **struct Summary** { FinishedInserted, LiveCreated, PlansInserted, QueueInserted, Duration }
|
||||
- **struct Runner**
|
||||
|
||||
### Functions
|
||||
- func NewRunner(*races.PgStore, *lobby.Service, *races.LiveStore, *clans.PgStore, *drivers.PgStore) *Runner
|
||||
|
||||
### Methods
|
||||
- func (*Runner) Run(context.Context, Options) (Summary, error)
|
||||
|
||||
---
|
||||
|
||||
## Package `realtime` (`internal/realtime`)
|
||||
|
||||
Package realtime manages WebSocket connections: registration, fan-out of
|
||||
race snapshots, and per-client send queues with backpressure handling.
|
||||
|
||||
### Structs
|
||||
- **struct Client** { ID, Send, SessionID, Done, DriverID, LastUDPCommand }
|
||||
- **struct Hub**
|
||||
- **struct Stats** { Connections, DropsTotal }
|
||||
|
||||
### Functions
|
||||
- func NewHub() *Hub
|
||||
- func MustEnvelope(transport.MessageType, any) *transport.Envelope
|
||||
|
||||
### Methods
|
||||
- func (*Client) Close()
|
||||
- func (*Hub) Stats() Stats
|
||||
- func (*Hub) Register(*Client)
|
||||
- func (*Hub) Unregister(*Client)
|
||||
- func (*Hub) Run(context.Context)
|
||||
- func (*Hub) Stop()
|
||||
- func (*Hub) Publish(*transport.Envelope)
|
||||
|
||||
---
|
||||
|
||||
## Package `catalog` (`internal/catalog`)
|
||||
|
||||
Package catalog owns the static configuration entities: tracks and
|
||||
cars. Both are first-class resources that drivers browse, pick, and
|
||||
(in later phases) author.
|
||||
|
||||
PoC: in-memory cache backed by a pluggable Store. The runtime uses
|
||||
postgres.PgStore so tracks / cars survive restarts. The cache is the
|
||||
source of truth for read paths; the Store is the source of truth for
|
||||
durability. Mutations go through the Store, then refresh the cache.
|
||||
|
||||
Design constraints come from the user's apartment layout:
|
||||
- largest room: 4.46 m × 3.19 m (446 × 319 cm)
|
||||
- car scale: 1/27 (Compact)
|
||||
- printer: Bambu A1 (220×220×250 mm build volume)
|
||||
|
||||
Tracks are sized to fit a 4.5 × 3.2 m bounding box with a safety
|
||||
margin, which keeps them physically realizable in the apartment.
|
||||
Cars are sized to a 1/27 touring envelope (~80 × 35 × 25 mm).
|
||||
|
||||
### Interfaces
|
||||
- **interface Store** { Load, UpsertTrack, DeleteTrack, UpsertCar, DeleteCar, UpdateCarStats, GetTrackCalendar, CreateTrackCalendar, DeleteTrackCalendar }
|
||||
|
||||
### Structs
|
||||
- **struct CreateTrackOptions** { ID, Name, Description, AuthorID, Visibility, Scale, LengthM, WidthM, LaneWidthM, Surface, Tags, Centerline }
|
||||
- **struct UpdateTrackOptions** { Name, Description, Visibility, LengthM, WidthM, LaneWidthM, Surface, Tags, Centerline }
|
||||
- **struct CreateCarOptions** { ID, Name, OwnerID, Visibility, Scale, LengthMm, WidthMm, HeightMm, WeightG, Chassis, Motor, Battery, Drive, TopSpeedMs, ColorHex, AvatarURL, Active, DeviceID }
|
||||
- **struct UpdateCarOptions** { Name, Visibility, LengthMm, WidthMm, HeightMm, WeightG, TopSpeedMs, ColorHex, Active, Chassis, Motor, Battery, Drive, DeviceID }
|
||||
- **struct Waypoint** { X, Y, HeadingRad, SpeedMs, Curvature }
|
||||
- **struct Bounds** { MinX, MinY, MaxX, MaxY, LengthM, WidthM }
|
||||
- **struct TrackMeta** { ID, Name, Description, AuthorID, Visibility, Scale, LengthM, WidthM, LaneWidthM, Bounds, Surface, Tags, Centerline, BestLapMs, BestLapHolder, CreatedMs, UpdatedMs }
|
||||
- **struct TrackCalendarEntry** { ID, TrackID, StartAt, EndAt }
|
||||
- **struct MotorSpec** { Kind, Class, KV, PowerW }
|
||||
- **struct BatterySpec** { VoltageV, CapacityMah, Cells, Chemistry }
|
||||
- **struct ChassisSpec** { Model, Material, Printed, WheelbaseMm, TrackMm }
|
||||
- **struct CarMeta** { ID, Name, OwnerID, Visibility, Scale, LengthMm, WidthMm, HeightMm, WeightG, Chassis, Motor, Battery, Drive, DeviceID, TopSpeedMs, ColorHex, AvatarURL, Active, TotalDistanceM, TotalRaces, TotalLaps, BestLapMs, CreatedMs, UpdatedMs }
|
||||
- **struct Snapshot** { GeneratedMs, Version, Tracks, Cars }
|
||||
- **struct Stats** { TracksTotal, TracksSystem, TracksPublic, TracksPrivate, CarsTotal, CarsSystem, CarsPublic, CarsPrivate, CarsActive }
|
||||
- **struct Event** { Kind, TrackID, CarID, Snapshot }
|
||||
- **struct Service**
|
||||
|
||||
### Functions
|
||||
- func NewService(context.Context, Store) (*Service, error)
|
||||
|
||||
### Methods
|
||||
- func (CreateTrackOptions) Validate() error
|
||||
- func (*Service) CreateTrack(context.Context, CreateTrackOptions) (TrackMeta, error)
|
||||
- func (*Service) UpdateTrack(context.Context, string, UpdateTrackOptions) (TrackMeta, error)
|
||||
- func (*Service) DeleteTrack(context.Context, string) error
|
||||
- func (CreateCarOptions) Validate() error
|
||||
- func (*Service) CreateCar(context.Context, CreateCarOptions) (CarMeta, error)
|
||||
- func (*Service) UpdateCar(context.Context, string, UpdateCarOptions) (CarMeta, error)
|
||||
- func (*Service) DeleteCar(context.Context, string) error
|
||||
- func (*Service) SetCarStats(context.Context, string, func) error
|
||||
- func (*Service) ListTracksByVisibility(Visibility) []TrackMeta
|
||||
- func (*Service) ListCarsByVisibility(Visibility) []CarMeta
|
||||
- func (*Service) ListCarsByOwner(string) []CarMeta
|
||||
- func (*Service) GetTrackCalendar(context.Context) ([]TrackCalendarEntry, error)
|
||||
- func (*Service) CreateTrackCalendar(context.Context, string, time.Time, time.Time) (TrackCalendarEntry, error)
|
||||
- func (*Service) DeleteTrackCalendar(context.Context, int) error
|
||||
- func (*memoryStore) Load(context.Context) ([]TrackMeta, []CarMeta, error)
|
||||
- func (*memoryStore) UpsertTrack(context.Context, TrackMeta) error
|
||||
- func (*memoryStore) DeleteTrack(context.Context, string) error
|
||||
- func (*memoryStore) UpsertCar(context.Context, CarMeta) error
|
||||
- func (*memoryStore) DeleteCar(context.Context, string) error
|
||||
- func (*memoryStore) UpdateCarStats(context.Context, string, func) error
|
||||
- func (*memoryStore) GetTrackCalendar(context.Context) ([]TrackCalendarEntry, error)
|
||||
- func (*memoryStore) CreateTrackCalendar(context.Context, TrackCalendarEntry) (TrackCalendarEntry, error)
|
||||
- func (*memoryStore) DeleteTrackCalendar(context.Context, int) error
|
||||
- func (*Service) Subscribe() (?, func)
|
||||
- func (*Service) Stop()
|
||||
- func (*Service) Done() ?
|
||||
- func (*Service) Snapshot() Snapshot
|
||||
- func (*Service) Stats() Stats
|
||||
- func (*Service) ListTracks() []TrackMeta
|
||||
- func (*Service) ListCars() []CarMeta
|
||||
- func (*Service) GetTrack(string) (TrackMeta, bool)
|
||||
- func (*Service) GetCar(string) (CarMeta, bool)
|
||||
|
||||
---
|
||||
|
||||
## Package `seeddefs` (`internal/catalog/seeddefs`)
|
||||
|
||||
Package seeddefs holds the canonical seed definitions for tracks and
|
||||
cars. It lives in its own package (no postgres dependencies) so both
|
||||
the catalog tests and the scripts/genseed SQL generator can consume
|
||||
the same source-of-truth data.
|
||||
|
||||
### Structs
|
||||
- **struct Waypoint** { X, Y, HeadingRad, SpeedMs, Curvature }
|
||||
- **struct Bounds** { MinX, MinY, MaxX, MaxY, LengthM, WidthM }
|
||||
- **struct Track** { ID, Name, Description, AuthorID, Visibility, Scale, LengthM, WidthM, LaneWidthM, Surface, Tags, Centerline, Bounds, BestLapMs, BestLapHolder }
|
||||
- **struct Chassis** { Model, Material, Printed, WheelbaseMm, TrackMm }
|
||||
- **struct Motor** { Kind, Class, KV, PowerW }
|
||||
- **struct Battery** { VoltageV, CapacityMah, Cells, Chemistry }
|
||||
- **struct Car** { ID, Name, OwnerID, Visibility, Scale, LengthMm, WidthMm, HeightMm, WeightG, Chassis, Motor, Battery, Drive, TopSpeedMs, ColorHex, Active, TotalDistanceM, TotalRaces, TotalLaps, BestLapMs }
|
||||
|
||||
### Functions
|
||||
- func DefaultTracks() []Track
|
||||
- func DefaultCars() []Car
|
||||
- func Monaco() Track
|
||||
- func BarcelonaCatalunya() Track
|
||||
- func AustriaRedBullRing() Track
|
||||
- func GreatBritainSilverstone() Track
|
||||
- func BelgiumSpa() Track
|
||||
- func RedBullRB20() Car
|
||||
- func FerrariSF24() Car
|
||||
- func McLarenMCL38() Car
|
||||
- func MercedesW15() Car
|
||||
- func AstonMartinAMR24() Car
|
||||
- func ComputeBounds([]Waypoint) Bounds
|
||||
|
||||
---
|
||||
|
||||
## Package `clans` (`internal/clans`)
|
||||
|
||||
Package clans owns persistence and business logic for clans.
|
||||
|
||||
A clan has a unique 3-letter tag (A-Z, uppercase, exactly 3 chars),
|
||||
a name, and an optional avatar URL. Drivers reference a clan via a
|
||||
nullable FK (see internal/drivers).
|
||||
|
||||
### Interfaces
|
||||
- **interface Store** { Exec, Create, Get, GetByTag, List, Delete, Update, Count }
|
||||
|
||||
### Structs
|
||||
- **struct Service**
|
||||
- **struct CreateInput** { ID, Tag, Name, AvatarURL }
|
||||
- **struct UpdateInput** { Name, AvatarURL }
|
||||
- **struct Clan** { ID, Tag, Name, AvatarURL, CreatedMs, UpdatedMs }
|
||||
- **struct PgStore**
|
||||
|
||||
### Functions
|
||||
- func NewService(Store) *Service
|
||||
- func IsValidTag(string) bool
|
||||
- func NewPgStore(*pgxpool.Pool) *PgStore
|
||||
|
||||
### Methods
|
||||
- func (*Service) Create(context.Context, CreateInput) (Clan, error)
|
||||
- func (*Service) Get(context.Context, string) (Clan, error)
|
||||
- func (*Service) GetByTag(context.Context, string) (Clan, error)
|
||||
- func (*Service) List(context.Context, int, int) ([]Clan, error)
|
||||
- func (*Service) Update(context.Context, string, UpdateInput) (Clan, error)
|
||||
- func (*Service) Delete(context.Context, string) error
|
||||
- func (*PgStore) Exec(context.Context, string) (int64, error)
|
||||
- func (*PgStore) Create(context.Context, Clan) error
|
||||
- func (*PgStore) Get(context.Context, string) (Clan, error)
|
||||
- func (*PgStore) GetByTag(context.Context, string) (Clan, error)
|
||||
- func (*PgStore) List(context.Context, int, int) ([]Clan, error)
|
||||
- func (*PgStore) Delete(context.Context, string) error
|
||||
- func (*PgStore) Update(context.Context, Clan) error
|
||||
- func (*PgStore) Count(context.Context) (int, error)
|
||||
|
||||
---
|
||||
|
||||
## Package `control` (`internal/control`)
|
||||
|
||||
Package control owns the authoritative race state.
|
||||
|
||||
For PoC, the physics model is intentionally simple (no tire slip, no
|
||||
collision): each car integrates position from (throttle, brake, steering).
|
||||
Realistic physics, anti-cheat checks, and CV-based ground truth will be
|
||||
added in MVP / production phases.
|
||||
|
||||
### Structs
|
||||
- **struct Car** { ID, DriverID, DriverName, DeviceID, X, Y, Heading, Speed, Lap, Sector, LastLapMs, BestLapMs, DNF, AppliedSteering, AppliedThrottle, AppliedBrake }
|
||||
- **struct Engine**
|
||||
|
||||
### Functions
|
||||
- func NewEngine(int) *Engine
|
||||
|
||||
### Methods
|
||||
- func (RacePhase) String() string
|
||||
- func (*Car) ApplyInput(transport.InputState, float64)
|
||||
- func (*Car) Snapshot() transport.CarInfo
|
||||
- func (*Engine) Snapshots() ?
|
||||
- func (*Engine) Phase() RacePhase
|
||||
- func (*Engine) SetPhase(RacePhase)
|
||||
- func (*Engine) AddCar(string, string, int, *int) (*Car, error)
|
||||
- func (*Engine) RemoveCar(string)
|
||||
- func (*Engine) GetCarByDriver(string) *Car
|
||||
- func (*Engine) Run(context.Context)
|
||||
- func (*Engine) Stop()
|
||||
|
||||
---
|
||||
|
||||
## Package `drivers` (`internal/drivers`)
|
||||
|
||||
Package drivers owns persistence and business logic for drivers
|
||||
(racers). Each driver has a unique 3-letter nickname (A-Z,
|
||||
uppercase, exactly 3 chars), a name, an optional avatar URL and an
|
||||
optional clan reference.
|
||||
|
||||
### Interfaces
|
||||
- **interface Store** { Exec, Create, Get, GetByNickname, List, Delete, Update, Count }
|
||||
|
||||
### Structs
|
||||
- **struct Service**
|
||||
- **struct CreateInput** { ID, Nickname, Name, AvatarURL, ClanID }
|
||||
- **struct UpdateInput** { Name, AvatarURL, ClanID }
|
||||
- **struct Driver** { ID, Nickname, Name, AvatarURL, ClanID, CreatedMs, UpdatedMs }
|
||||
- **struct PgStore**
|
||||
|
||||
### Functions
|
||||
- func NewService(Store) *Service
|
||||
- func IsValidNickname(string) bool
|
||||
- func NewPgStore(*pgxpool.Pool) *PgStore
|
||||
|
||||
### Methods
|
||||
- func (*Service) Create(context.Context, CreateInput) (Driver, error)
|
||||
- func (*Service) Get(context.Context, string) (Driver, error)
|
||||
- func (*Service) GetByNickname(context.Context, string) (Driver, error)
|
||||
- func (*Service) List(context.Context, int, int, string) ([]Driver, error)
|
||||
- func (*Service) Update(context.Context, string, UpdateInput) (Driver, error)
|
||||
- func (*Service) Delete(context.Context, string) error
|
||||
- func (*PgStore) Exec(context.Context, string) (int64, error)
|
||||
- func (*PgStore) Create(context.Context, Driver) error
|
||||
- func (*PgStore) Get(context.Context, string) (Driver, error)
|
||||
- func (*PgStore) GetByNickname(context.Context, string) (Driver, error)
|
||||
- func (*PgStore) List(context.Context, int, int, string) ([]Driver, error)
|
||||
- func (*PgStore) Update(context.Context, Driver) error
|
||||
- func (*PgStore) Delete(context.Context, string) error
|
||||
- func (*PgStore) Count(context.Context) (int, error)
|
||||
|
||||
---
|
||||
|
||||
## Package `lobby` (`internal/lobby`)
|
||||
|
||||
Package lobby owns the "outside any race" state of the server: which
|
||||
races are open, which drivers are connected but idle, who is the host
|
||||
of each race.
|
||||
|
||||
The lobby is the single source of truth for matchmaking and for
|
||||
rendering the "what races are available right now" list. It does NOT
|
||||
own per-race physics — that's the Engine (internal/control).
|
||||
|
||||
All operations are thread-safe. Mutations broadcast a snapshot event
|
||||
to subscribers (see Service.Subscribe).
|
||||
|
||||
### Interfaces
|
||||
- **interface Persistence** { UpsertRace, DeleteRace, AddDriver, RemoveDriver, SetRaceStatus, UpsertDriver, DeleteDriver }
|
||||
|
||||
### Structs
|
||||
- **struct CreateRaceOptions** { ID, Name, TrackID, MaxCars, Laps, TimeLimitS }
|
||||
- **struct RaceMeta** { ID, Name, TrackID, MaxCars, Laps, TimeLimitS, DriverIDs, Status, CreatedMs, StartedMs }
|
||||
- **struct DriverMeta** { ID, Name, Nickname, AvatarURL, ClanID, ClanTag, Status, RaceID, ConnectedMs, LastSeenMs, Country, AvatarHue, CarID }
|
||||
- **struct Snapshot** { GeneratedMs, Races, Drivers, Version }
|
||||
- **struct Stats** { RacesTotal, RacesOpen, RacesActive, DriversIdle, DriversTotal }
|
||||
- **struct Service**
|
||||
|
||||
### Functions
|
||||
- func NewService() *Service
|
||||
|
||||
### Methods
|
||||
- func (*Service) CreateRace(CreateRaceOptions) (RaceMeta, error)
|
||||
- func (*Service) AddRace(RaceMeta)
|
||||
- func (*Service) AddDriver(string, string, string) (DriverMeta, error)
|
||||
- func (*Service) RemoveDriver(string)
|
||||
- func (*Service) Heartbeat(string)
|
||||
- func (*Service) SetDriverProfile(string, string, string, string, string)
|
||||
- func (*Service) AddDriverToRace(string, string) error
|
||||
- func (*Service) RemoveDriverFromRace(string)
|
||||
- func (*Service) SetRaceStatus(string, RaceStatus) error
|
||||
- func (*Service) DeleteRace(string) error
|
||||
- func (*Service) QuickPlay(string, int) (RaceMeta, error)
|
||||
- func (*Service) GetRace(string) (RaceMeta, error)
|
||||
- func (*Service) GetDriver(string) (DriverMeta, error)
|
||||
- func (*Service) SelectCar(string, *string) error
|
||||
- func (*Service) ListRaces() []RaceMeta
|
||||
- func (*Service) ListDrivers() []DriverMeta
|
||||
- func (*Service) SetPersistence(Persistence)
|
||||
- func (*Service) SetRaceLifecycleHook(func, func)
|
||||
- func (*Service) Subscribe() (?, func)
|
||||
- func (*Service) Stop()
|
||||
- func (*Service) Done() ?
|
||||
- func (*Service) Snapshot() Snapshot
|
||||
- func (*Service) Stats() Stats
|
||||
|
||||
---
|
||||
|
||||
## Package `stats` (`internal/stats`)
|
||||
|
||||
Package stats collects and exposes server and per-driver metrics.
|
||||
|
||||
PoC: in-memory only, atomic counters where possible, mutex around
|
||||
driver and race records. The data lives for the lifetime of the
|
||||
process. A future storage layer will swap this for Postgres/Redis.
|
||||
|
||||
Public surface is split into:
|
||||
- lifecycle hooks (Connect/Disconnect/Race*/Lap*/Input*/RTT) —
|
||||
called from cmd/poc-server and from internal/control and lobby
|
||||
- Snapshot() — full server snapshot for /stats/detailed
|
||||
- DriverProfile(id) — per-driver summary
|
||||
- RecentLaps(n) — last N laps across all races
|
||||
|
||||
### Structs
|
||||
- **struct ServerStats** { StartedMs, UptimeMs, CurrentClients, PeakClients, TotalConnections, TotalDisconnects, TotalRacesCreated, TotalRacesStarted, TotalRacesFinished, TotalLapsRecorded, TotalInputsRecv, TotalSnapshotsSent, SnapshotsDropped, AvgRTTMs, RecentRTTs }
|
||||
- **struct DriverProfile** { ID, Name, TotalRacesStarted, TotalRacesFinished, TotalLaps, BestLapMs, LastLapMs, TotalDistanceM, TotalPlaytimeMs, Wins, Podiums, LastSeenMs }
|
||||
- **struct LapRecord** { RaceID, DriverID, LapMs, Sector1Ms, Sector2Ms, Sector3Ms, Position, TSMs }
|
||||
- **struct RaceResult** { RaceID, DriverID, FinishedAtMs, Position, TotalLaps, BestLapMs, TotalTimeMs, DNF }
|
||||
- **struct Collector**
|
||||
|
||||
### Functions
|
||||
- func NewCollector() *Collector
|
||||
|
||||
### Methods
|
||||
- func (*Collector) Start()
|
||||
- func (*Collector) OnConnect(string, string)
|
||||
- func (*Collector) OnDisconnect(string, int64)
|
||||
- func (*Collector) OnRaceCreated()
|
||||
- func (*Collector) OnRaceStarted([]string)
|
||||
- func (*Collector) OnRaceFinished(RaceResult)
|
||||
- func (*Collector) OnLapRecorded(LapRecord)
|
||||
- func (*Collector) OnInputProcessed()
|
||||
- func (*Collector) OnSnapshotSent(uint64, uint64)
|
||||
- func (*Collector) OnRTT(int64)
|
||||
- func (*Collector) OnDistance(string, float64)
|
||||
- func (*Collector) Server() ServerStats
|
||||
- func (*Collector) Driver(string, string) DriverProfile
|
||||
- func (*Collector) Drivers() []DriverProfile
|
||||
- func (*Collector) RecentLaps(int) []LapRecord
|
||||
- func (*Collector) RecentResults(int) []RaceResult
|
||||
|
||||
---
|
||||
|
||||
## Package `races` (`internal/races`)
|
||||
|
||||
Package races owns the cross-cutting "race list" surface: keyset-paginated
|
||||
listing of live and finished races, upcoming (next-N) selection, the
|
||||
driver-to-race queue and the recurring race-plan scheduler.
|
||||
|
||||
The package composes two storage backends:
|
||||
|
||||
- lobby.Service (in-memory): authoritative source for active races
|
||||
(status = lobby | countdown | racing). Used directly for the live
|
||||
page of /api/races, for the upcoming view and for the scheduler
|
||||
materialisation.
|
||||
- Postgres (pgxpool): authoritative for finished races (snapshotted
|
||||
on SetRaceStatus(finished)), for race plans and for the queue.
|
||||
Finished races are read with keyset pagination on
|
||||
(finished_ms DESC, id DESC).
|
||||
|
||||
### Structs
|
||||
- **struct Cursor** { Ms, ID }
|
||||
- **struct LeaderboardEntry** { DriverID, Name, Nickname, AvatarURL, ClanID, ClanTag, Points, BestPos, BestTimeMs, Rank }
|
||||
- **struct LeaderboardResult** { Items, Total, CurrentDriver }
|
||||
- **struct TrackCalendarInfo** { ID, TrackID, TrackName, StartAt, EndAt }
|
||||
- **struct TrackCalendarLeaderboard** { TrackID, TrackName, StartAt, EndAt, Status, Podium, CurrentDriver }
|
||||
- **struct LiveStore**
|
||||
- **struct Service**
|
||||
- **struct ListFilter** { Statuses, TrackID, Cursor, Limit }
|
||||
- **struct RaceItem** { Source, Meta, Podium }
|
||||
- **struct ListResult** { Items, NextCursor, TotalCount }
|
||||
- **struct UpcomingItem** { Meta, QueueLen, PlanID, StartAtMs }
|
||||
- **struct JoinUpcomingResult** { Joined, Skipped }
|
||||
- **struct SnapshotFinished** { Meta, FinishedMs, TotalLaps, WinnerDriverID, WinnerName, BestLapMs, Results }
|
||||
- **struct CreatePlanInput** { ID, Name, TrackID, MaxCars, Laps, TimeLimitS, StartAtMs, IntervalS, Count, Enabled }
|
||||
- **struct Scheduler**
|
||||
- **struct PodiumEntry** { Position, DriverID, Name, TotalTimeMs, Nickname, AvatarUrl, ClanID }
|
||||
- **struct DriverResult** { DriverID, Name, TotalTimeMs, BestLapMs, Position, Nickname, AvatarUrl, ClanID }
|
||||
- **struct FinishedRace** { ID, Name, TrackID, MaxCars, Laps, TimeLimitS, DriverIDs, Status, CreatedMs, StartedMs, FinishedMs, DurationMs, TotalLaps, TotalDrivers, Results, WinnerDriverID, WinnerName, BestLapMs, Podium }
|
||||
- **struct RacePlan** { ID, Name, TrackID, MaxCars, Laps, TimeLimitS, StartAtMs, IntervalS, Count, Enabled, CreatedMs, UpdatedMs, NextFireMs, FiresDone }
|
||||
- **struct QueueEntry** { DriverID, RaceID, PlanID, EnqueuedMs }
|
||||
- **struct PgStore**
|
||||
|
||||
### Functions
|
||||
- func DecodeCursor(string) (Cursor, error)
|
||||
- func NewLiveStore(*PgStore) *LiveStore
|
||||
- func NewService(*PgStore, *lobby.Service) *Service
|
||||
- func NewScheduler(*PgStore, *lobby.Service, time.Duration) *Scheduler
|
||||
- func NewPgStore(*pgxpool.Pool) *PgStore
|
||||
- func ParseStatusFilter(string) ([]StatusFilter, error)
|
||||
|
||||
### Methods
|
||||
- func (Cursor) String() string
|
||||
- func (Cursor) IsZero() bool
|
||||
- func (Cursor) Encode() string
|
||||
- func (*PgStore) GetTrackLeaderboard(context.Context, string, int, int, int) ([]LeaderboardEntry, int, error)
|
||||
- func (*PgStore) GetTrackLeaderboardDriver(context.Context, string, int, string) (*LeaderboardEntry, error)
|
||||
- func (*PgStore) GetOverallLeaderboard(context.Context, int, int, int) ([]LeaderboardEntry, int, error)
|
||||
- func (*PgStore) GetOverallLeaderboardDriver(context.Context, int, string) (*LeaderboardEntry, error)
|
||||
- func (*PgStore) GetDriverLeaderboardProfile(context.Context, string) (*LeaderboardEntry, error)
|
||||
- func (*PgStore) GetCalendarLeaderboardTracks(context.Context) ([]TrackCalendarInfo, error)
|
||||
- func (*LiveStore) UpsertRace(context.Context, lobby.RaceMeta) error
|
||||
- func (*LiveStore) DeleteRace(context.Context, string) error
|
||||
- func (*LiveStore) AddDriver(context.Context, string, string, int) error
|
||||
- func (*LiveStore) RemoveDriver(context.Context, string, string) error
|
||||
- func (*LiveStore) SetRaceStatus(context.Context, string, lobby.RaceStatus, int64) error
|
||||
- func (*LiveStore) UpsertDriver(context.Context, lobby.DriverMeta) error
|
||||
- func (*LiveStore) DeleteDriver(context.Context, string) error
|
||||
- func (*LiveStore) ListAllRaces(context.Context) ([]lobby.RaceMeta, error)
|
||||
- func (*LiveStore) ListAllDrivers(context.Context) ([]lobby.DriverMeta, error)
|
||||
- func (*LiveStore) ListLivePaged(context.Context, []string, string, Cursor, int) ([]lobby.RaceMeta, error)
|
||||
- func (*LiveStore) ListUpcoming(context.Context, int) ([]lobby.RaceMeta, error)
|
||||
- func (*LiveStore) Exec(context.Context, string) (int64, error)
|
||||
- func (*Service) SetLiveStore(*LiveStore)
|
||||
- func (*Service) RestoreFromDB(context.Context) (int, int, error)
|
||||
- func (*Service) SetMaxUpcoming(int)
|
||||
- func (*Service) ListRaces(context.Context, ListFilter) (ListResult, error)
|
||||
- func (*Service) Upcoming(context.Context, int) ([]UpcomingItem, error)
|
||||
- func (*Service) JoinUpcoming(context.Context, string, int) (JoinUpcomingResult, error)
|
||||
- func (*Service) LeaveQueue(context.Context, string, string) error
|
||||
- func (*Service) ListQueue(context.Context, string) ([]QueueEntry, error)
|
||||
- func (*Service) PersistFinished(context.Context, SnapshotFinished) error
|
||||
- func (*Service) CreatePlan(context.Context, CreatePlanInput) (RacePlan, error)
|
||||
- func (*Service) ListPlans(context.Context, Cursor, int) ([]RacePlan, error)
|
||||
- func (*Service) CountPlans(context.Context) (int, error)
|
||||
- func (*Service) DeletePlan(context.Context, string) error
|
||||
- func (*Service) GetLeaderboard(context.Context, string, int, int, int, string) (*LeaderboardResult, error)
|
||||
- func (*Service) GetCalendarLeaderboard(context.Context, int, string) ([]TrackCalendarLeaderboard, error)
|
||||
- func (*Scheduler) Run(context.Context)
|
||||
- func (*Scheduler) Stop()
|
||||
- func (*Scheduler) Done() ?
|
||||
- func (*PgStore) Exec(context.Context, string, ?) (int64, error)
|
||||
- func (*PgStore) ListTrackIDs(context.Context) ([]string, error)
|
||||
- func (*PgStore) UpsertLive(context.Context, lobby.RaceMeta) error
|
||||
- func (*PgStore) DeleteLive(context.Context, string) error
|
||||
- func (*PgStore) AddLiveDriver(context.Context, string, string, int) error
|
||||
- func (*PgStore) RemoveLiveDriver(context.Context, string, string) error
|
||||
- func (*PgStore) SetLiveStatus(context.Context, string, lobby.RaceStatus, int64) error
|
||||
- func (*PgStore) ListLivePaged(context.Context, []string, string, Cursor, int) ([]lobby.RaceMeta, error)
|
||||
- func (*PgStore) ListLiveUpcoming(context.Context, int) ([]lobby.RaceMeta, error)
|
||||
- func (*PgStore) GetLive(context.Context, string) (lobby.RaceMeta, error)
|
||||
- func (*PgStore) ListAllRaces(context.Context) ([]lobby.RaceMeta, error)
|
||||
- func (*PgStore) UpsertLobbyDriver(context.Context, lobby.DriverMeta) error
|
||||
- func (*PgStore) DeleteLobbyDriver(context.Context, string) error
|
||||
- func (*PgStore) ListLobbyDrivers(context.Context) ([]lobby.DriverMeta, error)
|
||||
- func (*PgStore) InsertFinished(context.Context, FinishedRace) error
|
||||
- func (*PgStore) ListFinished(context.Context, string, Cursor, int) ([]FinishedRace, error)
|
||||
- func (*PgStore) CountRaces(context.Context, []string, string) (int, error)
|
||||
- func (*PgStore) CreatePlan(context.Context, RacePlan) error
|
||||
- func (*PgStore) GetPlan(context.Context, string) (RacePlan, error)
|
||||
- func (*PgStore) ListPlans(context.Context, Cursor, int) ([]RacePlan, error)
|
||||
- func (*PgStore) CountPlans(context.Context) (int, error)
|
||||
- func (*PgStore) DeletePlan(context.Context, string) error
|
||||
- func (*PgStore) SetPlanEnabled(context.Context, string, bool) error
|
||||
- func (*PgStore) AdvancePlan(context.Context, string, int64) error
|
||||
- func (*PgStore) DuePlans(context.Context, int64, int) ([]RacePlan, error)
|
||||
- func (*PgStore) Enqueue(context.Context, string, string, string) (QueueEntry, error)
|
||||
- func (*PgStore) Dequeue(context.Context, string, string) error
|
||||
- func (*PgStore) ListQueueByDriver(context.Context, string) ([]QueueEntry, error)
|
||||
- func (*PgStore) ListQueueByRace(context.Context, string) ([]QueueEntry, error)
|
||||
- func (*PgStore) CountQueueByRace(context.Context, string) (int, error)
|
||||
- func (FinishedRace) ToLobbyMeta() lobby.RaceMeta
|
||||
|
||||
---
|
||||
|
||||
## Package `main` (`scripts/graphify`)
|
||||
|
||||
Package main is the graphify utility. It walks the codebase, parses Go AST,
|
||||
and outputs a highly compact symbol summary to server/CODEBASE.md.
|
||||
This is used for token savings and helping developers/AI agents quickly map out imports and structures.
|
||||
|
||||
### Structs
|
||||
- **struct PackageSummary** { Name, Path, Doc, Structs, Interfaces, Functions, Methods }
|
||||
|
||||
---
|
||||
|
||||
## Package `main` (`.`)
|
||||
|
||||
Smoke test: ws client connects, exchanges a few messages, exits.
|
||||
Run: go run ./smoke_test.go
|
||||
|
||||
---
|
||||
|
||||
## Package `transport` (`internal/transport`)
|
||||
|
||||
Package transport defines the wire format between clients and the server.
|
||||
|
||||
PoC: JSON for fast iteration. Specified by proto/client_server.proto for
|
||||
the production binary protobuf format.
|
||||
|
||||
TODO PoC: replace JSON with protobuf-generated structs (see
|
||||
../proto/client_server.proto).
|
||||
|
||||
### Structs
|
||||
- **struct Envelope** { Type, Seq, TSMs, Payload }
|
||||
- **struct ClientHello** { Version, Token, Locale, ClientID }
|
||||
- **struct ServerHello** { SessionID, RaceTick, ServerVersion, Config }
|
||||
- **struct JoinRace** { RaceID, CarSlot, CarName }
|
||||
- **struct LeaveRace** { RaceID }
|
||||
- **struct InputState** { Steering, Throttle, Brake, Gear, Buttons }
|
||||
- **struct ChatMessage** { Text }
|
||||
- **struct Ping** { ClientTSMs }
|
||||
- **struct RaceSnapshot** { Tick, TSMs, Elapsed, Cars }
|
||||
- **struct CarInfo** { ID, DriverName, X, Y, Heading, Speed, Lap, Sector, LastLapMs, BestLapMs, InputApplied, DNF }
|
||||
- **struct InputAck** { Seq, AppliedAtMs, ServerTickMs }
|
||||
- **struct Correction** { Tick, DeltaSteering, DeltaThrottle, Reason }
|
||||
- **struct RaceEvent** { RaceID, Event, CarID, Sector, LapMs, TSMs }
|
||||
- **struct Pong** { ClientTSMs, ServerTSMs }
|
||||
- **struct ErrorMsg** { Code, Message }
|
||||
- **struct LobbyRace** { ID, Name, TrackID, MaxCars, Laps, TimeLimitS, DriverIDs, Status, CreatedMs, StartedMs, Podium }
|
||||
- **struct RacePodiumEntry** { Position, DriverID, Name, TotalTimeMs, Nickname, AvatarUrl, ClanID }
|
||||
- **struct VersionResponse** { Version }
|
||||
- **struct WebRTCConnectRequest** { SDP, Type, DriverID }
|
||||
- **struct WebRTCConnectResponse** { SDP, Type }
|
||||
- **struct LobbyDriver** { ID, Name, Status, RaceID, ConnectedMs, LastSeenMs, Country, AvatarHue }
|
||||
- **struct LobbySnapshot** { GeneratedMs, Version, Races, Drivers }
|
||||
- **struct LobbyCreate** { RaceID, Name, TrackID, MaxCars, Laps, TimeLimitS }
|
||||
- **struct LobbyJoinRace** { RaceID, CarSlot, CarName }
|
||||
- **struct LobbyLeaveRace** { RaceID }
|
||||
- **struct LobbyKickDriver** { RaceID, DriverID }
|
||||
- **struct LobbyDelete** { RaceID }
|
||||
- **struct LobbyCreatedAck** { Ok, Race, Error, Driver }
|
||||
- **struct LobbyJoinAck** { Ok, Race, Error }
|
||||
- **struct TrackWaypoint** { X, Y, HeadingRad, SpeedMs, Curvature }
|
||||
- **struct TrackBounds** { MinX, MinY, MaxX, MaxY, LengthM, WidthM }
|
||||
- **struct TrackWire** { ID, Name, Description, AuthorID, Visibility, Scale, LengthM, WidthM, LaneWidthM, Bounds, Surface, Tags, Centerline, BestLapMs, BestLapHolder, CreatedMs, UpdatedMs, IsActive }
|
||||
- **struct TrackListRequest** { Scope, Tag }
|
||||
- **struct TrackGetRequest** { ID }
|
||||
- **struct TrackCreateRequest** { Track }
|
||||
- **struct TrackUpdateRequest** { ID, Patch }
|
||||
- **struct TrackPatch** { Name, Description, Visibility, Scale, LengthM, WidthM, LaneWidthM, Surface, Tags, Centerline }
|
||||
- **struct TrackDeleteRequest** { ID }
|
||||
- **struct TrackSnapshot** { GeneratedMs, Version, Tracks }
|
||||
- **struct TrackAck** { Ok, ID, Error, Track }
|
||||
- **struct MotorWire** { Kind, Class, KV, PowerW }
|
||||
- **struct BatteryWire** { VoltageV, CapacityMah, Cells, Chemistry }
|
||||
- **struct ChassisWire** { Model, Material, Printed, WheelbaseMm, TrackMm }
|
||||
- **struct CarWire** { ID, Name, OwnerID, Visibility, Scale, LengthMm, WidthMm, HeightMm, WeightG, Chassis, Motor, Battery, Drive, TopSpeedMs, ColorHex, AvatarURL, Active, TotalDistanceM, TotalRaces, TotalLaps, BestLapMs, CreatedMs, UpdatedMs, DeviceID }
|
||||
- **struct CarListRequest** { Scope, OwnerID }
|
||||
- **struct CarGetRequest** { ID }
|
||||
- **struct CarCreateRequest** { Car }
|
||||
- **struct CarUpdateRequest** { ID, Patch }
|
||||
- **struct CarPatch** { Name, Visibility, Scale, LengthMm, WidthMm, HeightMm, WeightG, Chassis, Motor, Battery, Drive, TopSpeedMs, ColorHex, AvatarURL, Active, DeviceID }
|
||||
- **struct CarDeleteRequest** { ID }
|
||||
- **struct CarSnapshot** { GeneratedMs, Version, Cars }
|
||||
- **struct CarAck** { Ok, ID, Error, Car }
|
||||
- **struct CatalogSummary** { TracksTotal, TracksSystem, TracksPublic, TracksPrivate, CarsTotal, CarsSystem, CarsPublic, CarsPrivate, CarsActive, TSMs }
|
||||
- **struct RaceListResponse** { Items, NextCursor, Count }
|
||||
- **struct RaceUpcomingItem** { Meta, QueueLen, PlanID, StartAtMs }
|
||||
- **struct RaceUpcomingResponse** { Items, Count }
|
||||
- **struct RaceQueueEntry** { DriverID, RaceID, PlanID, EnqueuedMs }
|
||||
- **struct RaceQueueJoinRequest** { DriverID, Limit }
|
||||
- **struct RaceQueueJoinResponse** { Joined, Skipped }
|
||||
- **struct RaceQueueListResponse** { Items, Count }
|
||||
- **struct RacePlan** { ID, Name, TrackID, MaxCars, Laps, TimeLimitS, StartAtMs, IntervalS, Count, Enabled, CreatedMs, UpdatedMs, NextFireMs, FiresDone }
|
||||
- **struct RacePlanCreateRequest** { ID, Name, TrackID, MaxCars, Laps, TimeLimitS, StartAtMs, IntervalS, Count, Enabled }
|
||||
- **struct RacePlanListResponse** { Items, NextCursor, Count }
|
||||
- **struct Driver** { ID, Nickname, Name, AvatarURL, ClanID, CreatedMs, UpdatedMs }
|
||||
- **struct DriverCreateRequest** { ID, Nickname, Name, AvatarURL, ClanID }
|
||||
- **struct DriverUpdateRequest** { Name, AvatarURL, ClanID }
|
||||
- **struct DriverListResponse** { Items, Count }
|
||||
- **struct Clan** { ID, Tag, Name, AvatarURL, CreatedMs, UpdatedMs }
|
||||
- **struct ClanCreateRequest** { ID, Tag, Name, AvatarURL }
|
||||
- **struct ClanUpdateRequest** { Name, AvatarURL }
|
||||
- **struct ClanListResponse** { Items, Count }
|
||||
- **struct StatsServer** { StartedMs, UptimeMs, CurrentClients, PeakClients, TotalConnections, TotalDisconnects, TotalRacesCreated, TotalRacesStarted, TotalRacesFinished, TotalLapsRecorded, TotalInputsRecv, TotalSnapshotsSent, SnapshotsDropped, AvgRTTMs }
|
||||
- **struct StatsDriver** { ID, Name, TotalRacesStarted, TotalRacesFinished, TotalLaps, BestLapMs, LastLapMs, TotalDistanceM, TotalPlaytimeMs, Wins, Podiums, LastSeenMs }
|
||||
- **struct StatsLap** { RaceID, DriverID, LapMs, Sector1Ms, Sector2Ms, Sector3Ms, Position, TSMs }
|
||||
- **struct StatsRaceResult** { RaceID, DriverID, FinishedAtMs, Position, TotalLaps, BestLapMs, TotalTimeMs, DNF }
|
||||
- **struct StatsSnapshot** { Server, Drivers, Laps, Results, TSMs }
|
||||
- **struct StatsRequest** { Scope, TargetID }
|
||||
- **struct RaceStanding** { Position, CarID, DriverID, DriverName, Lap, Sector, BestLapMs, LastLapMs, GapMs, DNF }
|
||||
- **struct RaceStandingsMsg** { RaceID, Tick, TSMs, Lap, Elapsed, Rows }
|
||||
- **struct LapRecordedMsg** { RaceID, DriverID, Lap, LapMs, Sector1Ms, Sector2Ms, Sector3Ms, TSMs, Position }
|
||||
- **struct RaceResultMsg** { RaceID, DriverID, Position, TotalLaps, BestLapMs, TotalTimeMs, DNF, FinishedAtMs }
|
||||
- **struct LeaderboardEntry** { DriverID, Nickname, Name, AvatarURL, ClanID, ClanTag, Points, BestPos, BestTimeMs, Rank }
|
||||
- **struct LeaderboardResponse** { Items, Total, Limit, Offset, CurrentDriver }
|
||||
- **struct TrackCalendarEntry** { ID, TrackID, StartAt, EndAt }
|
||||
- **struct TrackCalendarCreateRequest** { TrackID, StartAt, EndAt }
|
||||
- **struct TrackCalendarResponse** { Items }
|
||||
- **struct TrackCalendarLeaderboard** { TrackID, TrackName, StartAt, EndAt, Status, Podium, CurrentDriver }
|
||||
- **struct TrackCalendarLeaderboardResponse** { Items }
|
||||
|
||||
### Functions
|
||||
- func Encode(*Envelope) ([]byte, error)
|
||||
- func Decode([]byte) (*Envelope, error)
|
||||
|
||||
---
|
||||
|
||||
## Package `main` (`cmd/poc-server`)
|
||||
|
||||
HTTP handlers for the catalog (tracks + cars).
|
||||
|
||||
All HTTP routes live under /api/*:
|
||||
GET /api/tracks list tracks (optional ?scope=system|public|private&tag=)
|
||||
GET /api/tracks/{id} get a single track
|
||||
POST /api/tracks create a track
|
||||
PUT /api/tracks/{id} update a track
|
||||
DELETE /api/tracks/{id} delete a track
|
||||
GET /api/cars list cars (optional ?scope=...&owner_id=...)
|
||||
GET /api/cars/{id} get a single car
|
||||
POST /api/cars create a car
|
||||
PUT /api/cars/{id} update a car
|
||||
DELETE /api/cars/{id} delete a car
|
||||
GET /api/catalog combined snapshot + summary
|
||||
|
||||
WebSocket access is wired up directly in main.go's readPump switch.
|
||||
|
||||
### Structs
|
||||
- **struct Hub**
|
||||
- **struct UDPVideoService**
|
||||
- **struct WebRTCService**
|
||||
|
||||
### Functions
|
||||
- func NewHub(*slog.Logger) *Hub
|
||||
- func NewUDPVideoService(*slog.Logger, string, int) *UDPVideoService
|
||||
- func NewWebRTCService(*slog.Logger, *control.Engine, *lobby.Service, *UDPVideoService, *catalog.Service) *WebRTCService
|
||||
|
||||
### Methods
|
||||
- func (*catalogBroadcaster) Run()
|
||||
- func (*catalogBroadcaster) Stop()
|
||||
- func (*Hub) BroadcastSnapshot(transport.RaceSnapshot)
|
||||
- func (*statusRecorder) WriteHeader(int)
|
||||
- func (*statusRecorder) Hijack() (net.Conn, *bufio.ReadWriter, error)
|
||||
- func (*UDPVideoService) Start(context.Context, *sync.WaitGroup)
|
||||
- func (*UDPVideoService) SendControlPacket(uint8, []byte) error
|
||||
- func (*UDPVideoService) SendCommand(uint8, string) error
|
||||
- func (*UDPVideoService) StreamHandler() http.HandlerFunc
|
||||
- func (*UDPVideoService) ControlHandler() http.HandlerFunc
|
||||
- func (*WebRTCService) Start(context.Context, *sync.WaitGroup)
|
||||
- func (*WebRTCService) ConnectHandler() http.HandlerFunc
|
||||
- func (*WebRTCService) CloseSession(string)
|
||||
- func (*WebRTCService) CloseAll()
|
||||
|
||||
---
|
||||
|
||||
## Package `main` (`scripts/genseed`)
|
||||
|
||||
Package main contains the genseed utility. It is built into a standalone
|
||||
binary via `go build ./scripts/genseed` and is not part of the runtime.
|
||||
|
||||
Usage:
|
||||
|
||||
go run ./scripts/genseed seeds.json > 002_seed.sql
|
||||
|
||||
It reads the JSON dump produced by `DUMP_SEEDS=1 go test ./internal/catalog`
|
||||
and emits ON CONFLICT-safe INSERTs for the catalog tables.
|
||||
|
||||
---
|
||||
|
||||
## Package `main` (`scripts/genseed-json`)
|
||||
|
||||
Package main contains a small helper that prints the default seeds
|
||||
(5 F1 tracks + 5 F1 cars) as JSON to stdout. It is run as:
|
||||
|
||||
go run ./scripts/genseed-json > seeds.json
|
||||
|
||||
which is then fed into scripts/genseed to regenerate the SQL.
|
||||
|
||||
---
|
||||
|
||||
@@ -84,4 +84,9 @@ compose-down:
|
||||
clean:
|
||||
rm -rf $(BIN_DIR)
|
||||
|
||||
.PHONY: graphify
|
||||
graphify:
|
||||
$(GO) run ./scripts/graphify/main.go
|
||||
|
||||
.DEFAULT_GOAL := build
|
||||
|
||||
|
||||
@@ -10,14 +10,14 @@ import (
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
)
|
||||
|
||||
// Service composes validation on top of PgStore.
|
||||
// Service composes validation on top of Store.
|
||||
type Service struct {
|
||||
pg *PgStore
|
||||
pg Store
|
||||
now func() time.Time
|
||||
}
|
||||
|
||||
// NewService wires a service.
|
||||
func NewService(pg *PgStore) *Service {
|
||||
func NewService(pg Store) *Service {
|
||||
return &Service{pg: pg, now: time.Now}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
package clans
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
)
|
||||
|
||||
type memoryStore struct {
|
||||
clans map[string]Clan
|
||||
}
|
||||
|
||||
func newMemoryStore() *memoryStore {
|
||||
return &memoryStore{clans: make(map[string]Clan)}
|
||||
}
|
||||
|
||||
func (m *memoryStore) Exec(ctx context.Context, sql string) (int64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (m *memoryStore) Create(ctx context.Context, c Clan) error {
|
||||
for _, existing := range m.clans {
|
||||
if existing.Tag == c.Tag {
|
||||
return &pgconn.PgError{Code: "23505", Message: "duplicate key value violates unique constraint"}
|
||||
}
|
||||
}
|
||||
m.clans[c.ID] = c
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *memoryStore) Get(ctx context.Context, id string) (Clan, error) {
|
||||
c, ok := m.clans[id]
|
||||
if !ok {
|
||||
return Clan{}, ErrNotFound
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (m *memoryStore) GetByTag(ctx context.Context, tag string) (Clan, error) {
|
||||
for _, c := range m.clans {
|
||||
if c.Tag == tag {
|
||||
return c, nil
|
||||
}
|
||||
}
|
||||
return Clan{}, ErrNotFound
|
||||
}
|
||||
|
||||
func (m *memoryStore) List(ctx context.Context, limit, offset int) ([]Clan, error) {
|
||||
out := make([]Clan, 0)
|
||||
for _, c := range m.clans {
|
||||
out = append(out, c)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (m *memoryStore) Delete(ctx context.Context, id string) error {
|
||||
if _, ok := m.clans[id]; !ok {
|
||||
return ErrNotFound
|
||||
}
|
||||
delete(m.clans, id)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *memoryStore) Update(ctx context.Context, c Clan) error {
|
||||
if _, ok := m.clans[c.ID]; !ok {
|
||||
return ErrNotFound
|
||||
}
|
||||
m.clans[c.ID] = c
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *memoryStore) Count(ctx context.Context) (int, error) {
|
||||
return len(m.clans), nil
|
||||
}
|
||||
|
||||
func TestClansService(t *testing.T) {
|
||||
store := newMemoryStore()
|
||||
svc := NewService(store)
|
||||
svc.now = func() time.Time {
|
||||
return time.Unix(1700000000, 0)
|
||||
}
|
||||
ctx := context.Background()
|
||||
|
||||
// 1. Create a clan with valid inputs
|
||||
c1, err := svc.Create(ctx, CreateInput{
|
||||
ID: "clan-1",
|
||||
Tag: "SPD",
|
||||
Name: "Speed Demons",
|
||||
AvatarURL: "https://example.com/spd.png",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Create failed: %v", err)
|
||||
}
|
||||
if c1.ID != "clan-1" || c1.Tag != "SPD" || c1.Name != "Speed Demons" {
|
||||
t.Errorf("Unexpected created clan: %+v", c1)
|
||||
}
|
||||
|
||||
// 2. Validate Tag must be 3 uppercase ASCII letters
|
||||
_, err = svc.Create(ctx, CreateInput{Tag: "SPEED", Name: "toolongtag"})
|
||||
if !errors.Is(err, ErrInvalidInput) {
|
||||
t.Errorf("Expected ErrInvalidInput for tag 'SPEED', got: %v", err)
|
||||
}
|
||||
_, err = svc.Create(ctx, CreateInput{Tag: "S1D", Name: "withnumbertag"})
|
||||
if !errors.Is(err, ErrInvalidInput) {
|
||||
t.Errorf("Expected ErrInvalidInput for tag 'S1D', got: %v", err)
|
||||
}
|
||||
|
||||
// 3. Validate empty Name is rejected
|
||||
_, err = svc.Create(ctx, CreateInput{Tag: "AAA", Name: ""})
|
||||
if !errors.Is(err, ErrInvalidInput) {
|
||||
t.Errorf("Expected ErrInvalidInput for empty name, got: %v", err)
|
||||
}
|
||||
|
||||
// 4. Duplicate tag check
|
||||
_, err = svc.Create(ctx, CreateInput{
|
||||
ID: "clan-2",
|
||||
Tag: "SPD",
|
||||
Name: "Duplicate SPD Tag",
|
||||
})
|
||||
if !errors.Is(err, ErrAlreadyExists) {
|
||||
t.Errorf("Expected ErrAlreadyExists for duplicate tag, got: %v", err)
|
||||
}
|
||||
|
||||
// 5. Get clan
|
||||
got, err := svc.Get(ctx, "clan-1")
|
||||
if err != nil {
|
||||
t.Fatalf("Get failed: %v", err)
|
||||
}
|
||||
if got.Tag != "SPD" {
|
||||
t.Errorf("Expected SPD, got %s", got.Tag)
|
||||
}
|
||||
|
||||
// 6. Get by tag
|
||||
gotByTag, err := svc.GetByTag(ctx, "spd") // should normalize to SPD
|
||||
if err != nil {
|
||||
t.Fatalf("GetByTag failed: %v", err)
|
||||
}
|
||||
if gotByTag.ID != "clan-1" {
|
||||
t.Errorf("Expected clan-1, got %s", gotByTag.ID)
|
||||
}
|
||||
|
||||
// 7. Update clan
|
||||
updated, err := svc.Update(ctx, "clan-1", UpdateInput{
|
||||
Name: "Super Speed Demons",
|
||||
AvatarURL: "https://example.com/super-spd.png",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Update failed: %v", err)
|
||||
}
|
||||
if updated.Name != "Super Speed Demons" || updated.AvatarURL != "https://example.com/super-spd.png" {
|
||||
t.Errorf("Unexpected updated clan fields: %+v", updated)
|
||||
}
|
||||
|
||||
// 8. Delete clan
|
||||
err = svc.Delete(ctx, "clan-1")
|
||||
if err != nil {
|
||||
t.Fatalf("Delete failed: %v", err)
|
||||
}
|
||||
_, err = svc.Get(ctx, "clan-1")
|
||||
if !errors.Is(err, ErrNotFound) {
|
||||
t.Errorf("Expected ErrNotFound after deletion, got: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -39,6 +39,18 @@ type Clan struct {
|
||||
UpdatedMs int64
|
||||
}
|
||||
|
||||
// Store defines the persistence operations for clans.
|
||||
type Store interface {
|
||||
Exec(ctx context.Context, sql string) (int64, error)
|
||||
Create(ctx context.Context, c Clan) error
|
||||
Get(ctx context.Context, id string) (Clan, error)
|
||||
GetByTag(ctx context.Context, tag string) (Clan, error)
|
||||
List(ctx context.Context, limit, offset int) ([]Clan, error)
|
||||
Delete(ctx context.Context, id string) error
|
||||
Update(ctx context.Context, c Clan) error
|
||||
Count(ctx context.Context) (int, error)
|
||||
}
|
||||
|
||||
// PgStore is the Postgres-backed half of the package.
|
||||
type PgStore struct {
|
||||
pool *pgxpool.Pool
|
||||
|
||||
@@ -10,14 +10,14 @@ import (
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
)
|
||||
|
||||
// Service composes validation on top of PgStore.
|
||||
// Service composes validation on top of Store.
|
||||
type Service struct {
|
||||
pg *PgStore
|
||||
pg Store
|
||||
now func() time.Time
|
||||
}
|
||||
|
||||
// NewService wires a service.
|
||||
func NewService(pg *PgStore) *Service {
|
||||
func NewService(pg Store) *Service {
|
||||
return &Service{pg: pg, now: time.Now}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
package drivers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
)
|
||||
|
||||
type memoryStore struct {
|
||||
drivers map[string]Driver
|
||||
}
|
||||
|
||||
func newMemoryStore() *memoryStore {
|
||||
return &memoryStore{drivers: make(map[string]Driver)}
|
||||
}
|
||||
|
||||
func (m *memoryStore) Exec(ctx context.Context, sql string) (int64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (m *memoryStore) Create(ctx context.Context, d Driver) error {
|
||||
for _, existing := range m.drivers {
|
||||
if existing.Nickname == d.Nickname {
|
||||
return &pgconn.PgError{Code: "23505", Message: "duplicate key value violates unique constraint"}
|
||||
}
|
||||
}
|
||||
m.drivers[d.ID] = d
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *memoryStore) Get(ctx context.Context, id string) (Driver, error) {
|
||||
d, ok := m.drivers[id]
|
||||
if !ok {
|
||||
return Driver{}, ErrNotFound
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func (m *memoryStore) GetByNickname(ctx context.Context, nick string) (Driver, error) {
|
||||
for _, d := range m.drivers {
|
||||
if d.Nickname == nick {
|
||||
return d, nil
|
||||
}
|
||||
}
|
||||
return Driver{}, ErrNotFound
|
||||
}
|
||||
|
||||
func (m *memoryStore) List(ctx context.Context, limit, offset int, clanID string) ([]Driver, error) {
|
||||
out := make([]Driver, 0)
|
||||
for _, d := range m.drivers {
|
||||
if clanID == "" || d.ClanID == clanID {
|
||||
out = append(out, d)
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (m *memoryStore) Delete(ctx context.Context, id string) error {
|
||||
if _, ok := m.drivers[id]; !ok {
|
||||
return ErrNotFound
|
||||
}
|
||||
delete(m.drivers, id)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *memoryStore) Update(ctx context.Context, d Driver) error {
|
||||
if _, ok := m.drivers[d.ID]; !ok {
|
||||
return ErrNotFound
|
||||
}
|
||||
m.drivers[d.ID] = d
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *memoryStore) Count(ctx context.Context) (int, error) {
|
||||
return len(m.drivers), nil
|
||||
}
|
||||
|
||||
func TestDriversService(t *testing.T) {
|
||||
store := newMemoryStore()
|
||||
svc := NewService(store)
|
||||
svc.now = func() time.Time {
|
||||
return time.Unix(1700000000, 0)
|
||||
}
|
||||
ctx := context.Background()
|
||||
|
||||
// 1. Create a driver
|
||||
d1, err := svc.Create(ctx, CreateInput{
|
||||
ID: "driver-1",
|
||||
Nickname: "HAM",
|
||||
Name: "Lewis Hamilton",
|
||||
AvatarURL: "https://example.com/ham.png",
|
||||
ClanID: "clan-1",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Create failed: %v", err)
|
||||
}
|
||||
if d1.ID != "driver-1" || d1.Nickname != "HAM" || d1.Name != "Lewis Hamilton" || d1.ClanID != "clan-1" {
|
||||
t.Errorf("Unexpected created driver: %+v", d1)
|
||||
}
|
||||
|
||||
// 2. Validate Nickname must be 3 uppercase ASCII letters
|
||||
_, err = svc.Create(ctx, CreateInput{Nickname: "HAMILTON", Name: "toolong"})
|
||||
if !errors.Is(err, ErrInvalidInput) {
|
||||
t.Errorf("Expected ErrInvalidInput for tag 'HAMILTON', got: %v", err)
|
||||
}
|
||||
|
||||
// 3. Validate empty Name is rejected
|
||||
_, err = svc.Create(ctx, CreateInput{Nickname: "BOT", Name: ""})
|
||||
if !errors.Is(err, ErrInvalidInput) {
|
||||
t.Errorf("Expected ErrInvalidInput for empty name, got: %v", err)
|
||||
}
|
||||
|
||||
// 4. Duplicate nickname check
|
||||
_, err = svc.Create(ctx, CreateInput{
|
||||
ID: "driver-2",
|
||||
Nickname: "HAM",
|
||||
Name: "Another Lewis",
|
||||
})
|
||||
if !errors.Is(err, ErrAlreadyExists) {
|
||||
t.Errorf("Expected ErrAlreadyExists for duplicate nickname, got: %v", err)
|
||||
}
|
||||
|
||||
// 5. Get driver
|
||||
got, err := svc.Get(ctx, "driver-1")
|
||||
if err != nil {
|
||||
t.Fatalf("Get failed: %v", err)
|
||||
}
|
||||
if got.Nickname != "HAM" {
|
||||
t.Errorf("Expected HAM, got %s", got.Nickname)
|
||||
}
|
||||
|
||||
// 6. Get by nickname
|
||||
gotByNick, err := svc.GetByNickname(ctx, "ham") // should normalize to HAM
|
||||
if err != nil {
|
||||
t.Fatalf("GetByNickname failed: %v", err)
|
||||
}
|
||||
if gotByNick.ID != "driver-1" {
|
||||
t.Errorf("Expected driver-1, got %s", gotByNick.ID)
|
||||
}
|
||||
|
||||
// 7. Update driver
|
||||
clanID := "clan-2"
|
||||
updated, err := svc.Update(ctx, "driver-1", UpdateInput{
|
||||
Name: "Lewis Hamilton MBE",
|
||||
AvatarURL: "https://example.com/ham-mbe.png",
|
||||
ClanID: &clanID,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Update failed: %v", err)
|
||||
}
|
||||
if updated.Name != "Lewis Hamilton MBE" || updated.AvatarURL != "https://example.com/ham-mbe.png" || updated.ClanID != "clan-2" {
|
||||
t.Errorf("Unexpected updated driver fields: %+v", updated)
|
||||
}
|
||||
|
||||
// 8. Delete driver
|
||||
err = svc.Delete(ctx, "driver-1")
|
||||
if err != nil {
|
||||
t.Fatalf("Delete failed: %v", err)
|
||||
}
|
||||
_, err = svc.Get(ctx, "driver-1")
|
||||
if !errors.Is(err, ErrNotFound) {
|
||||
t.Errorf("Expected ErrNotFound after deletion, got: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -39,6 +39,18 @@ type Driver struct {
|
||||
UpdatedMs int64
|
||||
}
|
||||
|
||||
// Store defines the persistence operations for drivers.
|
||||
type Store interface {
|
||||
Exec(ctx context.Context, sql string) (int64, error)
|
||||
Create(ctx context.Context, d Driver) error
|
||||
Get(ctx context.Context, id string) (Driver, error)
|
||||
GetByNickname(ctx context.Context, nick string) (Driver, error)
|
||||
List(ctx context.Context, limit, offset int, clanID string) ([]Driver, error)
|
||||
Delete(ctx context.Context, id string) error
|
||||
Update(ctx context.Context, d Driver) error
|
||||
Count(ctx context.Context) (int, error)
|
||||
}
|
||||
|
||||
// PgStore is the Postgres-backed half of the package.
|
||||
type PgStore struct {
|
||||
pool *pgxpool.Pool
|
||||
|
||||
@@ -0,0 +1,250 @@
|
||||
// Package main is the graphify utility. It walks the codebase, parses Go AST,
|
||||
// and outputs a highly compact symbol summary to server/CODEBASE.md.
|
||||
// This is used for token savings and helping developers/AI agents quickly map out imports and structures.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type PackageSummary struct {
|
||||
Name string
|
||||
Path string
|
||||
Doc string
|
||||
Structs []string
|
||||
Interfaces []string
|
||||
Functions []string
|
||||
Methods []string
|
||||
}
|
||||
|
||||
func main() {
|
||||
rootDir := "."
|
||||
outputFile := "CODEBASE.md"
|
||||
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString("# x0gp Go Codebase Map (Generated)\n\n")
|
||||
buf.WriteString("This file contains a compiled map of all packages, structures, interfaces, and public functions in the `server` directory. It is optimized for token-efficiency so AI agents and humans can understand the codebase layout without reading all files.\n\n")
|
||||
|
||||
pkgs := make(map[string]*PackageSummary)
|
||||
fset := token.NewFileSet()
|
||||
|
||||
err := filepath.WalkDir(rootDir, func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if d.IsDir() {
|
||||
// Skip hidden and build dirs
|
||||
name := d.Name()
|
||||
if (name != "." && strings.HasPrefix(name, ".")) || name == "vendor" || name == "bin" || name == "docs" || name == "web" {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if !strings.HasSuffix(d.Name(), ".go") || strings.HasSuffix(d.Name(), "_test.go") {
|
||||
return nil
|
||||
}
|
||||
|
||||
dir := filepath.Dir(path)
|
||||
pkgSum, exists := pkgs[dir]
|
||||
if !exists {
|
||||
pkgSum = &PackageSummary{
|
||||
Path: dir,
|
||||
}
|
||||
pkgs[dir] = pkgSum
|
||||
}
|
||||
|
||||
node, err := parser.ParseFile(fset, path, nil, parser.ParseComments)
|
||||
if err != nil {
|
||||
return nil // ignore unparseable files defensively
|
||||
}
|
||||
|
||||
pkgSum.Name = node.Name.Name
|
||||
if node.Doc != nil && pkgSum.Doc == "" {
|
||||
pkgSum.Doc = strings.TrimSpace(node.Doc.Text())
|
||||
}
|
||||
|
||||
// Inspect AST for declarations
|
||||
ast.Inspect(node, func(n ast.Node) bool {
|
||||
switch decl := n.(type) {
|
||||
case *ast.GenDecl:
|
||||
for _, spec := range decl.Specs {
|
||||
if typeSpec, ok := spec.(*ast.TypeSpec); ok {
|
||||
if !ast.IsExported(typeSpec.Name.Name) {
|
||||
continue
|
||||
}
|
||||
switch typeType := typeSpec.Type.(type) {
|
||||
case *ast.StructType:
|
||||
structDesc := fmt.Sprintf("- **struct %s**", typeSpec.Name.Name)
|
||||
if typeType.Fields != nil && len(typeType.Fields.List) > 0 {
|
||||
fields := []string{}
|
||||
for _, f := range typeType.Fields.List {
|
||||
for _, id := range f.Names {
|
||||
if ast.IsExported(id.Name) {
|
||||
fields = append(fields, id.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(fields) > 0 {
|
||||
structDesc += fmt.Sprintf(" { %s }", strings.Join(fields, ", "))
|
||||
}
|
||||
}
|
||||
pkgSum.Structs = append(pkgSum.Structs, structDesc)
|
||||
case *ast.InterfaceType:
|
||||
methods := []string{}
|
||||
if typeType.Methods != nil {
|
||||
for _, m := range typeType.Methods.List {
|
||||
for _, id := range m.Names {
|
||||
if ast.IsExported(id.Name) {
|
||||
methods = append(methods, id.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pkgSum.Interfaces = append(pkgSum.Interfaces, fmt.Sprintf("- **interface %s** { %s }", typeSpec.Name.Name, strings.Join(methods, ", ")))
|
||||
}
|
||||
}
|
||||
}
|
||||
case *ast.FuncDecl:
|
||||
if !ast.IsExported(decl.Name.Name) {
|
||||
return true
|
||||
}
|
||||
// Format function signature
|
||||
params := []string{}
|
||||
if decl.Type.Params != nil {
|
||||
for _, p := range decl.Type.Params.List {
|
||||
for range p.Names {
|
||||
params = append(params, formatType(p.Type))
|
||||
}
|
||||
if len(p.Names) == 0 {
|
||||
params = append(params, formatType(p.Type))
|
||||
}
|
||||
}
|
||||
}
|
||||
results := []string{}
|
||||
if decl.Type.Results != nil {
|
||||
for _, r := range decl.Type.Results.List {
|
||||
for range r.Names {
|
||||
results = append(results, formatType(r.Type))
|
||||
}
|
||||
if len(r.Names) == 0 {
|
||||
results = append(results, formatType(r.Type))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sig := fmt.Sprintf("%s(%s)", decl.Name.Name, strings.Join(params, ", "))
|
||||
if len(results) > 0 {
|
||||
if len(results) == 1 {
|
||||
sig += " " + results[0]
|
||||
} else {
|
||||
sig += " (" + strings.Join(results, ", ") + ")"
|
||||
}
|
||||
}
|
||||
|
||||
if decl.Recv != nil && len(decl.Recv.List) > 0 {
|
||||
recvType := formatType(decl.Recv.List[0].Type)
|
||||
pkgSum.Methods = append(pkgSum.Methods, fmt.Sprintf("- func (%s) %s", recvType, sig))
|
||||
} else {
|
||||
pkgSum.Functions = append(pkgSum.Functions, fmt.Sprintf("- func %s", sig))
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error gathering package summaries: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Write out gathered summaries sorted/grouped by path
|
||||
// (we can sort keys or print them grouped by internal vs cmd)
|
||||
paths := []string{}
|
||||
for p := range pkgs {
|
||||
paths = append(paths, p)
|
||||
}
|
||||
// Simple sort: cmd/ first, then internal/
|
||||
// We want to present it nicely
|
||||
for _, path := range paths {
|
||||
sum := pkgs[path]
|
||||
cleanPath := filepath.ToSlash(path)
|
||||
buf.WriteString(fmt.Sprintf("## Package `%s` (`%s`)\n\n", sum.Name, cleanPath))
|
||||
if sum.Doc != "" {
|
||||
buf.WriteString(fmt.Sprintf("%s\n\n", sum.Doc))
|
||||
}
|
||||
|
||||
if len(sum.Interfaces) > 0 {
|
||||
buf.WriteString("### Interfaces\n")
|
||||
for _, item := range sum.Interfaces {
|
||||
buf.WriteString(item + "\n")
|
||||
}
|
||||
buf.WriteString("\n")
|
||||
}
|
||||
|
||||
if len(sum.Structs) > 0 {
|
||||
buf.WriteString("### Structs\n")
|
||||
for _, item := range sum.Structs {
|
||||
buf.WriteString(item + "\n")
|
||||
}
|
||||
buf.WriteString("\n")
|
||||
}
|
||||
|
||||
if len(sum.Functions) > 0 {
|
||||
buf.WriteString("### Functions\n")
|
||||
for _, item := range sum.Functions {
|
||||
buf.WriteString(item + "\n")
|
||||
}
|
||||
buf.WriteString("\n")
|
||||
}
|
||||
|
||||
if len(sum.Methods) > 0 {
|
||||
buf.WriteString("### Methods\n")
|
||||
for _, item := range sum.Methods {
|
||||
buf.WriteString(item + "\n")
|
||||
}
|
||||
buf.WriteString("\n")
|
||||
}
|
||||
|
||||
buf.WriteString("---\n\n")
|
||||
}
|
||||
|
||||
err = os.WriteFile(outputFile, buf.Bytes(), 0644)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error writing CODEBASE.md: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("Generated %s successfully!\n", outputFile)
|
||||
}
|
||||
|
||||
func formatType(expr ast.Expr) string {
|
||||
switch t := expr.(type) {
|
||||
case *ast.Ident:
|
||||
return t.Name
|
||||
case *ast.StarExpr:
|
||||
return "*" + formatType(t.X)
|
||||
case *ast.SelectorExpr:
|
||||
return formatType(t.X) + "." + t.Sel.Name
|
||||
case *ast.ArrayType:
|
||||
return "[]" + formatType(t.Elt)
|
||||
case *ast.MapType:
|
||||
return "map[" + formatType(t.Key) + "]" + formatType(t.Value)
|
||||
case *ast.InterfaceType:
|
||||
return "interface{}"
|
||||
case *ast.FuncType:
|
||||
return "func"
|
||||
default:
|
||||
return "?"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
# Supabase
|
||||
.branches
|
||||
.temp
|
||||
|
||||
# dotenvx
|
||||
.env.keys
|
||||
.env.local
|
||||
.env.*.local
|
||||
@@ -0,0 +1,414 @@
|
||||
# For detailed configuration reference documentation, visit:
|
||||
# https://supabase.com/docs/guides/local-development/cli/config
|
||||
# A string used to distinguish different Supabase projects on the same host. Defaults to the
|
||||
# working directory name when running `supabase init`.
|
||||
project_id = "x0gp"
|
||||
|
||||
[api]
|
||||
enabled = true
|
||||
# Port to use for the API URL.
|
||||
port = 54321
|
||||
# Schemas to expose in your API. Tables, views and stored procedures in this schema will get API
|
||||
# endpoints. `public` and `graphql_public` schemas are included by default.
|
||||
schemas = ["public", "graphql_public"]
|
||||
# Extra schemas to add to the search_path of every request.
|
||||
extra_search_path = ["public", "extensions"]
|
||||
# The maximum number of rows returns from a view, table, or stored procedure. Limits payload size
|
||||
# for accidental or malicious requests.
|
||||
max_rows = 1000
|
||||
# Controls whether new tables, views, sequences and functions created in the `public` schema by
|
||||
# `postgres` are reachable through the Data API roles (`anon`, `authenticated`, `service_role`)
|
||||
# without explicit GRANTs. When unset, new entities are NOT auto-exposed, matching the new cloud
|
||||
# default. Set to `true` to keep the legacy behaviour of auto-exposing new entities; this is
|
||||
# deprecated and the field is removed on 2026-10-30 once the always-revoked behaviour is permanent.
|
||||
# auto_expose_new_tables = true
|
||||
|
||||
[api.tls]
|
||||
# Enable HTTPS endpoints locally using a self-signed certificate.
|
||||
enabled = false
|
||||
# Paths to self-signed certificate pair.
|
||||
# cert_path = "../certs/my-cert.pem"
|
||||
# key_path = "../certs/my-key.pem"
|
||||
|
||||
[db]
|
||||
# Port to use for the local database URL.
|
||||
port = 54322
|
||||
# Port used by db diff command to initialize the shadow database.
|
||||
shadow_port = 54320
|
||||
# Maximum amount of time to wait for health check when starting the local database.
|
||||
health_timeout = "2m"
|
||||
# The database major version to use. This has to be the same as your remote database's. Run `SHOW
|
||||
# server_version;` on the remote database to check.
|
||||
major_version = 17
|
||||
|
||||
[db.pooler]
|
||||
enabled = false
|
||||
# Port to use for the local connection pooler.
|
||||
port = 54329
|
||||
# Specifies when a server connection can be reused by other clients.
|
||||
# Configure one of the supported pooler modes: `transaction`, `session`.
|
||||
pool_mode = "transaction"
|
||||
# How many server connections to allow per user/database pair.
|
||||
default_pool_size = 20
|
||||
# Maximum number of client connections allowed.
|
||||
max_client_conn = 100
|
||||
|
||||
# [db.vault]
|
||||
# secret_key = "env(SECRET_VALUE)"
|
||||
|
||||
[db.migrations]
|
||||
# If disabled, migrations will be skipped during a db push or reset.
|
||||
enabled = true
|
||||
# Specifies an ordered list of schema files, directories, or glob patterns that describe your database.
|
||||
# Supports paths relative to supabase directory: "./schemas/*.sql", "./database".
|
||||
schema_paths = []
|
||||
|
||||
[db.seed]
|
||||
# If enabled, seeds the database after migrations during a db reset.
|
||||
enabled = true
|
||||
# Specifies an ordered list of seed files to load during db reset.
|
||||
# Supports glob patterns relative to supabase directory: "./seeds/*.sql"
|
||||
sql_paths = ["./seed.sql"]
|
||||
|
||||
[db.network_restrictions]
|
||||
# Enable management of network restrictions.
|
||||
enabled = false
|
||||
# List of IPv4 CIDR blocks allowed to connect to the database.
|
||||
# Defaults to allow all IPv4 connections. Set empty array to block all IPs.
|
||||
allowed_cidrs = ["0.0.0.0/0"]
|
||||
# List of IPv6 CIDR blocks allowed to connect to the database.
|
||||
# Defaults to allow all IPv6 connections. Set empty array to block all IPs.
|
||||
allowed_cidrs_v6 = ["::/0"]
|
||||
|
||||
# Uncomment to reject non-secure connections to the database.
|
||||
# [db.ssl_enforcement]
|
||||
# enabled = true
|
||||
|
||||
[realtime]
|
||||
enabled = true
|
||||
# Bind realtime via either IPv4 or IPv6. (default: IPv4)
|
||||
# ip_version = "IPv6"
|
||||
# The maximum length in bytes of HTTP request headers. (default: 4096)
|
||||
# max_header_length = 4096
|
||||
|
||||
[studio]
|
||||
enabled = true
|
||||
# Port to use for Supabase Studio.
|
||||
port = 54323
|
||||
# External URL of the API server that frontend connects to.
|
||||
api_url = "http://127.0.0.1"
|
||||
# OpenAI API Key to use for Supabase AI in the Supabase Studio.
|
||||
openai_api_key = "env(OPENAI_API_KEY)"
|
||||
|
||||
# Email testing server. Emails sent with the local dev setup are not actually sent - rather, they
|
||||
# are monitored, and you can view the emails that would have been sent from the web interface.
|
||||
[local_smtp]
|
||||
enabled = true
|
||||
# Port to use for the email testing server web interface.
|
||||
port = 54324
|
||||
# Uncomment to expose additional ports for testing user applications that send emails.
|
||||
# smtp_port = 54325
|
||||
# pop3_port = 54326
|
||||
# admin_email = "admin@email.com"
|
||||
# sender_name = "Admin"
|
||||
|
||||
[storage]
|
||||
enabled = true
|
||||
# The maximum file size allowed (e.g. "5MB", "500KB").
|
||||
file_size_limit = "50MiB"
|
||||
|
||||
# Uncomment to configure local storage buckets
|
||||
# [storage.buckets.images]
|
||||
# public = false
|
||||
# file_size_limit = "50MiB"
|
||||
# allowed_mime_types = ["image/png", "image/jpeg"]
|
||||
# objects_path = "./images"
|
||||
|
||||
# Allow connections via S3 compatible clients
|
||||
[storage.s3_protocol]
|
||||
enabled = true
|
||||
|
||||
# Image transformation API is available to Supabase Pro plan.
|
||||
# [storage.image_transformation]
|
||||
# enabled = true
|
||||
|
||||
# Store analytical data in S3 for running ETL jobs over Iceberg Catalog
|
||||
# This feature is only available on the hosted platform.
|
||||
[storage.analytics]
|
||||
enabled = false
|
||||
max_namespaces = 5
|
||||
max_tables = 10
|
||||
max_catalogs = 2
|
||||
|
||||
# Analytics Buckets is available to Supabase Pro plan.
|
||||
# [storage.analytics.buckets.my-warehouse]
|
||||
|
||||
# Store vector embeddings in S3 for large and durable datasets
|
||||
[storage.vector]
|
||||
enabled = true
|
||||
max_buckets = 10
|
||||
max_indexes = 5
|
||||
|
||||
# Vector Buckets is available to Supabase Pro plan.
|
||||
# [storage.vector.buckets.documents-openai]
|
||||
|
||||
[auth]
|
||||
enabled = true
|
||||
# The base URL of your website. Used as an allow-list for redirects and for constructing URLs used
|
||||
# in emails.
|
||||
site_url = "http://127.0.0.1:3000"
|
||||
# The public URL that Auth serves on. Defaults to the API external URL with `/auth/v1` appended.
|
||||
# external_url = ""
|
||||
# A list of *exact* URLs that auth providers are permitted to redirect to post authentication.
|
||||
additional_redirect_urls = ["https://127.0.0.1:3000"]
|
||||
# How long tokens are valid for, in seconds. Defaults to 3600 (1 hour), maximum 604,800 (1 week).
|
||||
jwt_expiry = 3600
|
||||
# JWT issuer URL. If not set, defaults to auth.external_url.
|
||||
# jwt_issuer = ""
|
||||
# Path to JWT signing key. DO NOT commit your signing keys file to git.
|
||||
# signing_keys_path = "./signing_keys.json"
|
||||
# If disabled, the refresh token will never expire.
|
||||
enable_refresh_token_rotation = true
|
||||
# Allows refresh tokens to be reused after expiry, up to the specified interval in seconds.
|
||||
# Requires enable_refresh_token_rotation = true.
|
||||
refresh_token_reuse_interval = 10
|
||||
# Allow/disallow new user signups to your project.
|
||||
enable_signup = true
|
||||
# Allow/disallow anonymous sign-ins to your project.
|
||||
enable_anonymous_sign_ins = false
|
||||
# Allow/disallow testing manual linking of accounts
|
||||
enable_manual_linking = false
|
||||
# Passwords shorter than this value will be rejected as weak. Minimum 6, recommended 8 or more.
|
||||
minimum_password_length = 6
|
||||
# Passwords that do not meet the following requirements will be rejected as weak. Supported values
|
||||
# are: `letters_digits`, `lower_upper_letters_digits`, `lower_upper_letters_digits_symbols`
|
||||
password_requirements = ""
|
||||
|
||||
# Configure passkey sign-ins.
|
||||
# [auth.passkey]
|
||||
# enabled = false
|
||||
|
||||
# Configure WebAuthn relying party settings (required when passkey is enabled).
|
||||
# [auth.webauthn]
|
||||
# rp_display_name = "Supabase"
|
||||
# rp_id = "localhost"
|
||||
# rp_origins = ["http://127.0.0.1:3000"]
|
||||
|
||||
[auth.rate_limit]
|
||||
# Number of emails that can be sent per hour. Requires auth.email.smtp to be enabled.
|
||||
email_sent = 2
|
||||
# Number of SMS messages that can be sent per hour. Requires auth.sms to be enabled.
|
||||
sms_sent = 30
|
||||
# Number of anonymous sign-ins that can be made per hour per IP address. Requires enable_anonymous_sign_ins = true.
|
||||
anonymous_users = 30
|
||||
# Number of sessions that can be refreshed in a 5 minute interval per IP address.
|
||||
token_refresh = 150
|
||||
# Number of sign up and sign-in requests that can be made in a 5 minute interval per IP address (excludes anonymous users).
|
||||
sign_in_sign_ups = 30
|
||||
# Number of OTP / Magic link verifications that can be made in a 5 minute interval per IP address.
|
||||
token_verifications = 30
|
||||
# Number of Web3 logins that can be made in a 5 minute interval per IP address.
|
||||
web3 = 30
|
||||
|
||||
# Configure one of the supported captcha providers: `hcaptcha`, `turnstile`.
|
||||
# [auth.captcha]
|
||||
# enabled = true
|
||||
# provider = "hcaptcha"
|
||||
# secret = ""
|
||||
|
||||
[auth.email]
|
||||
# Allow/disallow new user signups via email to your project.
|
||||
enable_signup = true
|
||||
# If enabled, a user will be required to confirm any email change on both the old, and new email
|
||||
# addresses. If disabled, only the new email is required to confirm.
|
||||
double_confirm_changes = true
|
||||
# If enabled, users need to confirm their email address before signing in.
|
||||
enable_confirmations = false
|
||||
# If enabled, users will need to reauthenticate or have logged in recently to change their password.
|
||||
secure_password_change = false
|
||||
# Controls the minimum amount of time that must pass before sending another signup confirmation or password reset email.
|
||||
max_frequency = "1s"
|
||||
# Number of characters used in the email OTP.
|
||||
otp_length = 6
|
||||
# Number of seconds before the email OTP expires (defaults to 1 hour).
|
||||
otp_expiry = 3600
|
||||
|
||||
# Use a production-ready SMTP server
|
||||
# [auth.email.smtp]
|
||||
# enabled = true
|
||||
# host = "smtp.sendgrid.net"
|
||||
# port = 587
|
||||
# user = "apikey"
|
||||
# pass = "env(SENDGRID_API_KEY)"
|
||||
# admin_email = "admin@email.com"
|
||||
# sender_name = "Admin"
|
||||
|
||||
# Uncomment to customize email template
|
||||
# [auth.email.template.invite]
|
||||
# subject = "You have been invited"
|
||||
# content_path = "./supabase/templates/invite.html"
|
||||
|
||||
# Uncomment to customize notification email template
|
||||
# [auth.email.notification.password_changed]
|
||||
# enabled = true
|
||||
# subject = "Your password has been changed"
|
||||
# content_path = "./templates/password_changed_notification.html"
|
||||
|
||||
[auth.sms]
|
||||
# Allow/disallow new user signups via SMS to your project.
|
||||
enable_signup = false
|
||||
# If enabled, users need to confirm their phone number before signing in.
|
||||
enable_confirmations = false
|
||||
# Template for sending OTP to users
|
||||
template = "Your code is {{ `{{ .Code }}` }}"
|
||||
# Controls the minimum amount of time that must pass before sending another sms otp.
|
||||
max_frequency = "5s"
|
||||
|
||||
# Use pre-defined map of phone number to OTP for testing.
|
||||
# [auth.sms.test_otp]
|
||||
# 4152127777 = "123456"
|
||||
|
||||
# Configure logged in session timeouts.
|
||||
# [auth.sessions]
|
||||
# Force log out after the specified duration.
|
||||
# timebox = "24h"
|
||||
# Force log out if the user has been inactive longer than the specified duration.
|
||||
# inactivity_timeout = "8h"
|
||||
|
||||
# This hook runs before a new user is created and allows developers to reject the request based on the incoming user object.
|
||||
# [auth.hook.before_user_created]
|
||||
# enabled = true
|
||||
# uri = "pg-functions://postgres/auth/before-user-created-hook"
|
||||
|
||||
# This hook runs before a token is issued and allows you to add additional claims based on the authentication method used.
|
||||
# [auth.hook.custom_access_token]
|
||||
# enabled = true
|
||||
# uri = "pg-functions://<database>/<schema>/<hook_name>"
|
||||
|
||||
# Configure one of the supported SMS providers: `twilio`, `twilio_verify`, `messagebird`, `textlocal`, `vonage`.
|
||||
[auth.sms.twilio]
|
||||
enabled = false
|
||||
account_sid = ""
|
||||
message_service_sid = ""
|
||||
# DO NOT commit your Twilio auth token to git. Use environment variable substitution instead:
|
||||
auth_token = "env(SUPABASE_AUTH_SMS_TWILIO_AUTH_TOKEN)"
|
||||
|
||||
# Multi-factor-authentication is available to Supabase Pro plan.
|
||||
[auth.mfa]
|
||||
# Control how many MFA factors can be enrolled at once per user.
|
||||
max_enrolled_factors = 10
|
||||
|
||||
# Control MFA via App Authenticator (TOTP)
|
||||
[auth.mfa.totp]
|
||||
enroll_enabled = false
|
||||
verify_enabled = false
|
||||
|
||||
# Configure MFA via Phone Messaging
|
||||
[auth.mfa.phone]
|
||||
enroll_enabled = false
|
||||
verify_enabled = false
|
||||
otp_length = 6
|
||||
template = "Your code is {{ `{{ .Code }}` }}"
|
||||
max_frequency = "5s"
|
||||
|
||||
# Configure MFA via WebAuthn
|
||||
# [auth.mfa.web_authn]
|
||||
# enroll_enabled = true
|
||||
# verify_enabled = true
|
||||
|
||||
# Use an external OAuth provider. The full list of providers are: `apple`, `azure`, `bitbucket`,
|
||||
# `discord`, `facebook`, `github`, `gitlab`, `google`, `keycloak`, `linkedin_oidc`, `notion`, `twitch`,
|
||||
# `twitter`, `x`, `slack`, `spotify`, `workos`, `zoom`.
|
||||
[auth.external.apple]
|
||||
enabled = false
|
||||
client_id = ""
|
||||
# DO NOT commit your OAuth provider secret to git. Use environment variable substitution instead:
|
||||
secret = "env(SUPABASE_AUTH_EXTERNAL_APPLE_SECRET)"
|
||||
# Overrides the default auth callback URL derived from auth.external_url.
|
||||
redirect_uri = ""
|
||||
# Overrides the default auth provider URL. Used to support self-hosted gitlab, single-tenant Azure,
|
||||
# or any other third-party OIDC providers.
|
||||
url = ""
|
||||
# If enabled, the nonce check will be skipped. Required for local sign in with Google auth.
|
||||
skip_nonce_check = false
|
||||
# If enabled, it will allow the user to successfully authenticate when the provider does not return an email address.
|
||||
email_optional = false
|
||||
|
||||
# Allow Solana wallet holders to sign in to your project via the Sign in with Solana (SIWS, EIP-4361) standard.
|
||||
# You can configure "web3" rate limit in the [auth.rate_limit] section and set up [auth.captcha] if self-hosting.
|
||||
[auth.web3.solana]
|
||||
enabled = false
|
||||
|
||||
# Use Firebase Auth as a third-party provider alongside Supabase Auth.
|
||||
[auth.third_party.firebase]
|
||||
enabled = false
|
||||
# project_id = "my-firebase-project"
|
||||
|
||||
# Use Auth0 as a third-party provider alongside Supabase Auth.
|
||||
[auth.third_party.auth0]
|
||||
enabled = false
|
||||
# tenant = "my-auth0-tenant"
|
||||
# tenant_region = "us"
|
||||
|
||||
# Use AWS Cognito (Amplify) as a third-party provider alongside Supabase Auth.
|
||||
[auth.third_party.aws_cognito]
|
||||
enabled = false
|
||||
# user_pool_id = "my-user-pool-id"
|
||||
# user_pool_region = "us-east-1"
|
||||
|
||||
# Use Clerk as a third-party provider alongside Supabase Auth.
|
||||
[auth.third_party.clerk]
|
||||
enabled = false
|
||||
# Obtain from https://clerk.com/setup/supabase
|
||||
# domain = "example.clerk.accounts.dev"
|
||||
|
||||
# OAuth server configuration
|
||||
[auth.oauth_server]
|
||||
# Enable OAuth server functionality
|
||||
enabled = false
|
||||
# Path for OAuth consent flow UI
|
||||
authorization_url_path = "/oauth/consent"
|
||||
# Allow dynamic client registration
|
||||
allow_dynamic_registration = false
|
||||
|
||||
[edge_runtime]
|
||||
enabled = true
|
||||
# Supported request policies: `oneshot`, `per_worker`.
|
||||
# `per_worker` (default) — enables hot reload during local development.
|
||||
# `oneshot` — fallback mode if hot reload causes issues (e.g. in large repos or with symlinks).
|
||||
policy = "per_worker"
|
||||
# Port to attach the Chrome inspector for debugging edge functions.
|
||||
inspector_port = 8083
|
||||
# The Deno major version to use.
|
||||
deno_version = 2
|
||||
|
||||
# [edge_runtime.secrets]
|
||||
# secret_key = "env(SECRET_VALUE)"
|
||||
|
||||
[analytics]
|
||||
enabled = true
|
||||
port = 54327
|
||||
# Configure one of the supported backends: `postgres`, `bigquery`.
|
||||
backend = "postgres"
|
||||
|
||||
# Experimental features may be deprecated any time
|
||||
[experimental]
|
||||
# Configures Postgres storage engine to use OrioleDB (S3)
|
||||
orioledb_version = ""
|
||||
# Configures S3 bucket URL, eg. <bucket_name>.s3-<region>.amazonaws.com
|
||||
s3_host = "env(S3_HOST)"
|
||||
# Configures S3 bucket region, eg. us-east-1
|
||||
s3_region = "env(S3_REGION)"
|
||||
# Configures AWS_ACCESS_KEY_ID for S3 bucket
|
||||
s3_access_key = "env(S3_ACCESS_KEY)"
|
||||
# Configures AWS_SECRET_ACCESS_KEY for S3 bucket
|
||||
s3_secret_key = "env(S3_SECRET_KEY)"
|
||||
|
||||
# pg-delta is the schema diff engine for db diff / db pull / db remote commit.
|
||||
# Set enabled = false to fall back to the legacy migra engine.
|
||||
[experimental.pgdelta]
|
||||
enabled = true
|
||||
# Directory under `supabase/` where declarative files are written.
|
||||
# declarative_schema_path = "./database"
|
||||
# JSON string passed through to pg-delta SQL formatting.
|
||||
# format_options = "{\"keywordCase\":\"upper\",\"indent\":2,\"maxWidth\":80,\"commaStyle\":\"trailing\"}"
|
||||
@@ -0,0 +1,265 @@
|
||||
-- 20260717000000_schema.sql — Consolidated schema for x0gp catalog, races, and drivers.
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Tracks & Waypoints
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
CREATE TABLE IF NOT EXISTS tracks (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT NOT NULL DEFAULT '',
|
||||
author_id TEXT NOT NULL DEFAULT 'system',
|
||||
visibility TEXT NOT NULL DEFAULT 'system' CHECK (visibility IN ('system', 'public', 'private')),
|
||||
scale INT NOT NULL DEFAULT 27,
|
||||
length_m DOUBLE PRECISION NOT NULL DEFAULT 0,
|
||||
width_m DOUBLE PRECISION NOT NULL DEFAULT 0,
|
||||
lane_width_m DOUBLE PRECISION NOT NULL DEFAULT 0.20,
|
||||
surface TEXT NOT NULL DEFAULT 'carpet' CHECK (surface IN ('carpet', 'tile', 'wood', 'paper', 'mixed')),
|
||||
best_lap_ms BIGINT NOT NULL DEFAULT 0,
|
||||
best_lap_holder TEXT NOT NULL DEFAULT '',
|
||||
bounds_min_x DOUBLE PRECISION NOT NULL DEFAULT 0,
|
||||
bounds_min_y DOUBLE PRECISION NOT NULL DEFAULT 0,
|
||||
bounds_max_x DOUBLE PRECISION NOT NULL DEFAULT 0,
|
||||
bounds_max_y DOUBLE PRECISION NOT NULL DEFAULT 0,
|
||||
created_ms BIGINT NOT NULL,
|
||||
updated_ms BIGINT NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS tracks_visibility_idx ON tracks (visibility);
|
||||
CREATE INDEX IF NOT EXISTS tracks_author_idx ON tracks (author_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS track_waypoints (
|
||||
track_id TEXT NOT NULL REFERENCES tracks(id) ON DELETE CASCADE,
|
||||
seq INT NOT NULL,
|
||||
x DOUBLE PRECISION NOT NULL,
|
||||
y DOUBLE PRECISION NOT NULL,
|
||||
heading_rad DOUBLE PRECISION NOT NULL,
|
||||
speed_ms DOUBLE PRECISION NOT NULL,
|
||||
curvature DOUBLE PRECISION NOT NULL,
|
||||
PRIMARY KEY (track_id, seq)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS track_tags (
|
||||
track_id TEXT NOT NULL REFERENCES tracks(id) ON DELETE CASCADE,
|
||||
tag TEXT NOT NULL,
|
||||
PRIMARY KEY (track_id, tag)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS track_tags_tag_idx ON track_tags (tag);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Cars
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
CREATE TABLE IF NOT EXISTS cars (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
owner_id TEXT NOT NULL DEFAULT 'system',
|
||||
visibility TEXT NOT NULL DEFAULT 'system' CHECK (visibility IN ('system', 'public', 'private')),
|
||||
scale INT NOT NULL DEFAULT 27,
|
||||
|
||||
length_mm DOUBLE PRECISION NOT NULL,
|
||||
width_mm DOUBLE PRECISION NOT NULL,
|
||||
height_mm DOUBLE PRECISION NOT NULL DEFAULT 0,
|
||||
weight_g DOUBLE PRECISION NOT NULL DEFAULT 0,
|
||||
|
||||
chassis_model TEXT NOT NULL DEFAULT '',
|
||||
chassis_material TEXT NOT NULL DEFAULT '',
|
||||
chassis_printed BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
chassis_wheelbase_mm DOUBLE PRECISION NOT NULL DEFAULT 0,
|
||||
chassis_track_mm DOUBLE PRECISION NOT NULL DEFAULT 0,
|
||||
|
||||
motor_kind TEXT NOT NULL DEFAULT 'brushed',
|
||||
motor_class TEXT NOT NULL DEFAULT '',
|
||||
motor_kv INT NOT NULL DEFAULT 0,
|
||||
motor_power_w DOUBLE PRECISION NOT NULL DEFAULT 0,
|
||||
|
||||
battery_voltage_v DOUBLE PRECISION NOT NULL DEFAULT 0,
|
||||
battery_capacity_mah INT NOT NULL DEFAULT 0,
|
||||
battery_cells INT NOT NULL DEFAULT 0,
|
||||
battery_chemistry TEXT NOT NULL DEFAULT 'li-po',
|
||||
|
||||
drive TEXT NOT NULL DEFAULT '2WD' CHECK (drive IN ('2WD', '4WD')),
|
||||
|
||||
top_speed_ms DOUBLE PRECISION NOT NULL DEFAULT 0,
|
||||
color_hex TEXT NOT NULL DEFAULT '#ff6b1a',
|
||||
avatar_url TEXT NOT NULL DEFAULT '',
|
||||
active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
|
||||
total_distance_m DOUBLE PRECISION NOT NULL DEFAULT 0,
|
||||
total_races INT NOT NULL DEFAULT 0,
|
||||
total_laps INT NOT NULL DEFAULT 0,
|
||||
best_lap_ms BIGINT NOT NULL DEFAULT 0,
|
||||
|
||||
created_ms BIGINT NOT NULL,
|
||||
updated_ms BIGINT NOT NULL,
|
||||
device_id INT
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS cars_visibility_idx ON cars (visibility);
|
||||
CREATE INDEX IF NOT EXISTS cars_owner_idx ON cars (owner_id);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Clans & Drivers
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
CREATE TABLE IF NOT EXISTS clans (
|
||||
id TEXT PRIMARY KEY,
|
||||
tag TEXT NOT NULL UNIQUE,
|
||||
name TEXT NOT NULL,
|
||||
avatar_url TEXT NOT NULL DEFAULT '',
|
||||
created_ms BIGINT NOT NULL,
|
||||
updated_ms BIGINT NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS clans_tag_idx ON clans (tag);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS drivers (
|
||||
id TEXT PRIMARY KEY,
|
||||
nickname TEXT NOT NULL UNIQUE,
|
||||
name TEXT NOT NULL,
|
||||
avatar_url TEXT NOT NULL DEFAULT '',
|
||||
clan_id TEXT REFERENCES clans(id) ON DELETE SET NULL,
|
||||
created_ms BIGINT NOT NULL,
|
||||
updated_ms BIGINT NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS drivers_nickname_idx ON drivers (nickname);
|
||||
CREATE INDEX IF NOT EXISTS drivers_clan_idx ON drivers (clan_id);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Races & Race Drivers
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
CREATE TABLE IF NOT EXISTS races (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
track_id TEXT NOT NULL DEFAULT '',
|
||||
max_cars INT NOT NULL,
|
||||
laps INT NOT NULL,
|
||||
time_limit_s INT NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'lobby' CHECK (status IN ('lobby', 'countdown', 'racing', 'finished', 'cancelled')),
|
||||
created_ms BIGINT NOT NULL,
|
||||
started_ms BIGINT NOT NULL DEFAULT 0,
|
||||
finished_ms BIGINT NOT NULL DEFAULT 0,
|
||||
duration_ms BIGINT NOT NULL DEFAULT 0,
|
||||
total_laps INT NOT NULL DEFAULT 0,
|
||||
total_drivers INT NOT NULL DEFAULT 0,
|
||||
updated_ms BIGINT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS races_status_finished_idx ON races (status, finished_ms DESC, id DESC);
|
||||
CREATE INDEX IF NOT EXISTS races_started_idx ON races (status, started_ms DESC, id DESC);
|
||||
CREATE INDEX IF NOT EXISTS races_track_idx ON races (track_id);
|
||||
CREATE INDEX IF NOT EXISTS races_status_idx ON races (status);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS race_drivers (
|
||||
race_id TEXT NOT NULL REFERENCES races(id) ON DELETE CASCADE,
|
||||
driver_id TEXT NOT NULL,
|
||||
slot INT NOT NULL DEFAULT 0,
|
||||
joined_ms BIGINT NOT NULL,
|
||||
total_time_ms BIGINT NOT NULL DEFAULT 0,
|
||||
best_lap_ms BIGINT NOT NULL DEFAULT 0,
|
||||
position INT,
|
||||
PRIMARY KEY (race_id, driver_id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS race_drivers_driver_idx ON race_drivers (driver_id);
|
||||
CREATE INDEX IF NOT EXISTS race_drivers_podium_idx ON race_drivers (race_id, total_time_ms ASC, position ASC) WHERE total_time_ms > 0;
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Race Plans & Queue
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
CREATE TABLE IF NOT EXISTS race_plans (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
track_id TEXT NOT NULL DEFAULT 'default',
|
||||
max_cars INT NOT NULL,
|
||||
laps INT NOT NULL DEFAULT 0,
|
||||
time_limit_s INT NOT NULL DEFAULT 0,
|
||||
start_at_ms BIGINT NOT NULL,
|
||||
interval_s INT NOT NULL DEFAULT 0, -- 0 = single-shot
|
||||
count INT NOT NULL DEFAULT 0, -- 0 = repeat forever
|
||||
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
created_ms BIGINT NOT NULL,
|
||||
updated_ms BIGINT NOT NULL,
|
||||
next_fire_ms BIGINT NOT NULL, -- last materialised fire time
|
||||
fires_done INT NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS race_plans_start_idx ON race_plans (start_at_ms ASC, id ASC);
|
||||
CREATE INDEX IF NOT EXISTS race_plans_enabled_idx ON race_plans (enabled, next_fire_ms);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS race_queue (
|
||||
driver_id TEXT NOT NULL,
|
||||
race_id TEXT NOT NULL,
|
||||
plan_id TEXT NOT NULL DEFAULT '',
|
||||
enqueued_ms BIGINT NOT NULL,
|
||||
PRIMARY KEY (driver_id, race_id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS race_queue_race_idx ON race_queue (race_id);
|
||||
CREATE INDEX IF NOT EXISTS race_queue_driver_idx ON race_queue (driver_id, enqueued_ms);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Lobby Drivers
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
CREATE TABLE IF NOT EXISTS lobby_drivers (
|
||||
driver_id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL DEFAULT '',
|
||||
nickname TEXT NOT NULL DEFAULT '',
|
||||
avatar_url TEXT NOT NULL DEFAULT '',
|
||||
clan_id TEXT NOT NULL DEFAULT '',
|
||||
clan_tag TEXT NOT NULL DEFAULT '',
|
||||
status TEXT NOT NULL DEFAULT 'idle' CHECK (status IN ('idle', 'racing', 'offline')),
|
||||
current_race_id TEXT NOT NULL DEFAULT '',
|
||||
connected_ms BIGINT NOT NULL,
|
||||
last_seen_ms BIGINT NOT NULL,
|
||||
car_id TEXT
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS lobby_drivers_status_idx ON lobby_drivers (status);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Track Calendar
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
CREATE TABLE IF NOT EXISTS track_calendar (
|
||||
id SERIAL PRIMARY KEY,
|
||||
track_id TEXT NOT NULL REFERENCES tracks(id) ON DELETE CASCADE,
|
||||
start_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
end_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
CHECK (start_at < end_at)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS track_calendar_dates_idx ON track_calendar (start_at, end_at);
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Go Migration Metadata (Bridges Go server startup migrations and Supabase)
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
CREATE TABLE IF NOT EXISTS schema_migrations (
|
||||
version TEXT PRIMARY KEY,
|
||||
applied_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
INSERT INTO schema_migrations (version) VALUES
|
||||
('001_init.sql'),
|
||||
('002_seed.sql'),
|
||||
('003_f1_seed.sql'),
|
||||
('004_monaco_rescale.sql'),
|
||||
('005_races.sql'),
|
||||
('006_drop_host.sql'),
|
||||
('007_podium.sql'),
|
||||
('008_drivers_clans.sql'),
|
||||
('009_live_persistence.sql'),
|
||||
('010_unify_races.sql'),
|
||||
('011_drop_driver_ids.sql'),
|
||||
('012_normalize_race_results.sql'),
|
||||
('013_driver_device.sql'),
|
||||
('014_track_calendar.sql'),
|
||||
('015_driver_car_id.sql')
|
||||
ON CONFLICT (version) DO NOTHING;
|
||||
|
||||
@@ -0,0 +1,345 @@
|
||||
-- seed.sql — Initial system catalog (tracks + cars) for Supabase.
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Monaco (Rescaled)
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
INSERT INTO tracks (id, name, description, author_id, visibility, scale,
|
||||
length_m, width_m, lane_width_m, surface, best_lap_ms, best_lap_holder,
|
||||
bounds_min_x, bounds_min_y, bounds_max_x, bounds_max_y,
|
||||
created_ms, updated_ms)
|
||||
VALUES ('monaco', 'Monaco', 'Circuit de Monaco. Narrow street circuit, tight hairpins, no room for error.', 'system', 'system', 27,
|
||||
21.87, 6.46, 0.18, 'tile', 0, 'Verstappen',
|
||||
-3.231, -2.684, 3.231, 2.684,
|
||||
1700000000000, 1700000000000)
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 0, -3.2064, 1.0454, -1.4710, 0.632, -0.1988) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 1, -3.1387, 0.5857, -1.3926, 0.632, -0.1787) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 2, -3.0418, 0.1312, -1.3043, 0.632, -0.2544) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 3, -2.8946, -0.3084, -1.1557, 0.632, -0.4431) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 4, -2.6693, -0.7141, -0.8629, 0.632, -1.0875) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 5, -2.3517, -0.9426, -0.1919, 0.632, -1.2844) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 6, -1.8935, -0.8648, 0.1182, 0.632, -0.2237) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 7, -1.4302, -0.8332, 0.0660, 0.632, 0.0475) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 8, -0.9667, -0.8035, 0.0743, 0.632, 0.0756) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 9, -0.5052, -0.7644, -0.0045, 0.632, 0.0806) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 10, -0.0425, -0.8077, -0.0000, 0.632, -0.0425) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 11, 0.4197, -0.7644, 0.0351, 0.632, 0.3650) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 12, 0.8809, -0.7753, -0.3862, 0.632, 1.3887) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 13, 1.1985, -1.0811, -1.2440, 0.632, 1.4550) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 14, 1.1372, -1.5315, -1.5878, 0.632, -0.2144) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 15, 1.1839, -1.9468, -1.0284, 0.632, -1.2681) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 16, 1.5551, -2.2249, -0.6162, 0.632, -0.4763) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 17, 1.9414, -2.4832, -0.5226, 0.632, -0.1756) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 18, 2.3526, -2.6842, -0.4546, 0.632, -0.1494) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 19, 2.3526, -2.6842, 1.2476, 0.632, -0.0225) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 20, 2.4622, -2.3570, 1.2545, 0.632, 0.1775) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 21, 2.5976, -1.9356, 0.5751, 0.632, 34.1925) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 22, 2.6154, -2.2577, -1.0415, 0.632, -0.8400) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 23, 2.9239, -2.4933, 0.0053, 0.632, -3.1975) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 24, 3.2311, -2.2544, 1.4159, 0.632, -2.7456) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 25, 3.0259, -1.8399, 2.1127, 0.632, -0.6488) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 26, 2.7546, -1.4629, 2.2528, 0.632, -0.2769) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 27, 2.4413, -1.1199, 2.3708, 0.632, -0.3044) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 28, 2.0895, -0.8170, 2.5300, 0.632, -0.3825) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 29, 1.6852, -0.5897, 2.7255, 0.632, -0.4019) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 30, 1.2447, -0.4437, 2.9031, 0.632, -0.3538) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 31, 0.7862, -0.3711, 3.0547, 0.632, -0.3044) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 32, 0.3218, -0.3632, -3.0973, 0.632, -0.0975) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 33, -0.1396, -0.4121, -3.1387, 0.632, -0.0394) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 34, -0.5818, -0.3659, -3.0594, 0.632, -0.2375) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 35, -1.0290, -0.4854, -2.9319, 0.632, -0.0950) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 36, -1.4880, -0.5588, -2.9664, 0.632, -0.2500) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 37, -1.9441, -0.6474, 3.1052, 0.632, 0.8825) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 38, -2.3631, -0.5269, 2.4971, 0.632, 1.3600) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 39, -2.6181, -0.1409, 1.9547, 0.632, 0.9481) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 40, -2.7014, 0.3108, 1.5706, 0.632, 0.3788) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 41, -2.6179, 0.7633, 1.6025, 0.632, -0.3425) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 42, -2.7299, 1.2092, 1.8788, 0.632, -0.0725) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 43, -2.8930, 1.6280, 1.6659, 0.632, 0.7706) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 44, -2.8134, 2.0840, 1.2045, 0.632, 0.2162) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 45, -2.5685, 2.4739, 1.5682, 0.632, -2.4475) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 46, -2.8118, 2.6842, -3.0638, 0.632, -4.0544) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 47, -3.1281, 2.4303, -2.0706, 0.632, -1.2500) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 48, -3.2004, 1.9728, -1.6822, 0.632, -0.4706) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 49, -3.2311, 1.5093, -1.5774, 0.632, -0.2275) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('monaco', 50, -3.2064, 1.0454, -1.4710, 0.632, -0.1988) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
|
||||
INSERT INTO track_tags (track_id, tag) VALUES ('monaco', 'f1') ON CONFLICT DO NOTHING;
|
||||
INSERT INTO track_tags (track_id, tag) VALUES ('monaco', 'street') ON CONFLICT DO NOTHING;
|
||||
INSERT INTO track_tags (track_id, tag) VALUES ('monaco', 'tight') ON CONFLICT DO NOTHING;
|
||||
INSERT INTO track_tags (track_id, tag) VALUES ('monaco', 'slow') ON CONFLICT DO NOTHING;
|
||||
INSERT INTO track_tags (track_id, tag) VALUES ('monaco', 'monaco') ON CONFLICT DO NOTHING;
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Barcelona-Catalunya
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
INSERT INTO tracks (id, name, description, author_id, visibility, scale,
|
||||
length_m, width_m, lane_width_m, surface, best_lap_ms, best_lap_holder,
|
||||
bounds_min_x, bounds_min_y, bounds_max_x, bounds_max_y,
|
||||
created_ms, updated_ms)
|
||||
VALUES ('barcelona-catalunya', 'Barcelona-Catalunya', 'Circuit de Barcelona-Catalunya. Mix of high-speed corners and technical chicanes.', 'system', 'system', 27,
|
||||
3.75, 2.30, 0.18, 'tile', 0, 'Verstappen',
|
||||
0.3, -2.7, 4.05, -0.4,
|
||||
1700000000000, 1700000000000)
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('barcelona-catalunya', 0, 0.3, -0.4, -0, 4.5, 0) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('barcelona-catalunya', 1, 1.4, -0.4, -0, 4.5, 0) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('barcelona-catalunya', 2, 1.7, -0.65, -1.5707963267948966, 2.2, 1.6) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('barcelona-catalunya', 3, 1.5, -0.95, -3.141592653589793, 2, 2) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('barcelona-catalunya', 4, 1.2, -1.1, -3.4415926535897934, 2.6, 1) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('barcelona-catalunya', 5, 0.95, -1, -4.0415926535897935, 2.6, 1) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('barcelona-catalunya', 6, 0.8, -1.2, 1.5707963267948966, 2.4, 1.2) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('barcelona-catalunya', 7, 1.05, -1.5, -0, 3.2, 0.7) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('barcelona-catalunya', 8, 1.3, -1.65, -1.5707963267948966, 2.4, 1) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('barcelona-catalunya', 9, 1.15, -1.85, -3.141592653589793, 2.4, 1) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('barcelona-catalunya', 10, 1.4, -2, -2.641592653589793, 3, 0.7) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('barcelona-catalunya', 11, 1.7, -2, -2.141592653589793, 3, 0.7) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('barcelona-catalunya', 12, 1.95, -2.2, -1.5707963267948966, 2.8, 0.5) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('barcelona-catalunya', 13, 1.65, -2.5, -0.3, 2.8, 0.5) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('barcelona-catalunya', 14, 2.2, -2.7, -0.7, 3.2, 0.5) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('barcelona-catalunya', 15, 2.8, -2.7, -1.1, 3.4, 0.5) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('barcelona-catalunya', 16, 3.1, -2.5, -1.9707963267948967, 2.2, 1.4) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('barcelona-catalunya', 17, 2.95, -2.2, -3.541592653589793, 2.2, 1.4) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('barcelona-catalunya', 18, 3.3, -1.9, -0, 4, 0) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('barcelona-catalunya', 19, 4, -1.9, -0, 4, 0) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('barcelona-catalunya', 20, 4.05, -1.6, 1.5707963267948966, 2.4, 1.4) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('barcelona-catalunya', 21, 3.8, -1.4, -3.141592653589793, 2.4, 1.4) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('barcelona-catalunya', 22, 3.5, -1.5, -3.9269908169872414, 2.6, 1) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('barcelona-catalunya', 23, 3.1, -1.25, 1.9707963267948967, 3, 0.7) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('barcelona-catalunya', 24, 2.7, -1, 2.4707963267948965, 3, 0.7) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('barcelona-catalunya', 25, 2.3, -0.7, -3.541592653589793, 3.4, 0.5) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('barcelona-catalunya', 26, 1.6, -0.5, -3.3415926535897933, 3.6, 0.4) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('barcelona-catalunya', 27, 0.9, -0.42, -0, 4, 0) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
|
||||
INSERT INTO track_tags (track_id, tag) VALUES ('barcelona-catalunya', 'f1') ON CONFLICT DO NOTHING;
|
||||
INSERT INTO track_tags (track_id, tag) VALUES ('barcelona-catalunya', 'balanced') ON CONFLICT DO NOTHING;
|
||||
INSERT INTO track_tags (track_id, tag) VALUES ('barcelona-catalunya', 'testing') ON CONFLICT DO NOTHING;
|
||||
INSERT INTO track_tags (track_id, tag) VALUES ('barcelona-catalunya', 'barcelona') ON CONFLICT DO NOTHING;
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Austria (Red Bull Ring)
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
INSERT INTO tracks (id, name, description, author_id, visibility, scale,
|
||||
length_m, width_m, lane_width_m, surface, best_lap_ms, best_lap_holder,
|
||||
bounds_min_x, bounds_min_y, bounds_max_x, bounds_max_y,
|
||||
created_ms, updated_ms)
|
||||
VALUES ('austria-redbull-ring', 'Austria (Red Bull Ring)', 'Red Bull Ring. Short, fast, three straights separated by hairpins.', 'system', 'system', 27,
|
||||
3.55, 2.0, 0.18, 'wood', 0, 'Verstappen',
|
||||
0.3, -2.4, 3.85, -0.4,
|
||||
1700000000000, 1700000000000)
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('austria-redbull-ring', 0, 0.3, -0.4, -0, 4.8, 0) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('austria-redbull-ring', 1, 1.6, -0.4, -0, 4.8, 0) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('austria-redbull-ring', 2, 1.85, -0.65, -1.5707963267948966, 2, 1.8) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('austria-redbull-ring', 3, 1.6, -0.95, -3.141592653589793, 2, 1.8) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('austria-redbull-ring', 4, 1.3, -1, -3.641592653589793, 3, 0.7) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('austria-redbull-ring', 5, 1.05, -1.2, 1.5707963267948966, 2.6, 1) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('austria-redbull-ring', 6, 1.4, -1.4, -0, 3.2, 0.5) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('austria-redbull-ring', 7, 1.85, -1.4, -1.7707963267948965, 3.2, 0.5) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('austria-redbull-ring', 8, 2.1, -1.65, -3.141592653589793, 3, 0.7) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('austria-redbull-ring', 9, 2, -1.85, 1.5707963267948966, 3, 0.7) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('austria-redbull-ring', 10, 2.25, -2.05, -0, 2, 1.8) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('austria-redbull-ring', 11, 2.4, -2, -1.5707963267948966, 2, 1.8) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('austria-redbull-ring', 12, 2.4, -2.2, -3.141592653589793, 2, 1.8) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('austria-redbull-ring', 13, 2.2, -2.3, 1.5707963267948966, 2, 1.8) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('austria-redbull-ring', 14, 2.55, -2.4, 0.2, 3.4, 0.4) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('austria-redbull-ring', 15, 3, -2.4, -0, 4, 0) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('austria-redbull-ring', 16, 3.5, -2.4, -0, 4.5, 0) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('austria-redbull-ring', 17, 3.85, -2.15, -1.7707963267948965, 3.4, 0.6) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('austria-redbull-ring', 18, 3.6, -1.8, -3.141592653589793, 3, 0.8) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('austria-redbull-ring', 19, 3.25, -1.85, -3.741592653589793, 3, 0.8) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('austria-redbull-ring', 20, 3, -1.6, 1.9707963267948967, 3.2, 0.6) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('austria-redbull-ring', 21, 2.6, -1.4, 3.141592653589793, 3.4, 0.5) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('austria-redbull-ring', 22, 2, -1.2, -2.641592653589793, 3.6, 0.4) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('austria-redbull-ring', 23, 1.4, -0.8, -2.9415926535897934, 3.8, 0.3) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('austria-redbull-ring', 24, 0.8, -0.5, -0, 4.2, 0.2) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
|
||||
INSERT INTO track_tags (track_id, tag) VALUES ('austria-redbull-ring', 'f1') ON CONFLICT DO NOTHING;
|
||||
INSERT INTO track_tags (track_id, tag) VALUES ('austria-redbull-ring', 'short') ON CONFLICT DO NOTHING;
|
||||
INSERT INTO track_tags (track_id, tag) VALUES ('austria-redbull-ring', 'fast') ON CONFLICT DO NOTHING;
|
||||
INSERT INTO track_tags (track_id, tag) VALUES ('austria-redbull-ring', 'austria') ON CONFLICT DO NOTHING;
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Great Britain (Silverstone)
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
INSERT INTO tracks (id, name, description, author_id, visibility, scale,
|
||||
length_m, width_m, lane_width_m, surface, best_lap_ms, best_lap_holder,
|
||||
bounds_min_x, bounds_min_y, bounds_max_x, bounds_max_y,
|
||||
created_ms, updated_ms)
|
||||
VALUES ('great-britain-silverstone', 'Great Britain (Silverstone)', 'Silverstone Circuit. Fast, flowing corners; home of British motorsport.', 'system', 'system', 27,
|
||||
3.55, 2.4, 0.18, 'carpet', 0, 'Hamilton',
|
||||
0.3, -2.65, 3.85, -0.25,
|
||||
1700000000000, 1700000000000)
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('great-britain-silverstone', 0, 0.3, -0.3, -0, 5, 0) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('great-britain-silverstone', 1, 2.4, -0.3, -0, 5, 0) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('great-britain-silverstone', 2, 2.8, -0.45, -1.5707963267948966, 3, 0.8) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('great-britain-silverstone', 3, 2.6, -0.8, -3.141592653589793, 3.4, 0.6) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('great-britain-silverstone', 4, 2.95, -1.05, -2.0707963267948966, 3.2, 0.7) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('great-britain-silverstone', 5, 3.3, -1.3, -0, 2.2, 1.6) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('great-britain-silverstone', 6, 3.6, -1.15, 1.5707963267948966, 2.2, 1.6) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('great-britain-silverstone', 7, 3.6, -0.8, 3.141592653589793, 3, 0.7) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('great-britain-silverstone', 8, 3.85, -0.55, 1.5707963267948966, 2.6, 1) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('great-britain-silverstone', 9, 3.55, -0.25, -0, 2.4, 1) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('great-britain-silverstone', 10, 3.25, -0.25, -1.5707963267948966, 2.4, 1) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('great-britain-silverstone', 11, 3.05, -0.55, -1.8707963267948966, 3.4, 0.5) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('great-britain-silverstone', 12, 2.85, -0.45, -3.141592653589793, 4, 0.3) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('great-britain-silverstone', 13, 1.8, -0.3, -0, 4.6, 0) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('great-britain-silverstone', 14, 1.2, -0.45, 1.7707963267948965, 3.8, 0.4) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('great-britain-silverstone', 15, 1, -0.85, 3.541592653589793, 3.6, 0.5) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('great-britain-silverstone', 16, 0.7, -1.15, -1.8707963267948966, 2.4, 1.2) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('great-britain-silverstone', 17, 0.5, -0.9, -0, 2.6, 0.9) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('great-britain-silverstone', 18, 0.7, -0.65, 1.5707963267948966, 3, 0.7) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('great-britain-silverstone', 19, 0.45, -0.45, 3.541592653589793, 2.6, 1) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('great-britain-silverstone', 20, 0.75, -1, -1.1707963267948966, 2.4, 1) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('great-britain-silverstone', 21, 0.5, -1.5, 3.541592653589793, 2.6, 0.8) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('great-britain-silverstone', 22, 0.3, -1.15, 1.5707963267948966, 2.4, 1) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('great-britain-silverstone', 23, 0.7, -1.6, -0.7853981633974483, 3.4, 0.5) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('great-britain-silverstone', 24, 2, -1.95, -1.9707963267948967, 3, 0.7) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('great-britain-silverstone', 25, 3, -2.4, -3.3415926535897933, 3.4, 0.4) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('great-britain-silverstone', 26, 2.4, -2.65, 1.8707963267948966, 4, 0.3) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('great-britain-silverstone', 27, 1.4, -2.6, 3.141592653589793, 4.4, 0.2) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('great-britain-silverstone', 28, 0.5, -2.3, 2.8415926535897933, 4.8, 0.2) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('great-britain-silverstone', 29, 0.3, -1.7, 1.2707963267948965, 5, 0.1) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
|
||||
INSERT INTO track_tags (track_id, tag) VALUES ('great-britain-silverstone', 'f1') ON CONFLICT DO NOTHING;
|
||||
INSERT INTO track_tags (track_id, tag) VALUES ('great-britain-silverstone', 'fast') ON CONFLICT DO NOTHING;
|
||||
INSERT INTO track_tags (track_id, tag) VALUES ('great-britain-silverstone', 'flowing') ON CONFLICT DO NOTHING;
|
||||
INSERT INTO track_tags (track_id, tag) VALUES ('great-britain-silverstone', 'silverstone') ON CONFLICT DO NOTHING;
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Belgium (Spa-Francorchamps)
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
INSERT INTO tracks (id, name, description, author_id, visibility, scale,
|
||||
length_m, width_m, lane_width_m, surface, best_lap_ms, best_lap_holder,
|
||||
bounds_min_x, bounds_min_y, bounds_max_x, bounds_max_y,
|
||||
created_ms, updated_ms)
|
||||
VALUES ('belgium-spa', 'Belgium (Spa-Francorchamps)', 'Spa-Francorchamps. Long, fast, legendary Eau Rouge / Raidillon.', 'system', 'system', 27,
|
||||
3.85, 2.25, 0.18, 'carpet', 0, 'Verstappen',
|
||||
0.4, -2.75, 4.25, -0.5,
|
||||
1700000000000, 1700000000000)
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('belgium-spa', 0, 0.4, -0.5, -0, 4.8, 0) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('belgium-spa', 1, 1.6, -0.5, -0, 4.8, 0) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('belgium-spa', 2, 1.95, -0.6, -0.3, 4.2, 0.5) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('belgium-spa', 3, 2.2, -0.95, -0.9, 4, 0.6) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('belgium-spa', 4, 2.1, -1.3, -3.541592653589793, 4, 0.6) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('belgium-spa', 5, 2.7, -1.5, -0, 5, 0) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('belgium-spa', 6, 3.5, -1.5, -0, 5, 0) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('belgium-spa', 7, 3.85, -1.3, 1.5707963267948966, 2.4, 1.4) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('belgium-spa', 8, 3.6, -1.1, -3.4415926535897934, 2.6, 1.2) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('belgium-spa', 9, 3.3, -1.25, -4.0415926535897935, 2.6, 1.2) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('belgium-spa', 10, 3.5, -1.55, -1.5707963267948966, 3.6, 0.4) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('belgium-spa', 11, 3.25, -1.85, -0, 2.2, 1.6) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('belgium-spa', 12, 3.05, -1.7, 1.5707963267948966, 2.4, 1.4) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('belgium-spa', 13, 3.25, -1.5, -0, 3, 0.7) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('belgium-spa', 14, 3.7, -1.65, -1.8707963267948966, 3.4, 0.5) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('belgium-spa', 15, 4.1, -2, -2.1707963267948966, 4, 0.3) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('belgium-spa', 16, 4.25, -2.4, -3.541592653589793, 4.6, 0.3) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('belgium-spa', 17, 4, -2.7, 1.8707963267948966, 2.4, 1.4) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('belgium-spa', 18, 3.7, -2.55, -3.141592653589793, 2.4, 1.4) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('belgium-spa', 19, 3.55, -2.75, -3.8415926535897933, 2.4, 1.4) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('belgium-spa', 20, 3.05, -2.7, 1.5707963267948966, 2, 1.8) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('belgium-spa', 21, 2.85, -2.45, -0, 2, 1.8) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('belgium-spa', 22, 2, -2.2, -2.8415926535897933, 3.6, 0.3) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('belgium-spa', 23, 1.2, -1.6, -2.541592653589793, 4, 0.2) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
INSERT INTO track_waypoints (track_id, seq, x, y, heading_rad, speed_ms, curvature) VALUES ('belgium-spa', 24, 0.6, -1.05, -2.8415926535897933, 4.4, 0.2) ON CONFLICT (track_id, seq) DO NOTHING;
|
||||
|
||||
INSERT INTO track_tags (track_id, tag) VALUES ('belgium-spa', 'f1') ON CONFLICT DO NOTHING;
|
||||
INSERT INTO track_tags (track_id, tag) VALUES ('belgium-spa', 'long') ON CONFLICT DO NOTHING;
|
||||
INSERT INTO track_tags (track_id, tag) VALUES ('belgium-spa', 'fast') ON CONFLICT DO NOTHING;
|
||||
INSERT INTO track_tags (track_id, tag) VALUES ('belgium-spa', 'spa') ON CONFLICT DO NOTHING;
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Cars Catalog
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
INSERT INTO cars (id, name, owner_id, visibility, scale,
|
||||
length_mm, width_mm, height_mm, weight_g,
|
||||
chassis_model, chassis_material, chassis_printed, chassis_wheelbase_mm, chassis_track_mm,
|
||||
motor_kind, motor_class, motor_kv, motor_power_w,
|
||||
battery_voltage_v, battery_capacity_mah, battery_cells, battery_chemistry,
|
||||
drive, top_speed_ms, color_hex, avatar_url, active,
|
||||
created_ms, updated_ms)
|
||||
VALUES ('f1-2024-redbull-rb20', 'Oracle Red Bull Racing RB20', 'system', 'system', 24,
|
||||
238, 83, 40, 33,
|
||||
'RB20 carbon monocoque', 'carbon-fibre', FALSE, 150, 75,
|
||||
'turbo-hybrid', 'F1 PU', 0, 746000,
|
||||
0, 0, 0, 'li-ion MGU-K',
|
||||
'2WD', 95, '#1e3a8a', '', TRUE,
|
||||
1700000000000, 1700000000000)
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
INSERT INTO cars (id, name, owner_id, visibility, scale,
|
||||
length_mm, width_mm, height_mm, weight_g,
|
||||
chassis_model, chassis_material, chassis_printed, chassis_wheelbase_mm, chassis_track_mm,
|
||||
motor_kind, motor_class, motor_kv, motor_power_w,
|
||||
battery_voltage_v, battery_capacity_mah, battery_cells, battery_chemistry,
|
||||
drive, top_speed_ms, color_hex, avatar_url, active,
|
||||
created_ms, updated_ms)
|
||||
VALUES ('f1-2024-ferrari-sf24', 'Scuderia Ferrari SF-24', 'system', 'system', 24,
|
||||
238, 83, 40, 33,
|
||||
'SF-24 carbon monocoque', 'carbon-fibre', FALSE, 150, 75,
|
||||
'turbo-hybrid', 'F1 PU 066/12', 0, 746000,
|
||||
0, 0, 0, 'li-ion MGU-K',
|
||||
'2WD', 94, '#dc2626', '', TRUE,
|
||||
1700000000000, 1700000000000)
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
INSERT INTO cars (id, name, owner_id, visibility, scale,
|
||||
length_mm, width_mm, height_mm, weight_g,
|
||||
chassis_model, chassis_material, chassis_printed, chassis_wheelbase_mm, chassis_track_mm,
|
||||
motor_kind, motor_class, motor_kv, motor_power_w,
|
||||
battery_voltage_v, battery_capacity_mah, battery_cells, battery_chemistry,
|
||||
drive, top_speed_ms, color_hex, avatar_url, active,
|
||||
created_ms, updated_ms)
|
||||
VALUES ('f1-2024-mclaren-mcl38', 'McLaren MCL38', 'system', 'system', 24,
|
||||
238, 83, 40, 33,
|
||||
'MCL38 carbon monocoque', 'carbon-fibre', FALSE, 150, 75,
|
||||
'turbo-hybrid', 'F1 PU Mercedes-derived', 0, 746000,
|
||||
0, 0, 0, 'li-ion MGU-K',
|
||||
'2WD', 94, '#ea580c', '', TRUE,
|
||||
1700000000000, 1700000000000)
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
INSERT INTO cars (id, name, owner_id, visibility, scale,
|
||||
length_mm, width_mm, height_mm, weight_g,
|
||||
chassis_model, chassis_material, chassis_printed, chassis_wheelbase_mm, chassis_track_mm,
|
||||
motor_kind, motor_class, motor_kv, motor_power_w,
|
||||
battery_voltage_v, battery_capacity_mah, battery_cells, battery_chemistry,
|
||||
drive, top_speed_ms, color_hex, avatar_url, active,
|
||||
created_ms, updated_ms)
|
||||
VALUES ('f1-2024-mercedes-w15', 'Mercedes-AMG W15', 'system', 'system', 24,
|
||||
238, 83, 40, 33,
|
||||
'W15 carbon monocoque', 'carbon-fibre', FALSE, 150, 75,
|
||||
'turbo-hybrid', 'F1 PU M15', 0, 746000,
|
||||
0, 0, 0, 'li-ion MGU-K',
|
||||
'2WD', 94, '#06b6d4', '', TRUE,
|
||||
1700000000000, 1700000000000)
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
INSERT INTO cars (id, name, owner_id, visibility, scale,
|
||||
length_mm, width_mm, height_mm, weight_g,
|
||||
chassis_model, chassis_material, chassis_printed, chassis_wheelbase_mm, chassis_track_mm,
|
||||
motor_kind, motor_class, motor_kv, motor_power_w,
|
||||
battery_voltage_v, battery_capacity_mah, battery_cells, battery_chemistry,
|
||||
drive, top_speed_ms, color_hex, avatar_url, active,
|
||||
created_ms, updated_ms)
|
||||
VALUES ('f1-2024-astonmartin-amr24', 'Aston Martin Aramco AMR24', 'system', 'system', 24,
|
||||
238, 83, 40, 33,
|
||||
'AMR24 carbon monocoque', 'carbon-fibre', FALSE, 150, 75,
|
||||
'turbo-hybrid', 'F1 PU Mercedes-derived', 0, 746000,
|
||||
0, 0, 0, 'li-ion MGU-K',
|
||||
'2WD', 94, '#16a34a', '', TRUE,
|
||||
1700000000000, 1700000000000)
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
Reference in New Issue
Block a user