1409 lines
34 KiB
JSON
1409 lines
34 KiB
JSON
|
|
{
|
||
|
|
"openapi": "3.0.3",
|
||
|
|
"info": {
|
||
|
|
"title": "Shooting Event API",
|
||
|
|
"description": "REST API for live scoring, admin control, proof images, and AI score advice.",
|
||
|
|
"version": "1.0.0"
|
||
|
|
},
|
||
|
|
"servers": [
|
||
|
|
{
|
||
|
|
"url": "/api",
|
||
|
|
"description": "Same host API base path"
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"tags": [
|
||
|
|
{
|
||
|
|
"name": "Public",
|
||
|
|
"description": "Public endpoints available without admin authentication"
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"name": "Admin",
|
||
|
|
"description": "Admin endpoints protected by bearer token"
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"paths": {
|
||
|
|
"/health": {
|
||
|
|
"get": {
|
||
|
|
"tags": [
|
||
|
|
"Public"
|
||
|
|
],
|
||
|
|
"summary": "Health check",
|
||
|
|
"operationId": "getHealth",
|
||
|
|
"responses": {
|
||
|
|
"200": {
|
||
|
|
"description": "Service is healthy",
|
||
|
|
"content": {
|
||
|
|
"application/json": {
|
||
|
|
"schema": {
|
||
|
|
"$ref": "#/components/schemas/HealthResponse"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"/state": {
|
||
|
|
"get": {
|
||
|
|
"tags": [
|
||
|
|
"Public"
|
||
|
|
],
|
||
|
|
"summary": "Get application state",
|
||
|
|
"description": "Returns public state by default. If a valid admin bearer token is sent, `scoreProofs` is included.",
|
||
|
|
"operationId": "getState",
|
||
|
|
"security": [
|
||
|
|
{},
|
||
|
|
{
|
||
|
|
"bearerAuth": []
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"responses": {
|
||
|
|
"200": {
|
||
|
|
"description": "Current application state",
|
||
|
|
"content": {
|
||
|
|
"application/json": {
|
||
|
|
"schema": {
|
||
|
|
"$ref": "#/components/schemas/StateResponse"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"500": {
|
||
|
|
"$ref": "#/components/responses/InternalServerError"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"/events": {
|
||
|
|
"get": {
|
||
|
|
"tags": [
|
||
|
|
"Public"
|
||
|
|
],
|
||
|
|
"summary": "Live update stream (SSE)",
|
||
|
|
"description": "Server-Sent Events stream. Emits `ready` and `state` events plus periodic keepalive pings.",
|
||
|
|
"operationId": "streamEvents",
|
||
|
|
"responses": {
|
||
|
|
"200": {
|
||
|
|
"description": "Event stream opened",
|
||
|
|
"content": {
|
||
|
|
"text/event-stream": {
|
||
|
|
"schema": {
|
||
|
|
"type": "string",
|
||
|
|
"example": "event: ready\\ndata: {\"ts\":\"2026-04-28T10:00:00Z\"}\\n\\n"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"500": {
|
||
|
|
"$ref": "#/components/responses/InternalServerError"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"/admin/login": {
|
||
|
|
"post": {
|
||
|
|
"tags": [
|
||
|
|
"Admin"
|
||
|
|
],
|
||
|
|
"summary": "Admin login",
|
||
|
|
"description": "Authenticates admin credentials and returns a bearer token valid for 8 hours.",
|
||
|
|
"operationId": "adminLogin",
|
||
|
|
"requestBody": {
|
||
|
|
"required": true,
|
||
|
|
"content": {
|
||
|
|
"application/json": {
|
||
|
|
"schema": {
|
||
|
|
"$ref": "#/components/schemas/AdminLoginRequest"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"responses": {
|
||
|
|
"200": {
|
||
|
|
"description": "Login successful",
|
||
|
|
"content": {
|
||
|
|
"application/json": {
|
||
|
|
"schema": {
|
||
|
|
"$ref": "#/components/schemas/AdminLoginResponse"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"400": {
|
||
|
|
"$ref": "#/components/responses/BadRequest"
|
||
|
|
},
|
||
|
|
"401": {
|
||
|
|
"$ref": "#/components/responses/Unauthorized"
|
||
|
|
},
|
||
|
|
"500": {
|
||
|
|
"$ref": "#/components/responses/InternalServerError"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"/admin/logout": {
|
||
|
|
"post": {
|
||
|
|
"tags": [
|
||
|
|
"Admin"
|
||
|
|
],
|
||
|
|
"summary": "Admin logout",
|
||
|
|
"operationId": "adminLogout",
|
||
|
|
"security": [
|
||
|
|
{
|
||
|
|
"bearerAuth": []
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"responses": {
|
||
|
|
"200": {
|
||
|
|
"description": "Token invalidated",
|
||
|
|
"content": {
|
||
|
|
"application/json": {
|
||
|
|
"schema": {
|
||
|
|
"$ref": "#/components/schemas/OkResponse"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"401": {
|
||
|
|
"$ref": "#/components/responses/Unauthorized"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"/admin/state": {
|
||
|
|
"get": {
|
||
|
|
"tags": [
|
||
|
|
"Admin"
|
||
|
|
],
|
||
|
|
"summary": "Get full admin state",
|
||
|
|
"description": "Same shape as `/state`, always includes admin-visible fields such as `scoreProofs`.",
|
||
|
|
"operationId": "getAdminState",
|
||
|
|
"security": [
|
||
|
|
{
|
||
|
|
"bearerAuth": []
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"responses": {
|
||
|
|
"200": {
|
||
|
|
"description": "Full admin state",
|
||
|
|
"content": {
|
||
|
|
"application/json": {
|
||
|
|
"schema": {
|
||
|
|
"$ref": "#/components/schemas/StateResponse"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"401": {
|
||
|
|
"$ref": "#/components/responses/Unauthorized"
|
||
|
|
},
|
||
|
|
"500": {
|
||
|
|
"$ref": "#/components/responses/InternalServerError"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"/admin/settings": {
|
||
|
|
"put": {
|
||
|
|
"tags": [
|
||
|
|
"Admin"
|
||
|
|
],
|
||
|
|
"summary": "Update app settings",
|
||
|
|
"operationId": "updateAdminSettings",
|
||
|
|
"security": [
|
||
|
|
{
|
||
|
|
"bearerAuth": []
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"requestBody": {
|
||
|
|
"required": true,
|
||
|
|
"content": {
|
||
|
|
"application/json": {
|
||
|
|
"schema": {
|
||
|
|
"$ref": "#/components/schemas/AdminSettingsUpdateRequest"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"responses": {
|
||
|
|
"200": {
|
||
|
|
"description": "Updated state",
|
||
|
|
"content": {
|
||
|
|
"application/json": {
|
||
|
|
"schema": {
|
||
|
|
"$ref": "#/components/schemas/StateResponse"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"400": {
|
||
|
|
"$ref": "#/components/responses/BadRequest"
|
||
|
|
},
|
||
|
|
"401": {
|
||
|
|
"$ref": "#/components/responses/Unauthorized"
|
||
|
|
},
|
||
|
|
"500": {
|
||
|
|
"$ref": "#/components/responses/InternalServerError"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"/admin/players": {
|
||
|
|
"post": {
|
||
|
|
"tags": [
|
||
|
|
"Admin"
|
||
|
|
],
|
||
|
|
"summary": "Create player",
|
||
|
|
"operationId": "createPlayer",
|
||
|
|
"security": [
|
||
|
|
{
|
||
|
|
"bearerAuth": []
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"requestBody": {
|
||
|
|
"required": true,
|
||
|
|
"content": {
|
||
|
|
"application/json": {
|
||
|
|
"schema": {
|
||
|
|
"$ref": "#/components/schemas/PlayerCreateRequest"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"responses": {
|
||
|
|
"201": {
|
||
|
|
"description": "Player created; full updated state returned",
|
||
|
|
"content": {
|
||
|
|
"application/json": {
|
||
|
|
"schema": {
|
||
|
|
"$ref": "#/components/schemas/StateResponse"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"400": {
|
||
|
|
"$ref": "#/components/responses/BadRequest"
|
||
|
|
},
|
||
|
|
"401": {
|
||
|
|
"$ref": "#/components/responses/Unauthorized"
|
||
|
|
},
|
||
|
|
"500": {
|
||
|
|
"$ref": "#/components/responses/InternalServerError"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"/admin/players/auto-group": {
|
||
|
|
"post": {
|
||
|
|
"tags": [
|
||
|
|
"Admin"
|
||
|
|
],
|
||
|
|
"summary": "Auto-assign player groups",
|
||
|
|
"description": "Randomly shuffles players and assigns group codes in round-robin order.",
|
||
|
|
"operationId": "autoGroupPlayers",
|
||
|
|
"security": [
|
||
|
|
{
|
||
|
|
"bearerAuth": []
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"requestBody": {
|
||
|
|
"required": true,
|
||
|
|
"content": {
|
||
|
|
"application/json": {
|
||
|
|
"schema": {
|
||
|
|
"$ref": "#/components/schemas/AutoGroupPlayersRequest"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"responses": {
|
||
|
|
"200": {
|
||
|
|
"description": "Updated state",
|
||
|
|
"content": {
|
||
|
|
"application/json": {
|
||
|
|
"schema": {
|
||
|
|
"$ref": "#/components/schemas/StateResponse"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"400": {
|
||
|
|
"$ref": "#/components/responses/BadRequest"
|
||
|
|
},
|
||
|
|
"401": {
|
||
|
|
"$ref": "#/components/responses/Unauthorized"
|
||
|
|
},
|
||
|
|
"500": {
|
||
|
|
"$ref": "#/components/responses/InternalServerError"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"/admin/players/{id}": {
|
||
|
|
"put": {
|
||
|
|
"tags": [
|
||
|
|
"Admin"
|
||
|
|
],
|
||
|
|
"summary": "Update player",
|
||
|
|
"operationId": "updatePlayer",
|
||
|
|
"security": [
|
||
|
|
{
|
||
|
|
"bearerAuth": []
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"parameters": [
|
||
|
|
{
|
||
|
|
"$ref": "#/components/parameters/PlayerIdParam"
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"requestBody": {
|
||
|
|
"required": true,
|
||
|
|
"content": {
|
||
|
|
"application/json": {
|
||
|
|
"schema": {
|
||
|
|
"$ref": "#/components/schemas/PlayerUpdateRequest"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"responses": {
|
||
|
|
"200": {
|
||
|
|
"description": "Updated state",
|
||
|
|
"content": {
|
||
|
|
"application/json": {
|
||
|
|
"schema": {
|
||
|
|
"$ref": "#/components/schemas/StateResponse"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"400": {
|
||
|
|
"$ref": "#/components/responses/BadRequest"
|
||
|
|
},
|
||
|
|
"401": {
|
||
|
|
"$ref": "#/components/responses/Unauthorized"
|
||
|
|
},
|
||
|
|
"404": {
|
||
|
|
"$ref": "#/components/responses/NotFound"
|
||
|
|
},
|
||
|
|
"500": {
|
||
|
|
"$ref": "#/components/responses/InternalServerError"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"delete": {
|
||
|
|
"tags": [
|
||
|
|
"Admin"
|
||
|
|
],
|
||
|
|
"summary": "Delete player",
|
||
|
|
"operationId": "deletePlayer",
|
||
|
|
"security": [
|
||
|
|
{
|
||
|
|
"bearerAuth": []
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"parameters": [
|
||
|
|
{
|
||
|
|
"$ref": "#/components/parameters/PlayerIdParam"
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"responses": {
|
||
|
|
"200": {
|
||
|
|
"description": "Updated state",
|
||
|
|
"content": {
|
||
|
|
"application/json": {
|
||
|
|
"schema": {
|
||
|
|
"$ref": "#/components/schemas/StateResponse"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"400": {
|
||
|
|
"$ref": "#/components/responses/BadRequest"
|
||
|
|
},
|
||
|
|
"401": {
|
||
|
|
"$ref": "#/components/responses/Unauthorized"
|
||
|
|
},
|
||
|
|
"404": {
|
||
|
|
"$ref": "#/components/responses/NotFound"
|
||
|
|
},
|
||
|
|
"500": {
|
||
|
|
"$ref": "#/components/responses/InternalServerError"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"/admin/scores/{stage}/{id}": {
|
||
|
|
"put": {
|
||
|
|
"tags": [
|
||
|
|
"Admin"
|
||
|
|
],
|
||
|
|
"summary": "Set a score",
|
||
|
|
"description": "Upserts score for a given stage/player. Score must be 0..100.",
|
||
|
|
"operationId": "updateScore",
|
||
|
|
"security": [
|
||
|
|
{
|
||
|
|
"bearerAuth": []
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"parameters": [
|
||
|
|
{
|
||
|
|
"$ref": "#/components/parameters/ScoreStageParam"
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"$ref": "#/components/parameters/PlayerIdParam"
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"requestBody": {
|
||
|
|
"required": true,
|
||
|
|
"content": {
|
||
|
|
"application/json": {
|
||
|
|
"schema": {
|
||
|
|
"$ref": "#/components/schemas/ScoreUpdateRequest"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"responses": {
|
||
|
|
"200": {
|
||
|
|
"description": "Updated state",
|
||
|
|
"content": {
|
||
|
|
"application/json": {
|
||
|
|
"schema": {
|
||
|
|
"$ref": "#/components/schemas/StateResponse"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"400": {
|
||
|
|
"$ref": "#/components/responses/BadRequest"
|
||
|
|
},
|
||
|
|
"401": {
|
||
|
|
"$ref": "#/components/responses/Unauthorized"
|
||
|
|
},
|
||
|
|
"500": {
|
||
|
|
"$ref": "#/components/responses/InternalServerError"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"/admin/scores/{stage}/{id}/proof": {
|
||
|
|
"put": {
|
||
|
|
"tags": [
|
||
|
|
"Admin"
|
||
|
|
],
|
||
|
|
"summary": "Upload score proof image",
|
||
|
|
"description": "Stores proof image data (typically a data URL string).",
|
||
|
|
"operationId": "updateScoreProof",
|
||
|
|
"security": [
|
||
|
|
{
|
||
|
|
"bearerAuth": []
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"parameters": [
|
||
|
|
{
|
||
|
|
"$ref": "#/components/parameters/ScoreStageParam"
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"$ref": "#/components/parameters/PlayerIdParam"
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"requestBody": {
|
||
|
|
"required": true,
|
||
|
|
"content": {
|
||
|
|
"application/json": {
|
||
|
|
"schema": {
|
||
|
|
"$ref": "#/components/schemas/ScoreProofUpdateRequest"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"responses": {
|
||
|
|
"200": {
|
||
|
|
"description": "Updated state",
|
||
|
|
"content": {
|
||
|
|
"application/json": {
|
||
|
|
"schema": {
|
||
|
|
"$ref": "#/components/schemas/StateResponse"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"400": {
|
||
|
|
"$ref": "#/components/responses/BadRequest"
|
||
|
|
},
|
||
|
|
"401": {
|
||
|
|
"$ref": "#/components/responses/Unauthorized"
|
||
|
|
},
|
||
|
|
"500": {
|
||
|
|
"$ref": "#/components/responses/InternalServerError"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"delete": {
|
||
|
|
"tags": [
|
||
|
|
"Admin"
|
||
|
|
],
|
||
|
|
"summary": "Delete score proof image",
|
||
|
|
"operationId": "deleteScoreProof",
|
||
|
|
"security": [
|
||
|
|
{
|
||
|
|
"bearerAuth": []
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"parameters": [
|
||
|
|
{
|
||
|
|
"$ref": "#/components/parameters/ScoreStageParam"
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"$ref": "#/components/parameters/PlayerIdParam"
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"responses": {
|
||
|
|
"200": {
|
||
|
|
"description": "Updated state",
|
||
|
|
"content": {
|
||
|
|
"application/json": {
|
||
|
|
"schema": {
|
||
|
|
"$ref": "#/components/schemas/StateResponse"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"400": {
|
||
|
|
"$ref": "#/components/responses/BadRequest"
|
||
|
|
},
|
||
|
|
"401": {
|
||
|
|
"$ref": "#/components/responses/Unauthorized"
|
||
|
|
},
|
||
|
|
"500": {
|
||
|
|
"$ref": "#/components/responses/InternalServerError"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"/admin/scores/{stage}/{id}/advice": {
|
||
|
|
"post": {
|
||
|
|
"tags": [
|
||
|
|
"Admin"
|
||
|
|
],
|
||
|
|
"summary": "Get AI score advice",
|
||
|
|
"description": "Uses configured Gemini model with uploaded proof image to propose a score and reason.",
|
||
|
|
"operationId": "getScoreAdvice",
|
||
|
|
"security": [
|
||
|
|
{
|
||
|
|
"bearerAuth": []
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"parameters": [
|
||
|
|
{
|
||
|
|
"$ref": "#/components/parameters/ScoreStageParam"
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"$ref": "#/components/parameters/PlayerIdParam"
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"responses": {
|
||
|
|
"200": {
|
||
|
|
"description": "Advice generated",
|
||
|
|
"content": {
|
||
|
|
"application/json": {
|
||
|
|
"schema": {
|
||
|
|
"$ref": "#/components/schemas/ScoreAdviceResponse"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"400": {
|
||
|
|
"$ref": "#/components/responses/BadRequest"
|
||
|
|
},
|
||
|
|
"401": {
|
||
|
|
"$ref": "#/components/responses/Unauthorized"
|
||
|
|
},
|
||
|
|
"502": {
|
||
|
|
"description": "AI upstream returned an error",
|
||
|
|
"content": {
|
||
|
|
"application/json": {
|
||
|
|
"schema": {
|
||
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"503": {
|
||
|
|
"description": "AI service not configured",
|
||
|
|
"content": {
|
||
|
|
"application/json": {
|
||
|
|
"schema": {
|
||
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"500": {
|
||
|
|
"$ref": "#/components/responses/InternalServerError"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"/admin/scores/{stage}/reset": {
|
||
|
|
"post": {
|
||
|
|
"tags": [
|
||
|
|
"Admin"
|
||
|
|
],
|
||
|
|
"summary": "Reset a stage family",
|
||
|
|
"description": "Resets scores to zero for one stage family or tie-break stage. Optionally removes proofs.",
|
||
|
|
"operationId": "resetStage",
|
||
|
|
"security": [
|
||
|
|
{
|
||
|
|
"bearerAuth": []
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"parameters": [
|
||
|
|
{
|
||
|
|
"$ref": "#/components/parameters/ResetStageParam"
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"requestBody": {
|
||
|
|
"required": false,
|
||
|
|
"content": {
|
||
|
|
"application/json": {
|
||
|
|
"schema": {
|
||
|
|
"$ref": "#/components/schemas/ResetStageRequest"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"responses": {
|
||
|
|
"200": {
|
||
|
|
"description": "Updated state",
|
||
|
|
"content": {
|
||
|
|
"application/json": {
|
||
|
|
"schema": {
|
||
|
|
"$ref": "#/components/schemas/StateResponse"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"400": {
|
||
|
|
"$ref": "#/components/responses/BadRequest"
|
||
|
|
},
|
||
|
|
"401": {
|
||
|
|
"$ref": "#/components/responses/Unauthorized"
|
||
|
|
},
|
||
|
|
"500": {
|
||
|
|
"$ref": "#/components/responses/InternalServerError"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"components": {
|
||
|
|
"securitySchemes": {
|
||
|
|
"bearerAuth": {
|
||
|
|
"type": "http",
|
||
|
|
"scheme": "bearer",
|
||
|
|
"bearerFormat": "Token"
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"parameters": {
|
||
|
|
"PlayerIdParam": {
|
||
|
|
"name": "id",
|
||
|
|
"in": "path",
|
||
|
|
"required": true,
|
||
|
|
"description": "Player ID",
|
||
|
|
"schema": {
|
||
|
|
"type": "integer",
|
||
|
|
"minimum": 1
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"ScoreStageParam": {
|
||
|
|
"name": "stage",
|
||
|
|
"in": "path",
|
||
|
|
"required": true,
|
||
|
|
"description": "Score stage key",
|
||
|
|
"schema": {
|
||
|
|
"type": "string",
|
||
|
|
"enum": [
|
||
|
|
"prelim_r1",
|
||
|
|
"prelim_r2",
|
||
|
|
"prelim_r3",
|
||
|
|
"final_r1",
|
||
|
|
"final_r2",
|
||
|
|
"prelim_tiebreak",
|
||
|
|
"final_tiebreak"
|
||
|
|
]
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"ResetStageParam": {
|
||
|
|
"name": "stage",
|
||
|
|
"in": "path",
|
||
|
|
"required": true,
|
||
|
|
"description": "Reset target",
|
||
|
|
"schema": {
|
||
|
|
"type": "string",
|
||
|
|
"enum": [
|
||
|
|
"preliminary",
|
||
|
|
"final",
|
||
|
|
"prelim_tiebreak",
|
||
|
|
"final_tiebreak"
|
||
|
|
]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"responses": {
|
||
|
|
"BadRequest": {
|
||
|
|
"description": "Invalid input",
|
||
|
|
"content": {
|
||
|
|
"application/json": {
|
||
|
|
"schema": {
|
||
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"Unauthorized": {
|
||
|
|
"description": "Missing or invalid bearer token",
|
||
|
|
"content": {
|
||
|
|
"application/json": {
|
||
|
|
"schema": {
|
||
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"NotFound": {
|
||
|
|
"description": "Resource not found",
|
||
|
|
"content": {
|
||
|
|
"application/json": {
|
||
|
|
"schema": {
|
||
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"InternalServerError": {
|
||
|
|
"description": "Internal server error",
|
||
|
|
"content": {
|
||
|
|
"application/json": {
|
||
|
|
"schema": {
|
||
|
|
"$ref": "#/components/schemas/ErrorResponse"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"schemas": {
|
||
|
|
"ErrorResponse": {
|
||
|
|
"type": "object",
|
||
|
|
"required": [
|
||
|
|
"message"
|
||
|
|
],
|
||
|
|
"properties": {
|
||
|
|
"message": {
|
||
|
|
"type": "string"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"OkResponse": {
|
||
|
|
"type": "object",
|
||
|
|
"required": [
|
||
|
|
"ok"
|
||
|
|
],
|
||
|
|
"properties": {
|
||
|
|
"ok": {
|
||
|
|
"type": "boolean"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"HealthResponse": {
|
||
|
|
"type": "object",
|
||
|
|
"required": [
|
||
|
|
"status"
|
||
|
|
],
|
||
|
|
"properties": {
|
||
|
|
"status": {
|
||
|
|
"type": "string",
|
||
|
|
"example": "ok"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"AdminLoginRequest": {
|
||
|
|
"type": "object",
|
||
|
|
"required": [
|
||
|
|
"username",
|
||
|
|
"password"
|
||
|
|
],
|
||
|
|
"properties": {
|
||
|
|
"username": {
|
||
|
|
"type": "string"
|
||
|
|
},
|
||
|
|
"password": {
|
||
|
|
"type": "string"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"AdminLoginResponse": {
|
||
|
|
"type": "object",
|
||
|
|
"required": [
|
||
|
|
"token",
|
||
|
|
"expiresAt",
|
||
|
|
"username"
|
||
|
|
],
|
||
|
|
"properties": {
|
||
|
|
"token": {
|
||
|
|
"type": "string"
|
||
|
|
},
|
||
|
|
"expiresAt": {
|
||
|
|
"type": "string",
|
||
|
|
"format": "date-time"
|
||
|
|
},
|
||
|
|
"username": {
|
||
|
|
"type": "string"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"CompetitionMeta": {
|
||
|
|
"type": "object",
|
||
|
|
"required": [
|
||
|
|
"titleAr",
|
||
|
|
"titleEn"
|
||
|
|
],
|
||
|
|
"properties": {
|
||
|
|
"titleAr": {
|
||
|
|
"type": "string"
|
||
|
|
},
|
||
|
|
"titleEn": {
|
||
|
|
"type": "string"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"Player": {
|
||
|
|
"type": "object",
|
||
|
|
"required": [
|
||
|
|
"id",
|
||
|
|
"nameAr",
|
||
|
|
"nameEn",
|
||
|
|
"groupCode",
|
||
|
|
"imageData"
|
||
|
|
],
|
||
|
|
"properties": {
|
||
|
|
"id": {
|
||
|
|
"type": "integer"
|
||
|
|
},
|
||
|
|
"nameAr": {
|
||
|
|
"type": "string"
|
||
|
|
},
|
||
|
|
"nameEn": {
|
||
|
|
"type": "string"
|
||
|
|
},
|
||
|
|
"groupCode": {
|
||
|
|
"type": "string"
|
||
|
|
},
|
||
|
|
"imageData": {
|
||
|
|
"type": "string"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"RankingRow": {
|
||
|
|
"type": "object",
|
||
|
|
"required": [
|
||
|
|
"playerId",
|
||
|
|
"nameAr",
|
||
|
|
"nameEn",
|
||
|
|
"groupCode",
|
||
|
|
"imageData",
|
||
|
|
"score",
|
||
|
|
"tieBreak",
|
||
|
|
"rank",
|
||
|
|
"seed",
|
||
|
|
"finalGroup"
|
||
|
|
],
|
||
|
|
"properties": {
|
||
|
|
"playerId": {
|
||
|
|
"type": "integer"
|
||
|
|
},
|
||
|
|
"nameAr": {
|
||
|
|
"type": "string"
|
||
|
|
},
|
||
|
|
"nameEn": {
|
||
|
|
"type": "string"
|
||
|
|
},
|
||
|
|
"groupCode": {
|
||
|
|
"type": "string"
|
||
|
|
},
|
||
|
|
"imageData": {
|
||
|
|
"type": "string"
|
||
|
|
},
|
||
|
|
"score": {
|
||
|
|
"type": "integer"
|
||
|
|
},
|
||
|
|
"tieBreak": {
|
||
|
|
"type": "integer"
|
||
|
|
},
|
||
|
|
"rank": {
|
||
|
|
"type": "integer"
|
||
|
|
},
|
||
|
|
"seed": {
|
||
|
|
"type": "integer"
|
||
|
|
},
|
||
|
|
"finalGroup": {
|
||
|
|
"type": "integer"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"TieBreakInfo": {
|
||
|
|
"type": "object",
|
||
|
|
"required": [
|
||
|
|
"required",
|
||
|
|
"resolved",
|
||
|
|
"slots",
|
||
|
|
"playerIds"
|
||
|
|
],
|
||
|
|
"properties": {
|
||
|
|
"required": {
|
||
|
|
"type": "boolean"
|
||
|
|
},
|
||
|
|
"resolved": {
|
||
|
|
"type": "boolean"
|
||
|
|
},
|
||
|
|
"slots": {
|
||
|
|
"type": "integer"
|
||
|
|
},
|
||
|
|
"playerIds": {
|
||
|
|
"type": "array",
|
||
|
|
"items": {
|
||
|
|
"type": "integer"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"RankingBundle": {
|
||
|
|
"type": "object",
|
||
|
|
"required": [
|
||
|
|
"rows",
|
||
|
|
"tieBreak",
|
||
|
|
"unresolved"
|
||
|
|
],
|
||
|
|
"properties": {
|
||
|
|
"rows": {
|
||
|
|
"type": "array",
|
||
|
|
"items": {
|
||
|
|
"$ref": "#/components/schemas/RankingRow"
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"tieBreak": {
|
||
|
|
"$ref": "#/components/schemas/TieBreakInfo"
|
||
|
|
},
|
||
|
|
"unresolved": {
|
||
|
|
"type": "boolean"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"FinalGroups": {
|
||
|
|
"type": "object",
|
||
|
|
"required": [
|
||
|
|
"group1",
|
||
|
|
"group2"
|
||
|
|
],
|
||
|
|
"properties": {
|
||
|
|
"group1": {
|
||
|
|
"type": "array",
|
||
|
|
"items": {
|
||
|
|
"$ref": "#/components/schemas/RankingRow"
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"group2": {
|
||
|
|
"type": "array",
|
||
|
|
"items": {
|
||
|
|
"$ref": "#/components/schemas/RankingRow"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"DerivedState": {
|
||
|
|
"type": "object",
|
||
|
|
"required": [
|
||
|
|
"preliminaryRanking",
|
||
|
|
"finalists",
|
||
|
|
"finalGroups",
|
||
|
|
"finalRanking",
|
||
|
|
"podium"
|
||
|
|
],
|
||
|
|
"properties": {
|
||
|
|
"preliminaryRanking": {
|
||
|
|
"$ref": "#/components/schemas/RankingBundle"
|
||
|
|
},
|
||
|
|
"finalists": {
|
||
|
|
"type": "array",
|
||
|
|
"items": {
|
||
|
|
"$ref": "#/components/schemas/RankingRow"
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"finalGroups": {
|
||
|
|
"$ref": "#/components/schemas/FinalGroups"
|
||
|
|
},
|
||
|
|
"finalRanking": {
|
||
|
|
"$ref": "#/components/schemas/RankingBundle"
|
||
|
|
},
|
||
|
|
"podium": {
|
||
|
|
"type": "array",
|
||
|
|
"items": {
|
||
|
|
"$ref": "#/components/schemas/RankingRow"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"LiveModeSettings": {
|
||
|
|
"type": "object",
|
||
|
|
"required": [
|
||
|
|
"activeView",
|
||
|
|
"showGroupLive",
|
||
|
|
"groupDisplayMode",
|
||
|
|
"groupFixedCode",
|
||
|
|
"showPrelimTie",
|
||
|
|
"showPrelimOverall",
|
||
|
|
"showFinalGroups",
|
||
|
|
"finalDisplayMode",
|
||
|
|
"finalFixedGroup",
|
||
|
|
"showFinalTie",
|
||
|
|
"showPodium",
|
||
|
|
"rotationIntervalSec",
|
||
|
|
"rotationPlayerCount"
|
||
|
|
],
|
||
|
|
"properties": {
|
||
|
|
"activeView": {
|
||
|
|
"type": "string",
|
||
|
|
"enum": [
|
||
|
|
"group_live",
|
||
|
|
"prelim_tie",
|
||
|
|
"prelim_overall",
|
||
|
|
"final_groups",
|
||
|
|
"final_tie",
|
||
|
|
"podium"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
"showGroupLive": {
|
||
|
|
"type": "boolean"
|
||
|
|
},
|
||
|
|
"groupDisplayMode": {
|
||
|
|
"type": "string",
|
||
|
|
"enum": [
|
||
|
|
"rotate",
|
||
|
|
"fixed"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
"groupFixedCode": {
|
||
|
|
"type": "string"
|
||
|
|
},
|
||
|
|
"showPrelimTie": {
|
||
|
|
"type": "boolean"
|
||
|
|
},
|
||
|
|
"showPrelimOverall": {
|
||
|
|
"type": "boolean"
|
||
|
|
},
|
||
|
|
"showFinalGroups": {
|
||
|
|
"type": "boolean"
|
||
|
|
},
|
||
|
|
"finalDisplayMode": {
|
||
|
|
"type": "string",
|
||
|
|
"enum": [
|
||
|
|
"rotate",
|
||
|
|
"fixed"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
"finalFixedGroup": {
|
||
|
|
"type": "integer",
|
||
|
|
"enum": [
|
||
|
|
1,
|
||
|
|
2
|
||
|
|
]
|
||
|
|
},
|
||
|
|
"showFinalTie": {
|
||
|
|
"type": "boolean"
|
||
|
|
},
|
||
|
|
"showPodium": {
|
||
|
|
"type": "boolean"
|
||
|
|
},
|
||
|
|
"rotationIntervalSec": {
|
||
|
|
"type": "integer",
|
||
|
|
"minimum": 3,
|
||
|
|
"maximum": 30
|
||
|
|
},
|
||
|
|
"rotationPlayerCount": {
|
||
|
|
"type": "integer",
|
||
|
|
"minimum": 3,
|
||
|
|
"maximum": 40
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"LiveModeSettingsUpdateRequest": {
|
||
|
|
"type": "object",
|
||
|
|
"description": "Partial patch. Send only fields that need to change.",
|
||
|
|
"properties": {
|
||
|
|
"activeView": {
|
||
|
|
"type": "string",
|
||
|
|
"enum": [
|
||
|
|
"group_live",
|
||
|
|
"prelim_tie",
|
||
|
|
"prelim_overall",
|
||
|
|
"final_groups",
|
||
|
|
"final_tie",
|
||
|
|
"podium"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
"showGroupLive": {
|
||
|
|
"type": "boolean"
|
||
|
|
},
|
||
|
|
"groupDisplayMode": {
|
||
|
|
"type": "string",
|
||
|
|
"enum": [
|
||
|
|
"rotate",
|
||
|
|
"fixed"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
"groupFixedCode": {
|
||
|
|
"type": "string"
|
||
|
|
},
|
||
|
|
"showPrelimTie": {
|
||
|
|
"type": "boolean"
|
||
|
|
},
|
||
|
|
"showPrelimOverall": {
|
||
|
|
"type": "boolean"
|
||
|
|
},
|
||
|
|
"showFinalGroups": {
|
||
|
|
"type": "boolean"
|
||
|
|
},
|
||
|
|
"finalDisplayMode": {
|
||
|
|
"type": "string",
|
||
|
|
"enum": [
|
||
|
|
"rotate",
|
||
|
|
"fixed"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
"finalFixedGroup": {
|
||
|
|
"type": "integer",
|
||
|
|
"enum": [
|
||
|
|
1,
|
||
|
|
2
|
||
|
|
]
|
||
|
|
},
|
||
|
|
"showFinalTie": {
|
||
|
|
"type": "boolean"
|
||
|
|
},
|
||
|
|
"showPodium": {
|
||
|
|
"type": "boolean"
|
||
|
|
},
|
||
|
|
"rotationIntervalSec": {
|
||
|
|
"type": "integer",
|
||
|
|
"minimum": 3,
|
||
|
|
"maximum": 30
|
||
|
|
},
|
||
|
|
"rotationPlayerCount": {
|
||
|
|
"type": "integer",
|
||
|
|
"minimum": 3,
|
||
|
|
"maximum": 40
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"AppSettings": {
|
||
|
|
"type": "object",
|
||
|
|
"required": [
|
||
|
|
"viewProofInView",
|
||
|
|
"liveMode"
|
||
|
|
],
|
||
|
|
"properties": {
|
||
|
|
"viewProofInView": {
|
||
|
|
"type": "boolean"
|
||
|
|
},
|
||
|
|
"liveMode": {
|
||
|
|
"$ref": "#/components/schemas/LiveModeSettings"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"StateResponse": {
|
||
|
|
"type": "object",
|
||
|
|
"required": [
|
||
|
|
"competition",
|
||
|
|
"players",
|
||
|
|
"scores",
|
||
|
|
"settings",
|
||
|
|
"derived",
|
||
|
|
"serverTime"
|
||
|
|
],
|
||
|
|
"properties": {
|
||
|
|
"competition": {
|
||
|
|
"$ref": "#/components/schemas/CompetitionMeta"
|
||
|
|
},
|
||
|
|
"players": {
|
||
|
|
"type": "array",
|
||
|
|
"items": {
|
||
|
|
"$ref": "#/components/schemas/Player"
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"scores": {
|
||
|
|
"type": "object",
|
||
|
|
"additionalProperties": {
|
||
|
|
"type": "object",
|
||
|
|
"additionalProperties": {
|
||
|
|
"type": "integer"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"scoreProofs": {
|
||
|
|
"type": "object",
|
||
|
|
"additionalProperties": {
|
||
|
|
"type": "object",
|
||
|
|
"additionalProperties": {
|
||
|
|
"type": "string"
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"description": "Included for admin-authenticated state requests."
|
||
|
|
},
|
||
|
|
"settings": {
|
||
|
|
"$ref": "#/components/schemas/AppSettings"
|
||
|
|
},
|
||
|
|
"derived": {
|
||
|
|
"$ref": "#/components/schemas/DerivedState"
|
||
|
|
},
|
||
|
|
"serverTime": {
|
||
|
|
"type": "string",
|
||
|
|
"format": "date-time"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"AdminSettingsUpdateRequest": {
|
||
|
|
"type": "object",
|
||
|
|
"description": "At least one field must be provided.",
|
||
|
|
"properties": {
|
||
|
|
"viewProofInView": {
|
||
|
|
"type": "boolean"
|
||
|
|
},
|
||
|
|
"liveMode": {
|
||
|
|
"$ref": "#/components/schemas/LiveModeSettingsUpdateRequest"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"PlayerCreateRequest": {
|
||
|
|
"type": "object",
|
||
|
|
"required": [
|
||
|
|
"nameAr",
|
||
|
|
"nameEn",
|
||
|
|
"groupCode"
|
||
|
|
],
|
||
|
|
"properties": {
|
||
|
|
"nameAr": {
|
||
|
|
"type": "string"
|
||
|
|
},
|
||
|
|
"nameEn": {
|
||
|
|
"type": "string"
|
||
|
|
},
|
||
|
|
"groupCode": {
|
||
|
|
"type": "string"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"PlayerUpdateRequest": {
|
||
|
|
"type": "object",
|
||
|
|
"description": "Partial patch. Set `clearImage` true to remove image.",
|
||
|
|
"properties": {
|
||
|
|
"nameAr": {
|
||
|
|
"type": "string"
|
||
|
|
},
|
||
|
|
"nameEn": {
|
||
|
|
"type": "string"
|
||
|
|
},
|
||
|
|
"groupCode": {
|
||
|
|
"type": "string"
|
||
|
|
},
|
||
|
|
"imageData": {
|
||
|
|
"type": "string",
|
||
|
|
"description": "Base64/data URL payload. Max about 2.5MB characters."
|
||
|
|
},
|
||
|
|
"clearImage": {
|
||
|
|
"type": "boolean"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"AutoGroupPlayersRequest": {
|
||
|
|
"type": "object",
|
||
|
|
"required": [
|
||
|
|
"groups"
|
||
|
|
],
|
||
|
|
"properties": {
|
||
|
|
"groups": {
|
||
|
|
"type": "array",
|
||
|
|
"items": {
|
||
|
|
"type": "string"
|
||
|
|
},
|
||
|
|
"minItems": 1
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"ScoreUpdateRequest": {
|
||
|
|
"type": "object",
|
||
|
|
"required": [
|
||
|
|
"score"
|
||
|
|
],
|
||
|
|
"properties": {
|
||
|
|
"score": {
|
||
|
|
"type": "integer",
|
||
|
|
"minimum": 0,
|
||
|
|
"maximum": 100
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"ScoreProofUpdateRequest": {
|
||
|
|
"type": "object",
|
||
|
|
"required": [
|
||
|
|
"imageData"
|
||
|
|
],
|
||
|
|
"properties": {
|
||
|
|
"imageData": {
|
||
|
|
"type": "string",
|
||
|
|
"description": "Base64/data URL payload. Max about 5MB characters."
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"ResetStageRequest": {
|
||
|
|
"type": "object",
|
||
|
|
"properties": {
|
||
|
|
"resetProofs": {
|
||
|
|
"type": "boolean"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"ScoreAdviceResponse": {
|
||
|
|
"type": "object",
|
||
|
|
"required": [
|
||
|
|
"stage",
|
||
|
|
"playerId",
|
||
|
|
"advisedScore",
|
||
|
|
"reason",
|
||
|
|
"generatedAt"
|
||
|
|
],
|
||
|
|
"properties": {
|
||
|
|
"stage": {
|
||
|
|
"type": "string"
|
||
|
|
},
|
||
|
|
"playerId": {
|
||
|
|
"type": "integer"
|
||
|
|
},
|
||
|
|
"advisedScore": {
|
||
|
|
"type": "integer",
|
||
|
|
"minimum": 0,
|
||
|
|
"maximum": 100
|
||
|
|
},
|
||
|
|
"reason": {
|
||
|
|
"type": "string"
|
||
|
|
},
|
||
|
|
"generatedAt": {
|
||
|
|
"type": "string",
|
||
|
|
"format": "date-time"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|