mirror of
https://github.com/IS1DI/x0gp.git
synced 2026-07-16 20:47:55 +00:00
3388 lines
121 KiB
Go
3388 lines
121 KiB
Go
// Package docs Code generated by swaggo/swag. DO NOT EDIT
|
|
package docs
|
|
|
|
import "github.com/swaggo/swag"
|
|
|
|
const docTemplate = `{
|
|
"schemes": {{ marshal .Schemes }},
|
|
"swagger": "2.0",
|
|
"info": {
|
|
"description": "{{escape .Description}}",
|
|
"title": "{{.Title}}",
|
|
"contact": {
|
|
"name": "x0gp"
|
|
},
|
|
"license": {
|
|
"name": "MIT"
|
|
},
|
|
"version": "{{.Version}}"
|
|
},
|
|
"host": "{{.Host}}",
|
|
"basePath": "{{.BasePath}}",
|
|
"paths": {
|
|
"/api/cars": {
|
|
"get": {
|
|
"description": "Returns all cars visible to the caller. Optional query filters: ` + "`" + `scope=system|public|private` + "`" + `, ` + "`" + `owner_id=\u003cstring\u003e` + "`" + `.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"cars"
|
|
],
|
|
"summary": "List cars",
|
|
"parameters": [
|
|
{
|
|
"enum": [
|
|
"system",
|
|
"public",
|
|
"private"
|
|
],
|
|
"type": "string",
|
|
"description": "Filter by visibility",
|
|
"name": "scope",
|
|
"in": "query"
|
|
},
|
|
{
|
|
"type": "string",
|
|
"description": "Filter by owner id",
|
|
"name": "owner_id",
|
|
"in": "query"
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "List of cars with count",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal server error",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"post": {
|
|
"description": "Creates a new car. The server fills ` + "`" + `id` + "`" + ` if the client sends an empty one. ` + "`" + `owner_id` + "`" + ` should be the caller's id in production (auth is disabled in PoC).",
|
|
"consumes": [
|
|
"application/json"
|
|
],
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"cars"
|
|
],
|
|
"summary": "Create a car",
|
|
"parameters": [
|
|
{
|
|
"description": "Car to create",
|
|
"name": "car",
|
|
"in": "body",
|
|
"required": true,
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.CarCreateRequest"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"201": {
|
|
"description": "Created",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.CarAck"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Invalid input",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"409": {
|
|
"description": "Car id already exists",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal server error",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/cars/{id}": {
|
|
"get": {
|
|
"description": "Dispatches on HTTP method. The id is taken from the URL path (` + "`" + `/api/cars/{id}` + "`" + `). System cars are immutable and cannot be updated or deleted.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"cars"
|
|
],
|
|
"summary": "Get / update / delete a car by id",
|
|
"parameters": [
|
|
{
|
|
"type": "string",
|
|
"description": "Car id",
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true
|
|
},
|
|
{
|
|
"description": "Patch payload (only for PUT)",
|
|
"name": "car",
|
|
"in": "body",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.CarUpdateRequest"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Car payload (GET) or update ack (PUT)",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.CarWire"
|
|
}
|
|
},
|
|
"204": {
|
|
"description": "Delete ack",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.CarAck"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Bad request / invalid input",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "System car is immutable",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Car not found",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"409": {
|
|
"description": "Car id already exists",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal server error",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"put": {
|
|
"description": "Dispatches on HTTP method. The id is taken from the URL path (` + "`" + `/api/cars/{id}` + "`" + `). System cars are immutable and cannot be updated or deleted.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"cars"
|
|
],
|
|
"summary": "Get / update / delete a car by id",
|
|
"parameters": [
|
|
{
|
|
"type": "string",
|
|
"description": "Car id",
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true
|
|
},
|
|
{
|
|
"description": "Patch payload (only for PUT)",
|
|
"name": "car",
|
|
"in": "body",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.CarUpdateRequest"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Car payload (GET) or update ack (PUT)",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.CarWire"
|
|
}
|
|
},
|
|
"204": {
|
|
"description": "Delete ack",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.CarAck"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Bad request / invalid input",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "System car is immutable",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Car not found",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"409": {
|
|
"description": "Car id already exists",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal server error",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"delete": {
|
|
"description": "Dispatches on HTTP method. The id is taken from the URL path (` + "`" + `/api/cars/{id}` + "`" + `). System cars are immutable and cannot be updated or deleted.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"cars"
|
|
],
|
|
"summary": "Get / update / delete a car by id",
|
|
"parameters": [
|
|
{
|
|
"type": "string",
|
|
"description": "Car id",
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true
|
|
},
|
|
{
|
|
"description": "Patch payload (only for PUT)",
|
|
"name": "car",
|
|
"in": "body",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.CarUpdateRequest"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Car payload (GET) or update ack (PUT)",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.CarWire"
|
|
}
|
|
},
|
|
"204": {
|
|
"description": "Delete ack",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.CarAck"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Bad request / invalid input",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "System car is immutable",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Car not found",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"409": {
|
|
"description": "Car id already exists",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal server error",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/catalog": {
|
|
"get": {
|
|
"description": "Returns the full in-memory catalog: all tracks, all cars, and aggregate counters. This is the cheapest way for a UI to bootstrap its initial state.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"catalog"
|
|
],
|
|
"summary": "Combined catalog snapshot",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Snapshot of tracks, cars and summary counters",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal server error",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/clans": {
|
|
"get": {
|
|
"description": "Returns clans ordered by tag. Limit 1..200, default 50.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"clans"
|
|
],
|
|
"summary": "List clans",
|
|
"parameters": [
|
|
{
|
|
"type": "integer",
|
|
"description": "Page size",
|
|
"name": "limit",
|
|
"in": "query"
|
|
},
|
|
{
|
|
"type": "integer",
|
|
"description": "Page offset",
|
|
"name": "offset",
|
|
"in": "query"
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Page of clans",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.ClanListResponse"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal server error",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"post": {
|
|
"description": "Creates a new clan. ` + "`" + `tag` + "`" + ` must be exactly 3 uppercase ASCII letters (e.g. \"ACE\") and unique.",
|
|
"consumes": [
|
|
"application/json"
|
|
],
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"clans"
|
|
],
|
|
"summary": "Create a clan",
|
|
"parameters": [
|
|
{
|
|
"description": "Clan to create",
|
|
"name": "clan",
|
|
"in": "body",
|
|
"required": true,
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.ClanCreateRequest"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"201": {
|
|
"description": "Created clan",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.Clan"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Invalid input (tag must be 3 uppercase letters)",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"409": {
|
|
"description": "Clan with that tag already exists",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal server error",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/clans/{id}": {
|
|
"get": {
|
|
"description": "Dispatches on HTTP method.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"clans"
|
|
],
|
|
"summary": "Get / update / delete a clan by id",
|
|
"parameters": [
|
|
{
|
|
"type": "string",
|
|
"description": "Clan id",
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true
|
|
},
|
|
{
|
|
"description": "Patch payload (only for PUT)",
|
|
"name": "clan",
|
|
"in": "body",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.ClanUpdateRequest"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Clan payload or update result",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.Clan"
|
|
}
|
|
},
|
|
"204": {
|
|
"description": "Delete ack",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.Clan"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Bad request",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Clan not found",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal server error",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"put": {
|
|
"description": "Dispatches on HTTP method.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"clans"
|
|
],
|
|
"summary": "Get / update / delete a clan by id",
|
|
"parameters": [
|
|
{
|
|
"type": "string",
|
|
"description": "Clan id",
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true
|
|
},
|
|
{
|
|
"description": "Patch payload (only for PUT)",
|
|
"name": "clan",
|
|
"in": "body",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.ClanUpdateRequest"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Clan payload or update result",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.Clan"
|
|
}
|
|
},
|
|
"204": {
|
|
"description": "Delete ack",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.Clan"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Bad request",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Clan not found",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal server error",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"delete": {
|
|
"description": "Dispatches on HTTP method.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"clans"
|
|
],
|
|
"summary": "Get / update / delete a clan by id",
|
|
"parameters": [
|
|
{
|
|
"type": "string",
|
|
"description": "Clan id",
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true
|
|
},
|
|
{
|
|
"description": "Patch payload (only for PUT)",
|
|
"name": "clan",
|
|
"in": "body",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.ClanUpdateRequest"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Clan payload or update result",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.Clan"
|
|
}
|
|
},
|
|
"204": {
|
|
"description": "Delete ack",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.Clan"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Bad request",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Clan not found",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal server error",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/drivers": {
|
|
"get": {
|
|
"description": "Returns drivers ordered by nickname. Optional ` + "`" + `clan_id` + "`" + ` filter.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"drivers"
|
|
],
|
|
"summary": "List drivers",
|
|
"parameters": [
|
|
{
|
|
"type": "integer",
|
|
"description": "Page size",
|
|
"name": "limit",
|
|
"in": "query"
|
|
},
|
|
{
|
|
"type": "integer",
|
|
"description": "Page offset",
|
|
"name": "offset",
|
|
"in": "query"
|
|
},
|
|
{
|
|
"type": "string",
|
|
"description": "Filter by clan id",
|
|
"name": "clan_id",
|
|
"in": "query"
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Page of drivers",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.DriverListResponse"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal server error",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"post": {
|
|
"description": "Creates a new driver. ` + "`" + `nickname` + "`" + ` must be exactly 3 uppercase ASCII letters (e.g. \"ACE\") and unique. ` + "`" + `clan_id` + "`" + ` is optional and must reference an existing clan.",
|
|
"consumes": [
|
|
"application/json"
|
|
],
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"drivers"
|
|
],
|
|
"summary": "Create a driver",
|
|
"parameters": [
|
|
{
|
|
"description": "Driver to create",
|
|
"name": "driver",
|
|
"in": "body",
|
|
"required": true,
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.DriverCreateRequest"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"201": {
|
|
"description": "Created driver",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.Driver"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Invalid input (nickname must be 3 uppercase letters)",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"409": {
|
|
"description": "Driver with that nickname already exists",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal server error",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/drivers/{id}": {
|
|
"get": {
|
|
"description": "Dispatches on HTTP method. Path ` + "`" + `/api/drivers/by-nick/{nick}` + "`" + ` is a convenience that returns the driver by 3-letter nickname.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"drivers"
|
|
],
|
|
"summary": "Get / update / delete a driver by id",
|
|
"parameters": [
|
|
{
|
|
"type": "string",
|
|
"description": "Driver id",
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true
|
|
},
|
|
{
|
|
"description": "Patch payload (only for PUT)",
|
|
"name": "driver",
|
|
"in": "body",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.DriverUpdateRequest"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Driver payload or update result",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.Driver"
|
|
}
|
|
},
|
|
"204": {
|
|
"description": "Delete ack",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.Driver"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Bad request",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Driver not found",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal server error",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"put": {
|
|
"description": "Dispatches on HTTP method. Path ` + "`" + `/api/drivers/by-nick/{nick}` + "`" + ` is a convenience that returns the driver by 3-letter nickname.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"drivers"
|
|
],
|
|
"summary": "Get / update / delete a driver by id",
|
|
"parameters": [
|
|
{
|
|
"type": "string",
|
|
"description": "Driver id",
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true
|
|
},
|
|
{
|
|
"description": "Patch payload (only for PUT)",
|
|
"name": "driver",
|
|
"in": "body",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.DriverUpdateRequest"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Driver payload or update result",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.Driver"
|
|
}
|
|
},
|
|
"204": {
|
|
"description": "Delete ack",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.Driver"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Bad request",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Driver not found",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal server error",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"delete": {
|
|
"description": "Dispatches on HTTP method. Path ` + "`" + `/api/drivers/by-nick/{nick}` + "`" + ` is a convenience that returns the driver by 3-letter nickname.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"drivers"
|
|
],
|
|
"summary": "Get / update / delete a driver by id",
|
|
"parameters": [
|
|
{
|
|
"type": "string",
|
|
"description": "Driver id",
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true
|
|
},
|
|
{
|
|
"description": "Patch payload (only for PUT)",
|
|
"name": "driver",
|
|
"in": "body",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.DriverUpdateRequest"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Driver payload or update result",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.Driver"
|
|
}
|
|
},
|
|
"204": {
|
|
"description": "Delete ack",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.Driver"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Bad request",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Driver not found",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal server error",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/drivers/{id}/device": {
|
|
"put": {
|
|
"description": "Dispatches on HTTP method. Path ` + "`" + `/api/drivers/by-nick/{nick}` + "`" + ` is a convenience that returns the driver by 3-letter nickname.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"drivers"
|
|
],
|
|
"summary": "Get / update / delete a driver by id",
|
|
"parameters": [
|
|
{
|
|
"type": "string",
|
|
"description": "Driver id",
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true
|
|
},
|
|
{
|
|
"description": "Patch payload (only for PUT)",
|
|
"name": "driver",
|
|
"in": "body",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.DriverUpdateRequest"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Driver payload or update result",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.Driver"
|
|
}
|
|
},
|
|
"204": {
|
|
"description": "Delete ack",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.Driver"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Bad request",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Driver not found",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal server error",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/leaderboard": {
|
|
"get": {
|
|
"description": "Returns paginated leaderboard list. Supports overall leaderboard or track-specific leaderboard for the current year.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"leaderboard"
|
|
],
|
|
"summary": "Leaderboard",
|
|
"parameters": [
|
|
{
|
|
"type": "string",
|
|
"description": "Track ID. If empty, returns overall leaderboard.",
|
|
"name": "track_id",
|
|
"in": "query"
|
|
},
|
|
{
|
|
"type": "integer",
|
|
"description": "Year. Defaults to current year.",
|
|
"name": "year",
|
|
"in": "query"
|
|
},
|
|
{
|
|
"type": "integer",
|
|
"description": "Limit (1..200, default 50)",
|
|
"name": "limit",
|
|
"in": "query"
|
|
},
|
|
{
|
|
"type": "integer",
|
|
"description": "Offset (default 0)",
|
|
"name": "offset",
|
|
"in": "query"
|
|
},
|
|
{
|
|
"type": "string",
|
|
"description": "Current driver ID. Defaults to driver-seed-001 (mock Alice).",
|
|
"name": "X-Driver-Id",
|
|
"in": "header"
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Leaderboard page and current driver position",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.LeaderboardResponse"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Invalid input",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal server error",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/leaderboard/tracks": {
|
|
"get": {
|
|
"description": "Returns the list of scheduled tracks with their status (finished/active/planned), top 3 drivers, and current driver position, sorted by calendar start date.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"leaderboard"
|
|
],
|
|
"summary": "Track Calendar Leaderboards",
|
|
"parameters": [
|
|
{
|
|
"type": "integer",
|
|
"description": "Year. Defaults to current year.",
|
|
"name": "year",
|
|
"in": "query"
|
|
},
|
|
{
|
|
"type": "string",
|
|
"description": "Current driver ID. Defaults to driver-seed-001 (mock Alice).",
|
|
"name": "X-Driver-Id",
|
|
"in": "header"
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Calendar leaderboards list",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.TrackCalendarLeaderboardResponse"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Invalid input",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal server error",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/races": {
|
|
"get": {
|
|
"description": "Returns one keyset page of races. Live races (status lobby|racing) come from in-memory lobby.Service; finished races come from Postgres. The two sources are merged and ordered by (started_ms DESC, id ASC). The cursor is opaque: pass back the next_cursor verbatim from a previous page.\n## Filters\n- ` + "`" + `status` + "`" + ` (comma-separated, e.g. \"racing,finished\"). Recognised values: all, lobby, racing, finished.\n- ` + "`" + `track_id` + "`" + ` (exact match against a physical track id; custom lobbies are not supported in this build).\n- ` + "`" + `limit` + "`" + ` (1..200, default 50)",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"races"
|
|
],
|
|
"summary": "List races (keyset paginated)",
|
|
"parameters": [
|
|
{
|
|
"enum": [
|
|
"all",
|
|
"lobby",
|
|
"racing",
|
|
"finished"
|
|
],
|
|
"type": "string",
|
|
"description": "Comma-separated status filter",
|
|
"name": "status",
|
|
"in": "query"
|
|
},
|
|
{
|
|
"type": "string",
|
|
"description": "Filter by physical track id",
|
|
"name": "track_id",
|
|
"in": "query"
|
|
},
|
|
{
|
|
"type": "string",
|
|
"description": "Opaque keyset cursor (next_cursor from previous page)",
|
|
"name": "cursor",
|
|
"in": "query"
|
|
},
|
|
{
|
|
"type": "integer",
|
|
"description": "Page size (1..200, default 50)",
|
|
"name": "limit",
|
|
"in": "query"
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Page of races",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.RaceListResponse"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Bad request / invalid cursor / invalid status",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal server error",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/races/plans": {
|
|
"get": {
|
|
"description": "Returns race plans in ascending start_at order. Pass back the ` + "`" + `next_cursor` + "`" + ` to get the next page.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"plans"
|
|
],
|
|
"summary": "List race plans (keyset paginated)",
|
|
"parameters": [
|
|
{
|
|
"type": "string",
|
|
"description": "Opaque keyset cursor",
|
|
"name": "cursor",
|
|
"in": "query"
|
|
},
|
|
{
|
|
"type": "integer",
|
|
"description": "Page size (default 50)",
|
|
"name": "limit",
|
|
"in": "query"
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Page of plans",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.RacePlanListResponse"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Invalid cursor",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal server error",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"post": {
|
|
"description": "Creates a recurring or one-off scheduled race. The scheduler materialises a lobby race when ` + "`" + `next_fire_ms` + "`" + ` \u003c= now. If ` + "`" + `interval_s` + "`" + ` \u003e 0 the plan repeats; if ` + "`" + `count` + "`" + ` \u003e 0 the plan disables itself after that many fires.",
|
|
"consumes": [
|
|
"application/json"
|
|
],
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"plans"
|
|
],
|
|
"summary": "Create a race plan",
|
|
"parameters": [
|
|
{
|
|
"description": "Plan to create",
|
|
"name": "plan",
|
|
"in": "body",
|
|
"required": true,
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.RacePlanCreateRequest"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"201": {
|
|
"description": "Created plan",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.RacePlan"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Invalid input",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"409": {
|
|
"description": "Plan id already exists",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal server error",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/races/plans/{id}": {
|
|
"delete": {
|
|
"description": "Removes a plan by id. Materialised races already in the lobby are not affected.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"plans"
|
|
],
|
|
"summary": "Delete a race plan",
|
|
"parameters": [
|
|
{
|
|
"type": "string",
|
|
"description": "Plan id",
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "ok",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Plan not found",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal server error",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/races/queue": {
|
|
"get": {
|
|
"description": "Returns the queue entries for a driver, oldest first.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"races"
|
|
],
|
|
"summary": "List driver's queue entries",
|
|
"parameters": [
|
|
{
|
|
"type": "string",
|
|
"description": "Driver id",
|
|
"name": "X-Driver-Id",
|
|
"in": "header"
|
|
},
|
|
{
|
|
"type": "string",
|
|
"description": "Driver id (alternative to header)",
|
|
"name": "driver_id",
|
|
"in": "query"
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Queue entries",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.RaceQueueListResponse"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "driver_id required",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal server error",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"delete": {
|
|
"description": "Removes a single (driver, race) entry from the queue. Does not detach the driver from the race if already joined.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"races"
|
|
],
|
|
"summary": "Leave a queue entry",
|
|
"parameters": [
|
|
{
|
|
"type": "string",
|
|
"description": "Driver id",
|
|
"name": "X-Driver-Id",
|
|
"in": "header"
|
|
},
|
|
{
|
|
"type": "string",
|
|
"description": "Driver id",
|
|
"name": "driver_id",
|
|
"in": "query"
|
|
},
|
|
{
|
|
"type": "string",
|
|
"description": "Race id",
|
|
"name": "race_id",
|
|
"in": "query",
|
|
"required": true
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "ok",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "driver_id and race_id required",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "queue entry not found",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal server error",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/races/queue/join": {
|
|
"post": {
|
|
"description": "Puts the driver in the queue for each of the next N (default 3) lobby|countdown races, soonest first. If a race has a free slot, the driver is also attached to it (best effort). Idempotent per (driver, race).",
|
|
"consumes": [
|
|
"application/json"
|
|
],
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"races"
|
|
],
|
|
"summary": "Enqueue driver on next N upcoming races",
|
|
"parameters": [
|
|
{
|
|
"type": "string",
|
|
"description": "Driver id (alternative to body)",
|
|
"name": "X-Driver-Id",
|
|
"in": "header"
|
|
},
|
|
{
|
|
"description": "Driver id and optional limit",
|
|
"name": "body",
|
|
"in": "body",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.RaceQueueJoinRequest"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Queue entries created",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.RaceQueueJoinResponse"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "driver_id required",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal server error",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/races/upcoming": {
|
|
"get": {
|
|
"description": "Returns up to ` + "`" + `limit` + "`" + ` races currently in status lobby|countdown, ordered soonest first. Useful for the \"join queue\" UI and dashboard.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"races"
|
|
],
|
|
"summary": "Next N upcoming races",
|
|
"parameters": [
|
|
{
|
|
"type": "integer",
|
|
"description": "Number of races to return (1..16, default 3)",
|
|
"name": "limit",
|
|
"in": "query"
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Upcoming races",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.RaceUpcomingResponse"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal server error",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/tracks": {
|
|
"get": {
|
|
"description": "Returns all tracks visible to the caller. Optional query filters: ` + "`" + `scope=system|public|private` + "`" + `, ` + "`" + `tag=\u003cstring\u003e` + "`" + `.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"tracks"
|
|
],
|
|
"summary": "List tracks",
|
|
"parameters": [
|
|
{
|
|
"enum": [
|
|
"system",
|
|
"public",
|
|
"private"
|
|
],
|
|
"type": "string",
|
|
"description": "Filter by visibility",
|
|
"name": "scope",
|
|
"in": "query"
|
|
},
|
|
{
|
|
"type": "string",
|
|
"description": "Filter by tag (case-insensitive)",
|
|
"name": "tag",
|
|
"in": "query"
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "List of tracks with count",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal server error",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"post": {
|
|
"description": "Creates a new track. The server fills ` + "`" + `id` + "`" + ` if the client sends an empty one. ` + "`" + `author_id` + "`" + ` should be the caller's id in production (auth is disabled in PoC).",
|
|
"consumes": [
|
|
"application/json"
|
|
],
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"tracks"
|
|
],
|
|
"summary": "Create a track",
|
|
"parameters": [
|
|
{
|
|
"description": "Track to create",
|
|
"name": "track",
|
|
"in": "body",
|
|
"required": true,
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.TrackCreateRequest"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"201": {
|
|
"description": "Created",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.TrackAck"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Invalid input",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"409": {
|
|
"description": "Track id already exists",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal server error",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/tracks/calendar": {
|
|
"get": {
|
|
"description": "Returns the full calendar of track active periods.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"tracks"
|
|
],
|
|
"summary": "Get Track Calendar",
|
|
"responses": {
|
|
"200": {
|
|
"description": "List of scheduled active periods",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.TrackCalendarResponse"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal error",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"post": {
|
|
"description": "Schedules an active period for a physical track.",
|
|
"consumes": [
|
|
"application/json"
|
|
],
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"tracks"
|
|
],
|
|
"summary": "Schedule Track Active Period",
|
|
"parameters": [
|
|
{
|
|
"description": "Calendar entry details",
|
|
"name": "payload",
|
|
"in": "body",
|
|
"required": true,
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.TrackCalendarCreateRequest"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"201": {
|
|
"description": "Created calendar entry",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.TrackCalendarEntry"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Invalid input",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Track not found",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal error",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/tracks/calendar/{id}": {
|
|
"delete": {
|
|
"description": "Deletes a scheduled active period by ID.",
|
|
"tags": [
|
|
"tracks"
|
|
],
|
|
"summary": "Delete Scheduled Period",
|
|
"parameters": [
|
|
{
|
|
"type": "integer",
|
|
"description": "Calendar Entry ID",
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true
|
|
}
|
|
],
|
|
"responses": {
|
|
"204": {
|
|
"description": "No Content"
|
|
},
|
|
"404": {
|
|
"description": "Not found",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal error",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/tracks/{id}": {
|
|
"get": {
|
|
"description": "Dispatches on HTTP method. The id is taken from the URL path (` + "`" + `/api/tracks/{id}` + "`" + `). System tracks are immutable and cannot be updated or deleted.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"tracks"
|
|
],
|
|
"summary": "Get / update / delete a track by id",
|
|
"parameters": [
|
|
{
|
|
"type": "string",
|
|
"description": "Track id",
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true
|
|
},
|
|
{
|
|
"description": "Patch payload (only for PUT)",
|
|
"name": "track",
|
|
"in": "body",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.TrackUpdateRequest"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Track payload (GET) or update ack (PUT)",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.TrackWire"
|
|
}
|
|
},
|
|
"204": {
|
|
"description": "Delete ack",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.TrackAck"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Bad request / invalid input",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "System track is immutable",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Track not found",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"409": {
|
|
"description": "Track id already exists",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal server error",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"put": {
|
|
"description": "Dispatches on HTTP method. The id is taken from the URL path (` + "`" + `/api/tracks/{id}` + "`" + `). System tracks are immutable and cannot be updated or deleted.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"tracks"
|
|
],
|
|
"summary": "Get / update / delete a track by id",
|
|
"parameters": [
|
|
{
|
|
"type": "string",
|
|
"description": "Track id",
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true
|
|
},
|
|
{
|
|
"description": "Patch payload (only for PUT)",
|
|
"name": "track",
|
|
"in": "body",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.TrackUpdateRequest"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Track payload (GET) or update ack (PUT)",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.TrackWire"
|
|
}
|
|
},
|
|
"204": {
|
|
"description": "Delete ack",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.TrackAck"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Bad request / invalid input",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "System track is immutable",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Track not found",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"409": {
|
|
"description": "Track id already exists",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal server error",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"delete": {
|
|
"description": "Dispatches on HTTP method. The id is taken from the URL path (` + "`" + `/api/tracks/{id}` + "`" + `). System tracks are immutable and cannot be updated or deleted.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"tracks"
|
|
],
|
|
"summary": "Get / update / delete a track by id",
|
|
"parameters": [
|
|
{
|
|
"type": "string",
|
|
"description": "Track id",
|
|
"name": "id",
|
|
"in": "path",
|
|
"required": true
|
|
},
|
|
{
|
|
"description": "Patch payload (only for PUT)",
|
|
"name": "track",
|
|
"in": "body",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.TrackUpdateRequest"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Track payload (GET) or update ack (PUT)",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.TrackWire"
|
|
}
|
|
},
|
|
"204": {
|
|
"description": "Delete ack",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.TrackAck"
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Bad request / invalid input",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"403": {
|
|
"description": "System track is immutable",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"404": {
|
|
"description": "Track not found",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"409": {
|
|
"description": "Track id already exists",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal server error",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/version": {
|
|
"get": {
|
|
"description": "Returns the current server version.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"system"
|
|
],
|
|
"summary": "Get server version",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Success",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.VersionResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/video/control": {
|
|
"post": {
|
|
"description": "Sends a text command or a JSON control packet to the ESP32 command port (UDP 8888).",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"video"
|
|
],
|
|
"summary": "Send command to ESP32",
|
|
"parameters": [
|
|
{
|
|
"type": "integer",
|
|
"description": "Device ID (0..127)",
|
|
"name": "id",
|
|
"in": "query"
|
|
},
|
|
{
|
|
"type": "string",
|
|
"description": "Command text (legacy)",
|
|
"name": "cmd",
|
|
"in": "query"
|
|
},
|
|
{
|
|
"description": "Control packet JSON (steer, throttle, brake, gear, flags)",
|
|
"name": "body",
|
|
"in": "body",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": {
|
|
"type": "integer"
|
|
}
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "Command sent",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
},
|
|
"400": {
|
|
"description": "Missing parameter or invalid body",
|
|
"schema": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"500": {
|
|
"description": "Internal error",
|
|
"schema": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/video/stream": {
|
|
"get": {
|
|
"description": "Streams video frames received via UDP as multipart/x-mixed-replace. Suitable for raw img tags in browser.",
|
|
"produces": [
|
|
"multipart/x-mixed-replace"
|
|
],
|
|
"tags": [
|
|
"video"
|
|
],
|
|
"summary": "Get MJPEG video stream from ESP32",
|
|
"parameters": [
|
|
{
|
|
"type": "integer",
|
|
"description": "Device ID (0..127)",
|
|
"name": "id",
|
|
"in": "query"
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "MJPEG stream",
|
|
"schema": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/api/webrtc/connect": {
|
|
"post": {
|
|
"description": "Accepts SDP Offer, negotiates peer connection, and returns SDP Answer. Routes video and control data via DataChannels.",
|
|
"consumes": [
|
|
"application/json"
|
|
],
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"webrtc"
|
|
],
|
|
"summary": "Establish WebRTC connection for driver-car 1-to-1 proxying",
|
|
"parameters": [
|
|
{
|
|
"description": "SDP Offer details",
|
|
"name": "body",
|
|
"in": "body",
|
|
"required": true,
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.WebRTCConnectRequest"
|
|
}
|
|
}
|
|
],
|
|
"responses": {
|
|
"200": {
|
|
"description": "SDP Answer negotiation success",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.WebRTCConnectResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/health": {
|
|
"get": {
|
|
"description": "Returns 200 OK with current engine phase, WS connection count and total snapshot drops. Intended for load-balancer health checks.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"system"
|
|
],
|
|
"summary": "Liveness probe",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Server is up",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/stats": {
|
|
"get": {
|
|
"description": "Returns the current race phase plus realtime counters: active WS connections and total snapshots dropped due to slow consumers.",
|
|
"produces": [
|
|
"application/json"
|
|
],
|
|
"tags": [
|
|
"system"
|
|
],
|
|
"summary": "Realtime runtime stats",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Runtime counters",
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/ws": {
|
|
"get": {
|
|
"description": "Opens a bidirectional WebSocket for the realtime race protocol. The server replies with ` + "`" + `server_hello` + "`" + `, then broadcasts ` + "`" + `race_snapshot` + "`" + ` frames at ` + "`" + `X0GP_SNAPSHOT_HZ` + "`" + `.\n## Frame format\nEvery frame is a JSON envelope of the form:\n` + "`" + `` + "`" + `` + "`" + `json\n{ \"type\": \"\u003cmessage_type\u003e\", \"seq\": 42, \"ts_ms\": 1700000000000, \"payload\": { ... } }\n` + "`" + `` + "`" + `` + "`" + `\n` + "`" + `type` + "`" + ` discriminates the payload; ` + "`" + `payload` + "`" + ` is documented per message type in the schemas listed in ` + "`" + `components/schemas` + "`" + ` (see ` + "`" + `Envelope` + "`" + `, ` + "`" + `ClientHello` + "`" + `, ` + "`" + `ServerHello` + "`" + `, ` + "`" + `InputState` + "`" + `, ` + "`" + `JoinRace` + "`" + `, ` + "`" + `LeaveRace` + "`" + `, ` + "`" + `RaceSnapshot` + "`" + `, ` + "`" + `InputAck` + "`" + `, ` + "`" + `RaceEvent` + "`" + `, ` + "`" + `Ping` + "`" + `, ` + "`" + `Pong` + "`" + `, ` + "`" + `ErrorMsg` + "`" + `, plus the catalog/lobby/stats envelopes).\n## Client -\u003e Server\n- ` + "`" + `client_hello` + "`" + ` — first frame after connect. Sent once.\n- ` + "`" + `join_race` + "`" + ` / ` + "`" + `leave_race` + "`" + ` — join or leave the race session.\n- ` + "`" + `input_state` + "`" + ` — high-rate control (steering/throttle/brake/gear). Send at 60-120 Hz.\n- ` + "`" + `ping` + "`" + ` — round-trip time probe.\n- ` + "`" + `track_*` + "`" + ` / ` + "`" + `car_*` + "`" + ` — catalog CRUD (mirror of the REST /api/* endpoints).\n- ` + "`" + `lobby_*` + "`" + ` — lobby management.\n- ` + "`" + `stats_request` + "`" + ` — ask for a stats snapshot.\n## Server -\u003e Client\n- ` + "`" + `server_hello` + "`" + ` — handshake reply; includes session id and engine config.\n- ` + "`" + `race_snapshot` + "`" + ` — periodic authoritative state broadcast.\n- ` + "`" + `input_ack` + "`" + ` — per-input acknowledgement (optional; drop-tolerant).\n- ` + "`" + `race_event` + "`" + ` — start/lap/sector/dnf/finish.\n- ` + "`" + `track_snapshot` + "`" + ` / ` + "`" + `car_snapshot` + "`" + ` / ` + "`" + `catalog_summary` + "`" + ` — catalog feeds on change.\n- ` + "`" + `lobby_snapshot` + "`" + ` — full lobby state on change.\n- ` + "`" + `stats_snapshot` + "`" + ` / ` + "`" + `race_standings` + "`" + ` / ` + "`" + `lap_recorded` + "`" + ` / ` + "`" + `race_result` + "`" + ` — stats feeds.\n- ` + "`" + `error` + "`" + ` — protocol or validation error.\n## Notes\n- Origin checks are disabled in PoC mode (` + "`" + `CheckOrigin: always-allow` + "`" + `).\n- Ping interval: 20 s. Pong wait: 60 s. Max frame size: 8 KiB.\n- Input acks are best-effort and silently dropped under load.",
|
|
"tags": [
|
|
"websocket"
|
|
],
|
|
"summary": "WebSocket endpoint (binary JSON envelope protocol)",
|
|
"responses": {
|
|
"101": {
|
|
"description": "Switching protocols. Subsequent frames follow the envelope schema.",
|
|
"schema": {
|
|
"$ref": "#/definitions/transport.Envelope"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"definitions": {
|
|
"transport.BatteryWire": {
|
|
"type": "object",
|
|
"properties": {
|
|
"capacity_mah": {
|
|
"type": "integer"
|
|
},
|
|
"cells": {
|
|
"type": "integer"
|
|
},
|
|
"chemistry": {
|
|
"type": "string"
|
|
},
|
|
"voltage_v": {
|
|
"type": "number"
|
|
}
|
|
}
|
|
},
|
|
"transport.CarAck": {
|
|
"type": "object",
|
|
"properties": {
|
|
"car": {
|
|
"$ref": "#/definitions/transport.CarWire"
|
|
},
|
|
"error": {
|
|
"type": "string"
|
|
},
|
|
"id": {
|
|
"type": "string"
|
|
},
|
|
"ok": {
|
|
"type": "boolean"
|
|
}
|
|
}
|
|
},
|
|
"transport.CarCreateRequest": {
|
|
"type": "object",
|
|
"properties": {
|
|
"car": {
|
|
"$ref": "#/definitions/transport.CarWire"
|
|
}
|
|
}
|
|
},
|
|
"transport.CarPatch": {
|
|
"type": "object",
|
|
"properties": {
|
|
"active": {
|
|
"type": "boolean"
|
|
},
|
|
"avatar_url": {
|
|
"type": "string"
|
|
},
|
|
"battery": {
|
|
"$ref": "#/definitions/transport.BatteryWire"
|
|
},
|
|
"chassis": {
|
|
"$ref": "#/definitions/transport.ChassisWire"
|
|
},
|
|
"color_hex": {
|
|
"type": "string"
|
|
},
|
|
"drive": {
|
|
"type": "string"
|
|
},
|
|
"height_mm": {
|
|
"type": "number"
|
|
},
|
|
"length_mm": {
|
|
"type": "number"
|
|
},
|
|
"motor": {
|
|
"$ref": "#/definitions/transport.MotorWire"
|
|
},
|
|
"name": {
|
|
"type": "string"
|
|
},
|
|
"scale": {
|
|
"type": "integer"
|
|
},
|
|
"top_speed_ms": {
|
|
"type": "number"
|
|
},
|
|
"visibility": {
|
|
"type": "string"
|
|
},
|
|
"weight_g": {
|
|
"type": "number"
|
|
},
|
|
"width_mm": {
|
|
"type": "number"
|
|
}
|
|
}
|
|
},
|
|
"transport.CarUpdateRequest": {
|
|
"type": "object",
|
|
"properties": {
|
|
"id": {
|
|
"type": "string"
|
|
},
|
|
"patch": {
|
|
"$ref": "#/definitions/transport.CarPatch"
|
|
}
|
|
}
|
|
},
|
|
"transport.CarWire": {
|
|
"type": "object",
|
|
"properties": {
|
|
"active": {
|
|
"type": "boolean"
|
|
},
|
|
"avatar_url": {
|
|
"type": "string"
|
|
},
|
|
"battery": {
|
|
"$ref": "#/definitions/transport.BatteryWire"
|
|
},
|
|
"best_lap_ms": {
|
|
"type": "integer"
|
|
},
|
|
"chassis": {
|
|
"$ref": "#/definitions/transport.ChassisWire"
|
|
},
|
|
"color_hex": {
|
|
"type": "string"
|
|
},
|
|
"created_ms": {
|
|
"type": "integer"
|
|
},
|
|
"drive": {
|
|
"type": "string"
|
|
},
|
|
"height_mm": {
|
|
"type": "number"
|
|
},
|
|
"id": {
|
|
"type": "string"
|
|
},
|
|
"length_mm": {
|
|
"type": "number"
|
|
},
|
|
"motor": {
|
|
"$ref": "#/definitions/transport.MotorWire"
|
|
},
|
|
"name": {
|
|
"type": "string"
|
|
},
|
|
"owner_id": {
|
|
"type": "string"
|
|
},
|
|
"scale": {
|
|
"type": "integer"
|
|
},
|
|
"top_speed_ms": {
|
|
"type": "number"
|
|
},
|
|
"total_distance_m": {
|
|
"type": "number"
|
|
},
|
|
"total_laps": {
|
|
"type": "integer"
|
|
},
|
|
"total_races": {
|
|
"type": "integer"
|
|
},
|
|
"updated_ms": {
|
|
"type": "integer"
|
|
},
|
|
"visibility": {
|
|
"type": "string"
|
|
},
|
|
"weight_g": {
|
|
"type": "number"
|
|
},
|
|
"width_mm": {
|
|
"type": "number"
|
|
}
|
|
}
|
|
},
|
|
"transport.ChassisWire": {
|
|
"type": "object",
|
|
"properties": {
|
|
"material": {
|
|
"type": "string"
|
|
},
|
|
"model": {
|
|
"type": "string"
|
|
},
|
|
"printed": {
|
|
"type": "boolean"
|
|
},
|
|
"track_mm": {
|
|
"type": "number"
|
|
},
|
|
"wheelbase_mm": {
|
|
"type": "number"
|
|
}
|
|
}
|
|
},
|
|
"transport.Clan": {
|
|
"type": "object",
|
|
"properties": {
|
|
"avatar_url": {
|
|
"type": "string",
|
|
"example": "https://cdn.example.com/c/ace.png"
|
|
},
|
|
"created_ms": {
|
|
"type": "integer"
|
|
},
|
|
"id": {
|
|
"type": "string",
|
|
"example": "clan-1782000-1"
|
|
},
|
|
"name": {
|
|
"type": "string",
|
|
"example": "Ace Racing"
|
|
},
|
|
"tag": {
|
|
"type": "string",
|
|
"example": "ACE"
|
|
},
|
|
"updated_ms": {
|
|
"type": "integer"
|
|
}
|
|
}
|
|
},
|
|
"transport.ClanCreateRequest": {
|
|
"type": "object",
|
|
"properties": {
|
|
"avatar_url": {
|
|
"type": "string",
|
|
"example": "https://cdn.example.com/c/ace.png"
|
|
},
|
|
"id": {
|
|
"type": "string",
|
|
"example": "clan-1782000-1"
|
|
},
|
|
"name": {
|
|
"type": "string",
|
|
"example": "Ace Racing"
|
|
},
|
|
"tag": {
|
|
"type": "string",
|
|
"example": "ACE"
|
|
}
|
|
}
|
|
},
|
|
"transport.ClanListResponse": {
|
|
"type": "object",
|
|
"properties": {
|
|
"count": {
|
|
"type": "integer"
|
|
},
|
|
"items": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/definitions/transport.Clan"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"transport.ClanUpdateRequest": {
|
|
"type": "object",
|
|
"properties": {
|
|
"avatar_url": {
|
|
"type": "string",
|
|
"example": "https://cdn.example.com/c/ace.png"
|
|
},
|
|
"name": {
|
|
"type": "string",
|
|
"example": "Ace Racing"
|
|
}
|
|
}
|
|
},
|
|
"transport.Driver": {
|
|
"type": "object",
|
|
"properties": {
|
|
"avatar_url": {
|
|
"type": "string",
|
|
"example": "https://cdn.example.com/u/ace.png"
|
|
},
|
|
"clan_id": {
|
|
"type": "string",
|
|
"example": "clan-1782000-1"
|
|
},
|
|
"created_ms": {
|
|
"type": "integer"
|
|
},
|
|
"id": {
|
|
"type": "string",
|
|
"example": "driver-1782000-123"
|
|
},
|
|
"name": {
|
|
"type": "string",
|
|
"example": "Alice"
|
|
},
|
|
"nickname": {
|
|
"type": "string",
|
|
"example": "ACE"
|
|
},
|
|
"updated_ms": {
|
|
"type": "integer"
|
|
}
|
|
}
|
|
},
|
|
"transport.DriverCreateRequest": {
|
|
"type": "object",
|
|
"properties": {
|
|
"avatar_url": {
|
|
"type": "string",
|
|
"example": "https://cdn.example.com/u/ace.png"
|
|
},
|
|
"clan_id": {
|
|
"type": "string",
|
|
"example": "clan-1782000-1"
|
|
},
|
|
"id": {
|
|
"type": "string",
|
|
"example": "driver-1782000-123"
|
|
},
|
|
"name": {
|
|
"type": "string",
|
|
"example": "Alice"
|
|
},
|
|
"nickname": {
|
|
"type": "string",
|
|
"example": "ACE"
|
|
}
|
|
}
|
|
},
|
|
"transport.DriverListResponse": {
|
|
"type": "object",
|
|
"properties": {
|
|
"count": {
|
|
"type": "integer"
|
|
},
|
|
"items": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/definitions/transport.Driver"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"transport.DriverUpdateRequest": {
|
|
"type": "object",
|
|
"properties": {
|
|
"avatar_url": {
|
|
"type": "string",
|
|
"example": "https://cdn.example.com/u/ace.png"
|
|
},
|
|
"clan_id": {
|
|
"type": "string",
|
|
"example": "clan-1782000-1"
|
|
},
|
|
"name": {
|
|
"type": "string",
|
|
"example": "Alice"
|
|
}
|
|
}
|
|
},
|
|
"transport.Envelope": {
|
|
"type": "object",
|
|
"properties": {
|
|
"payload": {},
|
|
"seq": {
|
|
"type": "integer"
|
|
},
|
|
"ts_ms": {
|
|
"type": "integer"
|
|
},
|
|
"type": {
|
|
"$ref": "#/definitions/transport.MessageType"
|
|
}
|
|
}
|
|
},
|
|
"transport.LeaderboardEntry": {
|
|
"type": "object",
|
|
"properties": {
|
|
"avatar_url": {
|
|
"type": "string",
|
|
"example": "https://cdn.example.com/u/ace.png"
|
|
},
|
|
"best_pos": {
|
|
"description": "Only populated for track-specific leaderboard",
|
|
"type": "integer",
|
|
"example": 1
|
|
},
|
|
"best_time_ms": {
|
|
"type": "integer",
|
|
"example": 15000
|
|
},
|
|
"clan_id": {
|
|
"type": "string",
|
|
"example": "clan-seed-001"
|
|
},
|
|
"clan_tag": {
|
|
"type": "string",
|
|
"example": "ACE"
|
|
},
|
|
"driver_id": {
|
|
"type": "string",
|
|
"example": "driver-seed-001"
|
|
},
|
|
"name": {
|
|
"type": "string",
|
|
"example": "Alice"
|
|
},
|
|
"nickname": {
|
|
"type": "string",
|
|
"example": "ACE"
|
|
},
|
|
"points": {
|
|
"type": "integer",
|
|
"example": 25
|
|
},
|
|
"rank": {
|
|
"type": "integer",
|
|
"example": 1
|
|
}
|
|
}
|
|
},
|
|
"transport.LeaderboardResponse": {
|
|
"type": "object",
|
|
"properties": {
|
|
"current_driver": {
|
|
"$ref": "#/definitions/transport.LeaderboardEntry"
|
|
},
|
|
"items": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/definitions/transport.LeaderboardEntry"
|
|
}
|
|
},
|
|
"limit": {
|
|
"type": "integer",
|
|
"example": 50
|
|
},
|
|
"offset": {
|
|
"type": "integer",
|
|
"example": 0
|
|
},
|
|
"total": {
|
|
"type": "integer",
|
|
"example": 8
|
|
}
|
|
}
|
|
},
|
|
"transport.LobbyRace": {
|
|
"type": "object",
|
|
"properties": {
|
|
"created_ms": {
|
|
"type": "integer"
|
|
},
|
|
"driver_ids": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"id": {
|
|
"type": "string"
|
|
},
|
|
"laps": {
|
|
"type": "integer"
|
|
},
|
|
"max_cars": {
|
|
"type": "integer"
|
|
},
|
|
"name": {
|
|
"type": "string"
|
|
},
|
|
"podium": {
|
|
"description": "Podium is populated only for finished races. Empty for live ones.",
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/definitions/transport.RacePodiumEntry"
|
|
}
|
|
},
|
|
"started_ms": {
|
|
"type": "integer"
|
|
},
|
|
"status": {
|
|
"type": "string"
|
|
},
|
|
"time_limit_s": {
|
|
"type": "integer"
|
|
},
|
|
"track_id": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
},
|
|
"transport.MessageType": {
|
|
"type": "string",
|
|
"enum": [
|
|
"client_hello",
|
|
"join_race",
|
|
"leave_race",
|
|
"input_state",
|
|
"chat_message",
|
|
"ping",
|
|
"lobby_list",
|
|
"lobby_create",
|
|
"lobby_quick_play",
|
|
"lobby_join_race",
|
|
"lobby_leave_race",
|
|
"lobby_kick_driver",
|
|
"lobby_delete",
|
|
"track_list",
|
|
"track_get",
|
|
"track_create",
|
|
"track_update",
|
|
"track_delete",
|
|
"car_list",
|
|
"car_get",
|
|
"car_create",
|
|
"car_update",
|
|
"car_delete",
|
|
"stats_request",
|
|
"server_hello",
|
|
"race_snapshot",
|
|
"input_ack",
|
|
"correction",
|
|
"race_event",
|
|
"pong",
|
|
"error",
|
|
"lobby_snapshot",
|
|
"track_snapshot",
|
|
"track_ack",
|
|
"car_snapshot",
|
|
"car_ack",
|
|
"catalog_summary",
|
|
"stats_snapshot",
|
|
"driver_profile",
|
|
"lap_recorded",
|
|
"race_result",
|
|
"race_standings"
|
|
],
|
|
"x-enum-varnames": [
|
|
"TypeClientHello",
|
|
"TypeJoinRace",
|
|
"TypeLeaveRace",
|
|
"TypeInputState",
|
|
"TypeChatMessage",
|
|
"TypePing",
|
|
"TypeLobbyList",
|
|
"TypeLobbyCreate",
|
|
"TypeLobbyQuickPlay",
|
|
"TypeLobbyJoinRace",
|
|
"TypeLobbyLeaveRace",
|
|
"TypeLobbyKickDriver",
|
|
"TypeLobbyDelete",
|
|
"TypeTrackList",
|
|
"TypeTrackGet",
|
|
"TypeTrackCreate",
|
|
"TypeTrackUpdate",
|
|
"TypeTrackDelete",
|
|
"TypeCarList",
|
|
"TypeCarGet",
|
|
"TypeCarCreate",
|
|
"TypeCarUpdate",
|
|
"TypeCarDelete",
|
|
"TypeStatsRequest",
|
|
"TypeServerHello",
|
|
"TypeRaceSnapshot",
|
|
"TypeInputAck",
|
|
"TypeCorrection",
|
|
"TypeRaceEvent",
|
|
"TypePong",
|
|
"TypeError",
|
|
"TypeLobbySnapshot",
|
|
"TypeTrackSnapshot",
|
|
"TypeTrackAck",
|
|
"TypeCarSnapshot",
|
|
"TypeCarAck",
|
|
"TypeCatalogSummary",
|
|
"TypeStatsSnapshot",
|
|
"TypeDriverProfile",
|
|
"TypeLapRecorded",
|
|
"TypeRaceResult",
|
|
"TypeRaceStandings"
|
|
]
|
|
},
|
|
"transport.MotorWire": {
|
|
"type": "object",
|
|
"properties": {
|
|
"class": {
|
|
"type": "string"
|
|
},
|
|
"kind": {
|
|
"type": "string"
|
|
},
|
|
"kv": {
|
|
"type": "integer"
|
|
},
|
|
"power_w": {
|
|
"type": "number"
|
|
}
|
|
}
|
|
},
|
|
"transport.RaceListResponse": {
|
|
"type": "object",
|
|
"properties": {
|
|
"count": {
|
|
"type": "integer"
|
|
},
|
|
"items": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/definitions/transport.LobbyRace"
|
|
}
|
|
},
|
|
"next_cursor": {
|
|
"type": "string",
|
|
"example": "MTcwMDAwMDAwMDAwMDpyYWNlLWFiYzpmaW5pc2hlZA"
|
|
}
|
|
}
|
|
},
|
|
"transport.RacePlan": {
|
|
"type": "object",
|
|
"properties": {
|
|
"count": {
|
|
"type": "integer",
|
|
"example": 0
|
|
},
|
|
"created_ms": {
|
|
"type": "integer"
|
|
},
|
|
"enabled": {
|
|
"type": "boolean",
|
|
"example": true
|
|
},
|
|
"fires_done": {
|
|
"type": "integer"
|
|
},
|
|
"id": {
|
|
"type": "string",
|
|
"example": "plan-1730000-1"
|
|
},
|
|
"interval_s": {
|
|
"type": "integer",
|
|
"example": 3600
|
|
},
|
|
"laps": {
|
|
"type": "integer",
|
|
"example": 10
|
|
},
|
|
"max_cars": {
|
|
"type": "integer",
|
|
"example": 4
|
|
},
|
|
"name": {
|
|
"type": "string",
|
|
"example": "Daily Monza"
|
|
},
|
|
"next_fire_ms": {
|
|
"type": "integer"
|
|
},
|
|
"start_at_ms": {
|
|
"type": "integer",
|
|
"example": 1730000000000
|
|
},
|
|
"time_limit_s": {
|
|
"type": "integer"
|
|
},
|
|
"track_id": {
|
|
"type": "string",
|
|
"example": "default"
|
|
},
|
|
"updated_ms": {
|
|
"type": "integer"
|
|
}
|
|
}
|
|
},
|
|
"transport.RacePlanCreateRequest": {
|
|
"type": "object",
|
|
"properties": {
|
|
"count": {
|
|
"type": "integer",
|
|
"example": 0
|
|
},
|
|
"enabled": {
|
|
"type": "boolean",
|
|
"example": true
|
|
},
|
|
"id": {
|
|
"type": "string",
|
|
"example": "plan-1730000-1"
|
|
},
|
|
"interval_s": {
|
|
"type": "integer",
|
|
"example": 3600
|
|
},
|
|
"laps": {
|
|
"type": "integer",
|
|
"example": 10
|
|
},
|
|
"max_cars": {
|
|
"type": "integer",
|
|
"example": 4
|
|
},
|
|
"name": {
|
|
"type": "string",
|
|
"example": "Daily Monza"
|
|
},
|
|
"start_at_ms": {
|
|
"type": "integer",
|
|
"example": 1730000000000
|
|
},
|
|
"time_limit_s": {
|
|
"type": "integer"
|
|
},
|
|
"track_id": {
|
|
"type": "string",
|
|
"example": "default"
|
|
}
|
|
}
|
|
},
|
|
"transport.RacePlanListResponse": {
|
|
"type": "object",
|
|
"properties": {
|
|
"count": {
|
|
"type": "integer"
|
|
},
|
|
"items": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/definitions/transport.RacePlan"
|
|
}
|
|
},
|
|
"next_cursor": {
|
|
"type": "string"
|
|
}
|
|
}
|
|
},
|
|
"transport.RacePodiumEntry": {
|
|
"type": "object",
|
|
"properties": {
|
|
"avatar_url": {
|
|
"type": "string"
|
|
},
|
|
"clan_id": {
|
|
"type": "string",
|
|
"example": "clan-bob"
|
|
},
|
|
"driver_id": {
|
|
"type": "string",
|
|
"example": "driver-alice"
|
|
},
|
|
"name": {
|
|
"type": "string",
|
|
"example": "driver-alice"
|
|
},
|
|
"nickname": {
|
|
"type": "string",
|
|
"example": "driver-alice"
|
|
},
|
|
"position": {
|
|
"type": "integer",
|
|
"example": 1
|
|
},
|
|
"total_time_ms": {
|
|
"type": "integer",
|
|
"example": 123456
|
|
}
|
|
}
|
|
},
|
|
"transport.RaceQueueEntry": {
|
|
"type": "object",
|
|
"properties": {
|
|
"driver_id": {
|
|
"type": "string",
|
|
"example": "driver-42"
|
|
},
|
|
"enqueued_ms": {
|
|
"type": "integer",
|
|
"example": 1730000000000
|
|
},
|
|
"plan_id": {
|
|
"type": "string",
|
|
"example": "plan-1730000-1"
|
|
},
|
|
"race_id": {
|
|
"type": "string",
|
|
"example": "race-1730000-7"
|
|
}
|
|
}
|
|
},
|
|
"transport.RaceQueueJoinRequest": {
|
|
"type": "object",
|
|
"properties": {
|
|
"driver_id": {
|
|
"type": "string",
|
|
"example": "driver-42"
|
|
},
|
|
"limit": {
|
|
"type": "integer",
|
|
"example": 3
|
|
}
|
|
}
|
|
},
|
|
"transport.RaceQueueJoinResponse": {
|
|
"type": "object",
|
|
"properties": {
|
|
"joined": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/definitions/transport.RaceQueueEntry"
|
|
}
|
|
},
|
|
"skipped": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "string"
|
|
},
|
|
"example": [
|
|
"race-1",
|
|
"race-2"
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"transport.RaceQueueListResponse": {
|
|
"type": "object",
|
|
"properties": {
|
|
"count": {
|
|
"type": "integer"
|
|
},
|
|
"items": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/definitions/transport.RaceQueueEntry"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"transport.RaceUpcomingItem": {
|
|
"type": "object",
|
|
"properties": {
|
|
"meta": {
|
|
"$ref": "#/definitions/transport.LobbyRace"
|
|
},
|
|
"plan_id": {
|
|
"type": "string"
|
|
},
|
|
"queue_len": {
|
|
"type": "integer"
|
|
},
|
|
"start_at_ms": {
|
|
"type": "integer"
|
|
}
|
|
}
|
|
},
|
|
"transport.RaceUpcomingResponse": {
|
|
"type": "object",
|
|
"properties": {
|
|
"count": {
|
|
"type": "integer"
|
|
},
|
|
"items": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/definitions/transport.RaceUpcomingItem"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"transport.TrackAck": {
|
|
"type": "object",
|
|
"properties": {
|
|
"error": {
|
|
"type": "string"
|
|
},
|
|
"id": {
|
|
"type": "string"
|
|
},
|
|
"ok": {
|
|
"type": "boolean"
|
|
},
|
|
"track": {
|
|
"$ref": "#/definitions/transport.TrackWire"
|
|
}
|
|
}
|
|
},
|
|
"transport.TrackBounds": {
|
|
"type": "object",
|
|
"properties": {
|
|
"length_m": {
|
|
"type": "number"
|
|
},
|
|
"max_x": {
|
|
"type": "number"
|
|
},
|
|
"max_y": {
|
|
"type": "number"
|
|
},
|
|
"min_x": {
|
|
"type": "number"
|
|
},
|
|
"min_y": {
|
|
"type": "number"
|
|
},
|
|
"width_m": {
|
|
"type": "number"
|
|
}
|
|
}
|
|
},
|
|
"transport.TrackCalendarCreateRequest": {
|
|
"type": "object",
|
|
"properties": {
|
|
"end_at": {
|
|
"type": "string",
|
|
"example": "2026-07-30T00:00:00Z"
|
|
},
|
|
"start_at": {
|
|
"type": "string",
|
|
"example": "2026-07-15T00:00:00Z"
|
|
},
|
|
"track_id": {
|
|
"type": "string",
|
|
"example": "monaco"
|
|
}
|
|
}
|
|
},
|
|
"transport.TrackCalendarEntry": {
|
|
"type": "object",
|
|
"properties": {
|
|
"end_at": {
|
|
"type": "string",
|
|
"example": "2026-07-30T00:00:00Z"
|
|
},
|
|
"id": {
|
|
"type": "integer",
|
|
"example": 1
|
|
},
|
|
"start_at": {
|
|
"type": "string",
|
|
"example": "2026-07-15T00:00:00Z"
|
|
},
|
|
"track_id": {
|
|
"type": "string",
|
|
"example": "monaco"
|
|
}
|
|
}
|
|
},
|
|
"transport.TrackCalendarLeaderboard": {
|
|
"type": "object",
|
|
"properties": {
|
|
"current_driver": {
|
|
"$ref": "#/definitions/transport.LeaderboardEntry"
|
|
},
|
|
"end_at": {
|
|
"type": "string",
|
|
"example": "2026-07-30T00:00:00Z"
|
|
},
|
|
"podium": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/definitions/transport.LeaderboardEntry"
|
|
}
|
|
},
|
|
"start_at": {
|
|
"type": "string",
|
|
"example": "2026-07-15T00:00:00Z"
|
|
},
|
|
"status": {
|
|
"type": "string",
|
|
"example": "active"
|
|
},
|
|
"track_id": {
|
|
"type": "string",
|
|
"example": "monaco"
|
|
},
|
|
"track_name": {
|
|
"type": "string",
|
|
"example": "Monaco"
|
|
}
|
|
}
|
|
},
|
|
"transport.TrackCalendarLeaderboardResponse": {
|
|
"type": "object",
|
|
"properties": {
|
|
"items": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/definitions/transport.TrackCalendarLeaderboard"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"transport.TrackCalendarResponse": {
|
|
"type": "object",
|
|
"properties": {
|
|
"items": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/definitions/transport.TrackCalendarEntry"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"transport.TrackCreateRequest": {
|
|
"type": "object",
|
|
"properties": {
|
|
"track": {
|
|
"$ref": "#/definitions/transport.TrackWire"
|
|
}
|
|
}
|
|
},
|
|
"transport.TrackPatch": {
|
|
"type": "object",
|
|
"properties": {
|
|
"centerline": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/definitions/transport.TrackWaypoint"
|
|
}
|
|
},
|
|
"description": {
|
|
"type": "string"
|
|
},
|
|
"lane_width_m": {
|
|
"type": "number"
|
|
},
|
|
"length_m": {
|
|
"type": "number"
|
|
},
|
|
"name": {
|
|
"type": "string"
|
|
},
|
|
"scale": {
|
|
"type": "integer"
|
|
},
|
|
"surface": {
|
|
"type": "string"
|
|
},
|
|
"tags": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"visibility": {
|
|
"type": "string"
|
|
},
|
|
"width_m": {
|
|
"type": "number"
|
|
}
|
|
}
|
|
},
|
|
"transport.TrackUpdateRequest": {
|
|
"type": "object",
|
|
"properties": {
|
|
"id": {
|
|
"type": "string"
|
|
},
|
|
"patch": {
|
|
"$ref": "#/definitions/transport.TrackPatch"
|
|
}
|
|
}
|
|
},
|
|
"transport.TrackWaypoint": {
|
|
"type": "object",
|
|
"properties": {
|
|
"curvature": {
|
|
"type": "number"
|
|
},
|
|
"heading_rad": {
|
|
"type": "number"
|
|
},
|
|
"speed_ms": {
|
|
"type": "number"
|
|
},
|
|
"x": {
|
|
"type": "number"
|
|
},
|
|
"y": {
|
|
"type": "number"
|
|
}
|
|
}
|
|
},
|
|
"transport.TrackWire": {
|
|
"type": "object",
|
|
"properties": {
|
|
"author_id": {
|
|
"type": "string"
|
|
},
|
|
"best_lap_holder": {
|
|
"type": "string"
|
|
},
|
|
"best_lap_ms": {
|
|
"type": "integer"
|
|
},
|
|
"bounds": {
|
|
"$ref": "#/definitions/transport.TrackBounds"
|
|
},
|
|
"centerline": {
|
|
"type": "array",
|
|
"items": {
|
|
"$ref": "#/definitions/transport.TrackWaypoint"
|
|
}
|
|
},
|
|
"created_ms": {
|
|
"type": "integer"
|
|
},
|
|
"description": {
|
|
"type": "string"
|
|
},
|
|
"id": {
|
|
"type": "string"
|
|
},
|
|
"is_active": {
|
|
"type": "boolean",
|
|
"example": true
|
|
},
|
|
"lane_width_m": {
|
|
"type": "number"
|
|
},
|
|
"length_m": {
|
|
"type": "number"
|
|
},
|
|
"name": {
|
|
"type": "string"
|
|
},
|
|
"scale": {
|
|
"type": "integer"
|
|
},
|
|
"surface": {
|
|
"type": "string"
|
|
},
|
|
"tags": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"updated_ms": {
|
|
"type": "integer"
|
|
},
|
|
"visibility": {
|
|
"type": "string"
|
|
},
|
|
"width_m": {
|
|
"type": "number"
|
|
}
|
|
}
|
|
},
|
|
"transport.VersionResponse": {
|
|
"type": "object",
|
|
"properties": {
|
|
"version": {
|
|
"type": "string",
|
|
"example": "0.1.0-poc"
|
|
}
|
|
}
|
|
},
|
|
"transport.WebRTCConnectRequest": {
|
|
"type": "object",
|
|
"properties": {
|
|
"driver_id": {
|
|
"type": "string",
|
|
"example": "driver-alice"
|
|
},
|
|
"sdp": {
|
|
"type": "string"
|
|
},
|
|
"type": {
|
|
"type": "string",
|
|
"example": "offer"
|
|
}
|
|
}
|
|
},
|
|
"transport.WebRTCConnectResponse": {
|
|
"type": "object",
|
|
"properties": {
|
|
"sdp": {
|
|
"type": "string"
|
|
},
|
|
"type": {
|
|
"type": "string",
|
|
"example": "answer"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}`
|
|
|
|
// SwaggerInfo holds exported Swagger Info so clients can modify it
|
|
var SwaggerInfo = &swag.Spec{
|
|
Version: "0.1.0-poc",
|
|
Host: "localhost:8080",
|
|
BasePath: "/",
|
|
Schemes: []string{"http", "ws"},
|
|
Title: "x0gp PoC server API",
|
|
Description: "Minimal HTTP+WebSocket server for the x0gp RC racing PoC. REST endpoints under /api/* manage the track/car catalog; /health and /stats expose server liveness and realtime metrics; /ws carries the realtime race protocol.\nAuth is intentionally disabled in PoC mode (X0GP_DEV_MODE=true). All catalog mutations are allowed without a JWT.",
|
|
InfoInstanceName: "swagger",
|
|
SwaggerTemplate: docTemplate,
|
|
LeftDelim: "{{",
|
|
RightDelim: "}}",
|
|
}
|
|
|
|
func init() {
|
|
swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
|
|
}
|