cab-backend/API.md
2026-03-30 21:00:35 +03:00

11 KiB

Backend API

Base URL: http://localhost:8080

All protected endpoints require:

Authorization: Bearer <token>

Access Rules

  • Public:
    • POST /api/auth/login
    • POST /api/auth/register
    • GET /api/invites/validate/:code
  • Authorized users:
    • GET /api/auth/me
    • read endpoints for games, modules, languages, leaderboards, notifications
  • admin / moderator:
    • create/update/delete for games, modules inside games, variables, notifications, leaderboards, groups, languages, invites, image uploads

Auth

Login

POST /api/auth/login

{
  "email": "admin@admin.com",
  "password": "admin123"
}

Register

POST /api/auth/register

{
  "email": "user@example.com",
  "username": "player1",
  "password": "secret123",
  "invite_code": "abcd1234"
}

Current User

GET /api/auth/me

Games

List Games

GET /api/games

Each game includes connected modules.

Example response:

[
  {
    "id": 5,
    "name": "Black & White",
    "slug": "b&d",
    "description": "Sandbox",
    "image_url": "",
    "is_active": true,
    "modules": [
      {
        "id": 1,
        "key": "variables",
        "name": "Variables"
      },
      {
        "id": 2,
        "key": "notification",
        "name": "Notification"
      }
    ]
  }
]

Supports search by game name or slug:

  • GET /api/games?search=black
  • GET /api/games?search=b&d

Get Game

GET /api/games/:id

Useful when client is inside a module page and needs the current game slug to restore game search state on exit.

Example response:

{
  "id": 5,
  "name": "Black & White",
  "slug": "b&d",
  "description": "Sandbox",
  "image_url": "",
  "is_active": true,
  "modules": [
    {
      "id": 1,
      "key": "variables",
      "name": "Variables"
    },
    {
      "id": 2,
      "key": "notification",
      "name": "Notification"
    }
  ]
}

Create Game

POST /api/games

{
  "name": "My Game",
  "slug": "my-game",
  "description": "Description",
  "image_url": ""
}

Update Game

PUT /api/games/:id

Delete Game

DELETE /api/games/:id

Modules

List Available Modules

GET /api/modules

Default modules:

  • variables
  • notification
  • leaderboard

List Game Modules

GET /api/games/:id/modules

Connect Module To Game

POST /api/games/:id/modules

{
  "module_key": "variables"
}

Disconnect Module From Game

DELETE /api/games/:id/modules/:module_key

Languages

Default seeded languages:

  • en / English
  • ru / Русский

List Languages

GET /api/languages

Get Language

GET /api/languages/:id

Create Language

POST /api/languages

{
  "code": "de",
  "name": "Deutsch"
}

Update Language

PUT /api/languages/:id

Delete Language

DELETE /api/languages/:id

User-Game Connections

Search Users In Game

GET /api/games/:id/users?search=...

Returns only users connected to game :id.

Search behavior:

  • if search is an integer, search by user id
  • otherwise search by username or email

Examples:

  • GET /api/games/5/users
  • GET /api/games/5/users?search=12
  • GET /api/games/5/users?search=denis
  • GET /api/games/5/users?search=denis@example.com

Example response:

[
  {
    "id": 12,
    "email": "denis@example.com",
    "username": "denis",
    "role": "user",
    "is_active": true,
    "created_at": "2026-03-23T20:00:00Z",
    "updated_at": "2026-03-23T20:00:00Z"
  }
]

List User Games

GET /api/users/:id/games

Connect Game To User

POST /api/users/:id/games

{
  "game_id": 5
}

Disconnect Game From User

DELETE /api/users/:id/games/:game_id

Variables

All variable endpoints work only if the game has module variables.

List Variables

GET /api/games/:id/variables

Create Variable

POST /api/games/:id/variables

Number variable:

{
  "key": "max_bet",
  "type": "number",
  "number_value": 100
}

String variable:

{
  "key": "welcome_text",
  "type": "string",
  "string_value": "hello"
}

Table variable with numbers:

{
  "key": "rates",
  "type": "table",
  "table_value_type": "number",
  "items": [
    { "key": "bronze", "number_value": 1.1 },
    { "key": "silver", "number_value": 1.3 }
  ]
}

Table variable with strings:

{
  "key": "titles",
  "type": "table",
  "table_value_type": "string",
  "items": [
    { "key": "bronze", "string_value": "Bronze" },
    { "key": "silver", "string_value": "Silver" }
  ]
}

Vector variable with numbers:

{
  "key": "steps",
  "type": "vector",
  "table_value_type": "number",
  "items": [
    { "index": 0, "number_value": 10 },
    { "index": 1, "number_value": 20 }
  ]
}

Vector variable with strings:

{
  "key": "messages",
  "type": "vector",
  "table_value_type": "string",
  "items": [
    { "index": 0, "string_value": "hello" },
    { "index": 1, "string_value": "world" }
  ]
}

Update Variable

PUT /api/games/:id/variables/:var_id

Delete Variable

DELETE /api/games/:id/variables/:var_id

Variable Rules

  • type = number uses only number_value
  • type = string uses only string_value
  • type = table uses only table_value_type and items
  • type = vector uses only table_value_type and items
  • all table items must use the same value type
  • all vector items must use index instead of key
  • vector indexes must be unique and >= 0
  • variable key is unique inside one game

Images

Upload Image

POST /api/images

Content type: multipart/form-data

Form field:

  • file

Example response:

{
  "name": "f6e7b0b2-6f8d-4a33-8fc8-2a2f8f6d8c4b.png",
  "path": "/uploads/images/f6e7b0b2-6f8d-4a33-8fc8-2a2f8f6d8c4b.png",
  "url": "http://localhost:8080/uploads/images/f6e7b0b2-6f8d-4a33-8fc8-2a2f8f6d8c4b.png",
  "content_type": "image/png"
}

Static file access:

GET /uploads/images/:filename

Notifications

All notification endpoints work only if the game has module notification.

Notification structure:

  • one notification has:
    • name
    • multilingual descriptions
    • shared custom variables
    • many entries
  • each entry has its own:
    • time_second
    • image
    • login
    • values for all shared custom variables

Macros returned by API always include:

  • {{time_second}}
  • {{image}}
  • {{login}}
  • all custom variable keys

List Notifications

GET /api/games/:id/notifications

Get Notification

GET /api/games/:id/notifications/:notification_id

Create Notification

POST /api/games/:id/notifications

{
  "name": "Welcome bonus",
  "descriptions": [
    {
      "language_id": 1,
      "description": "Hello {{login}}, promo {{promo_code}} after {{time_second}} seconds"
    },
    {
      "language_id": 2,
      "description": "Привет {{login}}, промокод {{promo_code}} через {{time_second}} секунд"
    }
  ],
  "variables": [
    { "key": "promo_code" },
    { "key": "reward" }
  ],
  "entries": [
    {
      "time_second": 30,
      "image": "http://localhost:8080/uploads/images/a.png",
      "login": "Denis",
      "variables": [
        { "key": "promo_code", "value": "WELCOME30" },
        { "key": "reward", "value": "100" }
      ]
    },
    {
      "time_second": 60,
      "image": "http://localhost:8080/uploads/images/b.png",
      "login": "Alex",
      "variables": [
        { "key": "promo_code", "value": "WELCOME60" },
        { "key": "reward", "value": "200" }
      ]
    }
  ]
}

Update Notification

PUT /api/games/:id/notifications/:notification_id

Delete Notification

DELETE /api/games/:id/notifications/:notification_id

Notification Rules

  • name is required
  • descriptions must contain at least one item
  • entries must contain at least one item
  • one language_id cannot repeat inside descriptions
  • top-level variables define the shared custom variable set
  • reserved variable keys are forbidden:
    • time_second
    • image
    • login
  • each entry must contain:
    • time_second >= 0
    • image
    • login
    • full set of custom variable values
  • entry variable keys must exactly match top-level custom variable keys

Leaderboards

All leaderboard endpoints work only if the game has module leaderboard.

List Game Leaderboards

GET /api/games/:id/leaderboards

Get Leaderboard

GET /api/games/:id/leaderboards/:leaderboard_id

Create Leaderboard

POST /api/games/:id/leaderboards

{
  "key": "top_balance",
  "name": "Top Balance",
  "sort_order": "desc",
  "period_type": "all_time",
  "is_active": true
}

sort_order:

  • asc
  • desc

period_type:

  • all_time
  • daily
  • weekly
  • monthly

Update Leaderboard

PUT /api/games/:id/leaderboards/:leaderboard_id

Delete Leaderboard

DELETE /api/games/:id/leaderboards/:leaderboard_id

List Leaderboard Groups

GET /api/leaderboards/:leaderboard_id/groups

Create Group

POST /api/leaderboards/:leaderboard_id/groups

{
  "key": "vip",
  "name": "VIP",
  "is_default": false
}

Update Group

PUT /api/leaderboard-groups/:group_id

Delete Group

DELETE /api/leaderboard-groups/:group_id

Add Group Member

POST /api/leaderboard-groups/:group_id/members

{
  "user_id": 12
}

User must already be connected to the same game.

Delete Group Member

DELETE /api/leaderboard-groups/:group_id/members/:user_id

Save Score

POST /api/leaderboards/:leaderboard_id/scores

{
  "user_game_id": 5,
  "score": 1200
}

If a score already exists for this user in this leaderboard, it is updated.

Get Rankings

GET /api/leaderboards/:leaderboard_id/rankings

Query params:

  • group_id optional
  • limit optional, default 50, max 200

Example:

GET /api/leaderboards/1/rankings?group_id=2&limit=20

Response:

{
  "leaderboard": {
    "id": 1,
    "game_id": 5,
    "key": "top_balance",
    "name": "Top Balance",
    "sort_order": "desc",
    "period_type": "all_time",
    "is_active": true
  },
  "items": [
    {
      "rank": 1,
      "user_id": 12,
      "user_game_id": 5,
      "username": "arnold",
      "score": 1200
    },
    {
      "rank": 2,
      "user_id": 17,
      "user_game_id": 8,
      "username": "german",
      "score": 950
    }
  ]
}

Leaderboard Rules

  • leaderboard key is unique inside one game
  • group key is unique inside one leaderboard
  • score is stored once per leaderboard_id + user_game_id
  • groups do not duplicate scores, they only filter ranking members

Balance

Top Up Balance

POST /api/user-games/:ug_id/topup

{
  "amount": 100,
  "comment": "manual topup"
}

List Transactions

GET /api/user-games/:ug_id/transactions

Invites

Validate Invite

GET /api/invites/validate/:code

Create Invite

POST /api/invites

{
  "role": "user",
  "max_uses": 10
}

List Invites

GET /api/invites

Delete Invite

DELETE /api/invites/:code

Common Error Patterns

  • 400 invalid path param or invalid request body
  • 401 invalid or missing token
  • 403 module is not enabled for the target game, or role is insufficient
  • 404 entity not found
  • 409 duplicate key / duplicate connection / duplicate membership