# Languard Servers Manager — API Contract ## Base URL ``` http://localhost:8000/api ``` ## Authentication - All endpoints except `POST /auth/login` and `GET /system/health` require: `Authorization: Bearer ` - WebSocket: pass token as query param: `ws://localhost:8000/ws/{server_id}?token=` - JWT payload: `{ "sub": "user_id", "username": "string", "role": "admin|viewer", "exp": timestamp }` ## Common Response Envelope ```json { "success": true, "data": { ... }, "error": null } ``` Error response: ```json { "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 (or capability not supported by adapter) | | 409 | Conflict (already running, duplicate) | | 422 | Unprocessable (Pydantic validation) | | 500 | Internal server error | ## Capability-Based Routing Some endpoints depend on the server's game adapter supporting a specific capability. If the adapter does not support the capability, the endpoint returns **404** with a clear message: ```json { "success": false, "data": null, "error": { "code": "CAPABILITY_NOT_SUPPORTED", "message": "Missions not supported for game type 'rust'" } } ``` --- ## Auth Endpoints ### POST /auth/login Login and receive JWT. **Request:** ```json { "username": "admin", "password": "secret" } ``` **Response 200:** ```json { "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. ```json { "current_password": "old", "new_password": "new" } ``` ### GET /auth/users List all users. Admin only. ### POST /auth/users Create user. Admin only. ```json { "username": "viewer1", "password": "pass", "role": "viewer" } ``` ### DELETE /auth/users/{user_id} Delete user. Admin only. --- ## Game Type Discovery Endpoints ### GET /games List all registered game types and their capabilities. **Response 200:** ```json { "success": true, "data": [ { "game_type": "arma3", "display_name": "Arma 3", "version": "1.0.0", "capabilities": ["config_generator", "process_config", "log_parser", "remote_admin", "mission_manager", "mod_manager", "ban_manager"] } ] } ``` ### GET /games/{game_type} Get details for a specific game type. **Response 200:** ```json { "success": true, "data": { "game_type": "arma3", "display_name": "Arma 3", "version": "1.0.0", "capabilities": ["config_generator", "process_config", "log_parser", "remote_admin", "mission_manager", "mod_manager", "ban_manager"], "config_sections": ["server", "basic", "profile", "launch", "rcon"], "allowed_executables": ["arma3server_x64.exe", "arma3server.exe"] } } ``` ### GET /games/{game_type}/config-schema Returns JSON Schema for each config section defined by the adapter. Used by frontend to build dynamic config forms. **Response 200:** ```json { "success": true, "data": { "server": { /* JSON Schema for server.cfg params */ }, "basic": { /* JSON Schema for basic.cfg params */ }, "profile": { /* JSON Schema for Arma3Profile params */ }, "launch": { /* JSON Schema for launch params */ }, "rcon": { /* JSON Schema for RCon params */ } } } ``` ### GET /games/{game_type}/defaults Default config values for new server creation. **Response 200:** ```json { "success": true, "data": { "server": { "hostname": "My Arma 3 Server", "max_players": 40, ... }, "basic": { "min_bandwidth": 800000, ... }, "profile": { "reduced_damage": 0, ... }, "launch": { "world": "empty", "limit_fps": 50, ... }, "rcon": { "max_ping": 200, "enabled": 1 } } } ``` --- ## Server Endpoints ### GET /servers List all servers with current status. Supports filtering by game type. **Query params:** `?limit=50&offset=0&game_type=arma3` **Response 200:** ```json { "success": true, "data": [ { "id": 1, "name": "Main Server", "description": "Primary COOP server", "game_type": "arma3", "status": "running", "pid": 12345, "game_port": 2302, "rcon_port": 2306, "player_count": 15, "max_players": 40, "cpu_percent": 34.2, "ram_mb": 1850.5, "started_at": "2026-04-16T10:00:00Z" } ] } ``` ### POST /servers Create a new server. Admin only. `game_type` determines which adapter handles this server. **Request:** ```json { "name": "Main Server", "description": "Primary COOP server", "game_type": "arma3", "exe_path": "C:/Arma3Server/arma3server_x64.exe", "game_port": 2302, "rcon_port": 2306, "auto_restart": true, "max_restarts": 3 } ``` The adapter provides default config values for all sections. Auto-generated credentials (e.g., `password_admin`, `rcon_password` for Arma 3) are returned in the response and not stored in plaintext. **Response 201:** Returns full server object including auto-generated credentials. ### GET /servers/{server_id} Get server detail with full status. **Response 200:** ```json { "success": true, "data": { "id": 1, "name": "Main Server", "game_type": "arma3", "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 } } ``` ### 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. Core resolves the adapter and delegates config generation + launch arg building. **Response 200:** ```json { "success": true, "data": { "status": "starting", "pid": null } } ``` **Response 409:** Server already running. ### POST /servers/{server_id}/stop Graceful stop (sends shutdown via adapter's RemoteAdmin, then force-kill after 30s). Admin only. **Request (optional):** ```json { "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 Config sections are defined by the server's game adapter. The core `game_configs` table stores them as JSON. The adapter's Pydantic models validate input. ### GET /servers/{server_id}/config Get all config sections combined. Each section includes its `config_version` for optimistic locking. **Response 200:** ```json { "success": true, "data": { "server": { /* section JSON */, "_meta": {"config_version": 3, "schema_version": "1.0"} }, "basic": { /* section JSON */, "_meta": {"config_version": 1, "schema_version": "1.0"} }, "profile": { /* section JSON */, "_meta": {"config_version": 2, "schema_version": "1.0"} }, "launch": { /* section JSON */, "_meta": {"config_version": 1, "schema_version": "1.0"} }, "rcon": { "rcon_password": "***", "max_ping": 200, "enabled": 1, "_meta": {"config_version": 1, "schema_version": "1.0"} } } } ``` ### GET /servers/{server_id}/config/{section} Get a single config section. Section names are defined by the adapter (e.g., `server`, `basic`, `profile`, `launch`, `rcon` for Arma 3). **Response 200:** ```json { "success": true, "data": { "hostname": "My Server", "max_players": 40, "_meta": { "config_version": 3, "schema_version": "1.0" } } } ``` ### PUT /servers/{server_id}/config/{section} Update a config section. Admin only. Validated against the adapter's Pydantic model for that section. Sensitive fields (passwords) are encrypted before storage. **Optimistic locking** — client must send `config_version` from their last read. **Request:** Partial object matching the adapter's section schema. Any omitted field keeps current value. Must include `config_version` for conflict detection. **Arma 3 `server` section example:** ```json { "hostname": "Updated Server Name", "max_players": 64, "battleye": 1, "verify_signatures": 2, "motd_lines": ["Welcome!", "Have fun"], "motd_interval": 5.0, "config_version": 3 } ``` **Response 409 (Conflict):** Another admin updated this section since you read it. ```json { "success": false, "data": { "current_config": { /* latest values */ }, "current_version": 5 }, "error": { "code": "CONFIG_VERSION_CONFLICT", "message": "Config section 'server' was modified by another user. Re-read and merge your changes." } } ``` **Arma 3 `basic` section example:** ```json { "max_bandwidth": 50000000, "max_msg_send": 256 } ``` **Arma 3 `profile` section example:** ```json { "third_person_view": 0, "weapon_crosshair": 0, "ai_level_preset": 3, "skill_ai": 0.7, "precision_ai": 0.6 } ``` **Arma 3 `launch` section example:** ```json { "world": "empty", "limit_fps": 50, "auto_init": false, "load_mission_to_memory": true } ``` **Arma 3 `rcon` section example:** ```json { "rcon_password": "newpassword", "rcon_port": 2306, "max_ping": 300, "enabled": true } ``` **Note:** `rcon_port` is stored in the `servers` table, not in the config JSON. The service layer updates both tables as needed. ### GET /servers/{server_id}/config/preview Returns rendered config for preview in UI. **Admin only** — may contain plaintext credentials. Returns a dict of `{label: rendered_content}` — labels are filenames for file-based configs, variable names for env-var configs, or argument names for CLI configs. **Response 200:** ```json { "success": true, "data": { "server.cfg": "// Generated server.cfg\nhostname = \"My Server\";\n...", "basic.cfg": "// Generated basic.cfg\n...", "server.Arma3Profile": "// Generated profile\n..." } } ``` (Non-file games would return e.g. `{"SERVER_NAME": "My Server", "MAX_PLAYERS": "40"}` for env-var configs.) ### GET /servers/{server_id}/config/download/{filename} Download generated config file. Filename must be in adapter's allowlist (whitelist-validated, no path traversal). **Admin only**. --- ## Mission Endpoints (Capability: `mission_manager`) Returns **404** if adapter does not support `mission_manager`. ### GET /servers/{server_id}/missions List all mission/scenario files for a server. **Response 200:** ```json { "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/scenario file. Admin only. `multipart/form-data`. File extension validated by adapter's `MissionManager.file_extension`. **Form fields:** - `file`: the mission file (filename sanitized; only adapter-allowed extensions accepted) **Response 201:** ```json { "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 file (removes file from disk). Admin only. ### GET /servers/{server_id}/missions/rotation Get current mission/scenario rotation (ordered list). **Response 200:** ```json { "success": true, "data": [ { "id": 1, "sort_order": 0, "mission": { "id": 1, "mission_name": "MyMission.Altis" }, "difficulty": "Regular", "params_json": { "RespawnDelay": 15 } } ] } ``` ### PUT /servers/{server_id}/missions/rotation Replace the entire mission rotation. Admin only. ```json { "rotation": [ { "mission_id": 1, "difficulty": "Regular", "params": {} }, { "mission_id": 2, "difficulty": "Veteran", "params": { "RespawnDelay": 30 } } ] } ``` --- ## Mod Endpoints (Capability: `mod_manager`) Returns **404** if adapter does not support `mod_manager`. ### GET /mods List all registered mods. Optionally filter by game type. **Query params:** `?game_type=arma3` ### POST /mods Register a mod folder. Admin only. ```json { "game_type": "arma3", "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:** ```json { "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. ```json { "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:** ```json { "success": true, "data": [ { "slot_id": "1", "name": "PlayerOne", "guid": "abc123...", "ping": 45, "game_data": { "verified": true, "steam_uid": "76561198..." }, "joined_at": "2026-04-16T10:15:00Z" } ] } ``` ### POST /servers/{server_id}/players/{slot_id}/kick Kick a player. Admin only. Requires adapter `remote_admin` capability. ```json { "reason": "AFK" } ``` ### POST /servers/{server_id}/players/{slot_id}/ban Ban a player. Admin only. Requires adapter `remote_admin` capability. ```json { "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. If adapter has `ban_manager`, also syncs to the game's ban file. ```json { "guid": "abc123...", "name": "PlayerName", "reason": "Cheating", "duration_minutes": 0 } ``` ### DELETE /servers/{server_id}/bans/{ban_id} Remove a ban. Admin only. If adapter has `ban_manager`, also removes from the game's ban file. --- ## Remote Admin Endpoints (Capability: `remote_admin`) Returns **404** if adapter does not support `remote_admin`. ### POST /servers/{server_id}/remote-admin/command Send raw remote admin command. Admin only. ```json { "command": "#restart" } ``` **Arma 3 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}/remote-admin/say Broadcast a message to all players. Admin only. ```json { "message": "Server restarting in 5 minutes!" } ``` --- ## 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:** ```json { "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:** ```json { "success": true, "data": [ { "timestamp": "2026-04-16T10:00:00Z", "cpu_percent": 34.2, "ram_mb": 1850.5, "player_count": 15 } ] } ``` --- ## 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). ```json { "success": true, "data": { "version": "1.0.0", "running_servers": 2, "total_servers": 3, "supported_games": ["arma3"], "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= ``` Use `server_id = "all"` to subscribe to events from all servers. ### Client → Server Messages ```json { "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"]`. ### Server → Client Messages #### Status Update ```json { "type": "status", "server_id": 1, "data": { "status": "running", "pid": 12345, "started_at": "2026-04-16T10:00:00Z" } } ``` #### Log Line ```json { "type": "log", "server_id": 1, "data": { "timestamp": "2026-04-16T10:05:23Z", "level": "info", "message": "BattlEye Server: Initialized (v1.240)" } } ``` #### Player List Update ```json { "type": "players", "server_id": 1, "data": { "players": [ { "slot_id": "1", "name": "PlayerOne", "ping": 45 } ], "count": 1 } } ``` #### Metrics Update ```json { "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 ```json { "type": "event", "server_id": 1, "data": { "event_type": "crashed", "detail": { "exit_code": 1 }, "timestamp": "2026-04-16T10:30:00Z" } } ``` #### Pong ```json { "type": "pong" } ``` --- ## Adapter-Specific Routes Adapters may register additional routes under `/api/servers/{server_id}/game/{game_type}/...` for features that have no generic counterpart. **Arma 3 example:** ``` POST /api/servers/{server_id}/game/arma3/battleye/reload ``` These routes are registered at app startup by iterating over all registered adapters. --- ## 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 | | `CAPABILITY_NOT_SUPPORTED` | Adapter lacks required capability for this endpoint | | `SERVER_ALREADY_RUNNING` | Start called on running server | | `SERVER_NOT_RUNNING` | Stop/command on stopped server | | `REMOTE_ADMIN_UNAVAILABLE` | Remote admin connection failed | | `INVALID_CONFIG` | Config validation failed (adapter-specific) | | `CONFIG_WRITE_ERROR` | Config file write failed (disk, permissions) | | `CONFIG_VERSION_CONFLICT` | Optimistic locking conflict on config update | | `EXE_NOT_ALLOWED` | Executable not in adapter's allowlist | | `PORT_IN_USE` | Game port already occupied | | `UPLOAD_FAILED` | File upload error | | `VALIDATION_ERROR` | Pydantic validation failure | | `GAME_TYPE_NOT_FOUND` | No adapter registered for this game type | | `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 |