Files
languard-servers-manager/API.md
Khoa (Revenovich) Tran Gia 2c72e45b5f fix: address santa-loop review findings (round 2)
Stage and commit remaining 4 title renames that were left as
unstaged working-tree changes:
- API.md: Languard Server Manager → Languard Servers Manager
- DATABASE.md: Languard Server Manager → Languard Servers Manager
- MODULES.md: Languard Server Manager → Languard Servers Manager
- THREADING.md: Languard Server Manager → Languard Servers Manager
2026-04-16 14:08:44 +07:00

16 KiB

Languard Servers Manager — API Contract

Base URL

http://localhost:8000/api

Authentication

  • All endpoints except POST /auth/login require: Authorization: Bearer <JWT>
  • WebSocket: pass token as query param: ws://localhost:8000/ws/{server_id}?token=<JWT>
  • JWT payload: { "sub": "user_id", "username": "string", "role": "admin|viewer", "exp": timestamp }

Common Response Envelope

{
  "success": true,
  "data": { ... },
  "error": null
}

Error response:

{
  "success": false,
  "data": null,
  "error": {
    "code": "NOT_FOUND",
    "message": "Server with id 5 not found"
  }
}

HTTP Status Codes

Code Meaning
200 Success
201 Created
204 No content (DELETE)
400 Validation error
401 Unauthenticated
403 Forbidden (insufficient role)
404 Not found
409 Conflict (already running, duplicate)
422 Unprocessable (Pydantic validation)
500 Internal server error

Auth Endpoints

POST /auth/login

Login and receive JWT.

Request:

{
  "username": "admin",
  "password": "secret"
}

Response 200:

{
  "success": true,
  "data": {
    "access_token": "eyJhbGciOiJIUzI1NiIs...",
    "token_type": "bearer",
    "expires_in": 86400,
    "user": {
      "id": 1,
      "username": "admin",
      "role": "admin"
    }
  }
}

POST /auth/logout

Invalidate token (client-side token deletion; server-side blacklist optional).

GET /auth/me

Return current user info.

PUT /auth/password

Change password. Admin only.

{ "current_password": "old", "new_password": "new" }

GET /auth/users

List all users. Admin only.

POST /auth/users

Create user. Admin only.

{ "username": "viewer1", "password": "pass", "role": "viewer" }

DELETE /auth/users/{user_id}

Delete user. Admin only.


Server Endpoints

GET /servers

List all servers with current status. Supports pagination.

Query params: ?limit=50&offset=0

Response 200:

{
  "success": true,
  "data": [
    {
      "id": 1,
      "name": "Main Server",
      "description": "Primary COOP server",
      "status": "running",
      "pid": 12345,
      "game_port": 2302,
      "rcon_port": 2306,
      "player_count": 15,
      "max_players": 40,
      "current_mission": "MyMission.Altis",
      "uptime_seconds": 3600,
      "cpu_percent": 34.2,
      "ram_mb": 1850.5,
      "started_at": "2026-04-16T10:00:00Z"
      // current_mission: computed from RCon 'players' response or mission_rotation + server status
      // uptime_seconds: computed as (now - started_at) in the service layer
    }
  ]
}

POST /servers

Create a new server. Admin only.

Request:

{
  "name": "Main Server",
  "description": "Primary COOP server",
  "exe_path": "C:/Arma3Server/arma3server_x64.exe",
  "game_port": 2302,
  "rcon_port": 2306,
  "auto_restart": true,
  "max_restarts": 3
}

Note: password_admin is auto-generated if not provided in the request. The generated value is returned in the response (shown once — not stored in plaintext API responses after creation). rcon_password is also auto-generated if not provided. Response 201: Returns full server object including auto-generated credentials.

GET /servers/{server_id}

Get server detail with full status.

Response 200:

{
  "success": true,
  "data": {
    "id": 1,
    "name": "Main Server",
    "status": "running",
    "pid": 12345,
    "game_port": 2302,
    "rcon_port": 2306,
    "auto_restart": true,
    "restart_count": 0,
    "player_count": 15,
    "max_players": 40,
    "cpu_percent": 34.2,
    "ram_mb": 1850.5,
    "started_at": "2026-04-16T10:00:00Z",
    "uptime_seconds": 3600,
    "current_mission": "MyMission.Altis"
  }
}

PUT /servers/{server_id}

Update server metadata (name, description, exe_path, ports). Admin only.

DELETE /servers/{server_id}

Delete server (must be stopped first). Admin only. Removes DB rows and servers/{id}/ directory.

POST /servers/{server_id}/start

Start the server. Admin only.

Response 200:

{ "success": true, "data": { "status": "starting", "pid": null } }

Response 409: Server already running.

POST /servers/{server_id}/stop

Graceful stop (send #shutdown via RCon, then kill after 30s). Admin only.

Request (optional):

{ "force": false, "reason": "Maintenance" }

POST /servers/{server_id}/restart

Stop then start. Admin only.

POST /servers/{server_id}/kill

Force-kill the process immediately. Admin only. Emergency use only.


Server Config Endpoints

GET /servers/{server_id}/config

Get all config sections combined.

Response 200:

{
  "success": true,
  "data": {
    "server": { /* server_configs row */ },
    "basic": { /* basic_configs row */ },
    "profile": { /* server_profiles row */ },
    "launch": { /* launch_params row */ },
    "rcon": { "rcon_password": "***", "max_ping": 200, "enabled": true }
  }
}

PUT /servers/{server_id}/config/server

Update server.cfg settings. Admin only.

Request: Partial object matching server_configs columns (snake_case). Any omitted field keeps current value.

{
  "hostname": "Updated Server Name",
  "max_players": 64,
  "battleye": 1,
  "verify_signatures": 2,
  "motd_lines": ["Welcome!", "Have fun"],
  "motd_interval": 5.0
}

PUT /servers/{server_id}/config/basic

Update basic.cfg (bandwidth) settings. Admin only.

{
  "max_bandwidth": 50000000,
  "max_msg_send": 256
}

PUT /servers/{server_id}/config/profile

Update difficulty profile. Admin only.

{
  "third_person_view": 0,
  "weapon_crosshair": 0,
  "ai_level_preset": 3,
  "skill_ai": 0.7,
  "precision_ai": 0.6
}

PUT /servers/{server_id}/config/launch

Update launch parameters. Admin only.

{
  "world": "empty",
  "limit_fps": 50,
  "auto_init": false,
  "load_mission_to_memory": true,
  "bandwidth_alg": 2,
  "enable_ht": true,
  "huge_pages": false
}

PUT /servers/{server_id}/config/rcon

Update BattlEye RCon settings. Admin only. Regenerates battleye/beserver.cfg immediately. Note: BattlEye reads beserver.cfg only at server startup — RCon config changes require a server restart to take effect. The updated config file is ready for the next start.

Note on rcon_port: This field is stored in the servers table (not rcon_configs). The service layer updates both tables as needed. Include only fields you want to change.

{
  "rcon_password": "newpassword",
  "rcon_port": 2306,
  "max_ping": 300,
  "enabled": true
}

GET /servers/{server_id}/config/preview

Returns rendered server.cfg as plain text string (for preview in UI). Admin only — contains plaintext credentials.

Response 200: Content-Type: text/plain

GET /servers/{server_id}/config/download/{filename}

Download generated config file. Filename must be one of: server.cfg | basic.cfg | server.Arma3Profile (whitelist-validated, no path traversal). Admin only — config files contain plaintext passwords.


Mission Endpoints

GET /servers/{server_id}/missions

List all mission PBOs for a server.

Response 200:

{
  "success": true,
  "data": [
    {
      "id": 1,
      "filename": "MyMission.Altis.pbo",
      "mission_name": "MyMission.Altis",
      "terrain": "Altis",
      "file_size": 102400,
      "uploaded_at": "2026-04-16T09:00:00Z"
    }
  ]
}

POST /servers/{server_id}/missions/upload

Upload a mission PBO. Admin only. multipart/form-data.

Form fields:

  • file: the .pbo file (filename is sanitized with os.path.basename() to prevent path traversal; only .pbo extension allowed)

Response 201:

{
  "success": true,
  "data": {
    "id": 2,
    "filename": "NewMission.Stratis.pbo",
    "mission_name": "NewMission.Stratis",
    "terrain": "Stratis",
    "file_size": 51200
  }
}

DELETE /servers/{server_id}/missions/{mission_id}

Delete a mission PBO (removes file from disk). Admin only.

GET /servers/{server_id}/missions/rotation

Get current mission rotation (ordered list).

Response 200:

{
  "success": true,
  "data": [
    {
      "id": 1,
      "sort_order": 0,
      "mission": { "id": 1, "mission_name": "MyMission.Altis" },
      "difficulty": "Regular",
      "params": { "RespawnDelay": 15 }
    }
  ]
}

PUT /servers/{server_id}/missions/rotation

Replace the entire mission rotation. Admin only.

{
  "rotation": [
    { "mission_id": 1, "difficulty": "Regular", "params": {} },
    { "mission_id": 2, "difficulty": "Veteran", "params": { "RespawnDelay": 30 } }
  ]
}

Mod Endpoints

GET /mods

List all registered mods (global list).

POST /mods

Register a mod folder. Admin only.

{
  "name": "@CBA_A3",
  "folder_path": "C:/Arma3Server/@CBA_A3",
  "workshop_id": "450814997",
  "description": "Community Base Addons"
}

DELETE /mods/{mod_id}

Delete mod registration. Admin only.

GET /servers/{server_id}/mods

Get mods enabled for a server.

Response 200:

{
  "success": true,
  "data": [
    {
      "mod_id": 1,
      "name": "@CBA_A3",
      "folder_path": "C:/Arma3Server/@CBA_A3",
      "is_server_mod": false,
      "sort_order": 0
    }
  ]
}

PUT /servers/{server_id}/mods

Replace the mod list for a server. Admin only.

{
  "mods": [
    { "mod_id": 1, "is_server_mod": false, "sort_order": 0 },
    { "mod_id": 2, "is_server_mod": true,  "sort_order": 1 }
  ]
}

Player Endpoints

GET /servers/{server_id}/players

Get currently connected players.

Response 200:

{
  "success": true,
  "data": [
    {
      "player_num": 1,
      "name": "PlayerOne",
      "guid": "abc123...",
      "ping": 45,
      "verified": true,
      "joined_at": "2026-04-16T10:15:00Z"
    }
  ]
}

POST /servers/{server_id}/players/{player_num}/kick

Kick a player via RCon. Admin only.

{ "reason": "AFK" }

POST /servers/{server_id}/players/{player_num}/ban

Ban a player via RCon. Admin only.

{
  "reason": "Griefing",
  "duration_minutes": 0
}

GET /servers/{server_id}/players/history

Player connection history. Supports pagination.

Query params: ?limit=50&offset=0&search=PlayerName


Ban Endpoints

GET /servers/{server_id}/bans

List all bans for a server.

Query params: ?active_only=true&limit=50&offset=0

POST /servers/{server_id}/bans

Add ban manually. Admin only.

{
  "guid": "abc123...",
  "steam_uid": "76561198...",
  "name": "PlayerName",
  "reason": "Cheating",
  "duration_minutes": 0
}

DELETE /servers/{server_id}/bans/{ban_id}

Remove a ban. Admin only.


Log Endpoints

GET /servers/{server_id}/logs

Query stored log lines.

Query params: ?limit=200&offset=0&level=error&since=2026-04-16T10:00:00Z&search=BattlEye

Response 200:

{
  "success": true,
  "data": {
    "total": 1542,
    "logs": [
      {
        "id": 100,
        "timestamp": "2026-04-16T10:05:23Z",
        "level": "info",
        "message": "Player PlayerOne connected"
      }
    ]
  }
}

DELETE /servers/{server_id}/logs

Clear all stored log lines for a server. Admin only.


Metrics Endpoints

GET /servers/{server_id}/metrics

Get time-series metrics.

Query params: ?from=2026-04-16T00:00:00Z&to=2026-04-16T23:59:59Z&resolution=5m

Response 200:

{
  "success": true,
  "data": [
    {
      "timestamp": "2026-04-16T10:00:00Z",
      "cpu_percent": 34.2,
      "ram_mb": 1850.5,
      "player_count": 15
    }
  ]
}

RCon Endpoints

POST /servers/{server_id}/rcon/command

Send raw RCon/admin command. Admin only.

{ "command": "#restart" }

Available commands:

  • #restart — Restart mission
  • #reassign — Restart with roles unassigned
  • #missions — Open mission selection
  • #lock / #unlock — Lock/unlock server
  • #mission NAME.TERRAIN [difficulty] — Load specific mission
  • #shutdown — Shut down server
  • #monitor N — Toggle performance monitoring
  • say -1 MESSAGE — Message all players

POST /servers/{server_id}/rcon/say

Broadcast a message to all players. Admin only.

{ "message": "Server restarting in 5 minutes!" }

Event Log Endpoints

GET /servers/{server_id}/events

Get server event history (audit trail).

Query params: ?limit=50&offset=0&event_type=crashed


System Endpoints

GET /system/status

Overall system status. Requires authentication (admin or viewer).

{
  "success": true,
  "data": {
    "version": "1.0.0",
    "running_servers": 2,
    "total_servers": 3,
    "uptime_seconds": 86400
  }
}

GET /system/health

Health check (for load balancer/Docker). Returns 200 if healthy.


WebSocket API

Connection

ws://localhost:8000/ws/{server_id}?token=<JWT>

Use server_id = "all" to subscribe to events from all servers.

Client → Server Messages

{ "type": "ping" }
{ "type": "subscribe", "channels": ["logs", "players", "metrics", "status"] }
{ "type": "unsubscribe", "channels": ["metrics"] }

Channel subscription: The ConnectionManager tracks per-connection channel subscriptions. Only messages matching subscribed channels are delivered. Default subscriptions on connect: ["status"].

Channel names match message types exactly: status, log, players, metrics, event. Subscribe with channel names matching the type field in server→client messages.

Server → Client Messages

Status Update

Sent when server status changes (starting → running → stopped, etc.)

{
  "type": "status",
  "server_id": 1,
  "data": {
    "status": "running",
    "pid": 12345,
    "started_at": "2026-04-16T10:00:00Z"
  }
}

Log Line

Sent for each new RPT log line.

{
  "type": "log",
  "server_id": 1,
  "data": {
    "timestamp": "2026-04-16T10:05:23Z",
    "level": "info",
    "message": "BattlEye Server: Initialized (v1.240)"
  }
}

Player List Update

Sent after each RCon poll (every 10s).

{
  "type": "players",
  "server_id": 1,
  "data": {
    "players": [
      { "player_num": 1, "name": "PlayerOne", "ping": 45, "verified": true }
    ],
    "count": 1
  }
}

Metrics Update

Sent every 5 seconds.

{
  "type": "metrics",
  "server_id": 1,
  "data": {
    "cpu_percent": 34.2,
    "ram_mb": 1850.5,
    "player_count": 1,
    "timestamp": "2026-04-16T10:05:25Z"
  }
}

Server Event

Sent for significant events (crash, restart, etc.)

{
  "type": "event",
  "server_id": 1,
  "data": {
    "event_type": "crashed",
    "detail": { "exit_code": 1 },
    "timestamp": "2026-04-16T10:30:00Z"
  }
}

Pong

{ "type": "pong" }

Rate Limiting

  • POST /auth/login: 5 attempts per minute per IP. Exceeded returns 429 Too Many Requests.
  • All other endpoints: 60 requests per minute per token. Exceeded returns 429.
  • Implemented via FastAPI middleware (e.g., slowapi).

Error Codes Reference

Code Description
UNAUTHORIZED Missing or invalid token
FORBIDDEN Role insufficient
NOT_FOUND Resource not found
SERVER_ALREADY_RUNNING Start called on running server
SERVER_NOT_RUNNING Stop/command on stopped server
RCON_UNAVAILABLE RCon connection failed
INVALID_CONFIG Config validation failed
EXE_NOT_FOUND arma3server.exe not at configured path
PORT_IN_USE Game port already occupied
UPLOAD_FAILED Mission file upload error
VALIDATION_ERROR Pydantic validation failure
INTERNAL_ERROR Unexpected server error
MOD_IN_USE Cannot delete mod — enabled on one or more servers
MISSION_IN_ROTATION Cannot delete mission — in active rotation
RATE_LIMITED Too many requests