feat: initial system design documents for Languard Server Manager
Complete backend design for an Arma 3 dedicated server management panel: - ARCHITECTURE.md: System architecture, tech stack, component responsibilities, data flows - DATABASE.md: SQLite schema with WAL mode, CHECK constraints, 16+ tables - API.md: REST + WebSocket API contract with auth, CRUD, and real-time channels - MODULES.md: Python module breakdown with class definitions and dependencies - THREADING.md: Concurrency model with thread safety, auto-restart, and WS bridge - IMPLEMENTATION_PLAN.md: 7-phase implementation plan with security from Phase 1 Key design decisions: - Sync SQLAlchemy only (no aiosqlite), thread-local DB connections - Structured config builder (not f-strings) preventing config injection - RCon request multiplexer for concurrent UDP access - BackgroundScheduler for sync DB cleanup jobs - ban.txt bidirectional sync with documented field mapping - Auto-restart sequenced after thread cleanup Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
755
API.md
Normal file
755
API.md
Normal file
@@ -0,0 +1,755 @@
|
||||
# Languard Server 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
|
||||
```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 |
|
||||
| 409 | Conflict (already running, duplicate) |
|
||||
| 422 | Unprocessable (Pydantic validation) |
|
||||
| 500 | Internal server error |
|
||||
|
||||
---
|
||||
|
||||
## 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.
|
||||
|
||||
---
|
||||
|
||||
## Server Endpoints
|
||||
|
||||
### GET /servers
|
||||
List all servers with current status. Supports pagination.
|
||||
|
||||
**Query params:** `?limit=50&offset=0`
|
||||
|
||||
**Response 200:**
|
||||
```json
|
||||
{
|
||||
"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:**
|
||||
```json
|
||||
{
|
||||
"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:**
|
||||
```json
|
||||
{
|
||||
"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:**
|
||||
```json
|
||||
{ "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):**
|
||||
```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
|
||||
|
||||
### GET /servers/{server_id}/config
|
||||
Get all config sections combined.
|
||||
|
||||
**Response 200:**
|
||||
```json
|
||||
{
|
||||
"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.
|
||||
|
||||
```json
|
||||
{
|
||||
"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.
|
||||
|
||||
```json
|
||||
{
|
||||
"max_bandwidth": 50000000,
|
||||
"max_msg_send": 256
|
||||
}
|
||||
```
|
||||
|
||||
### PUT /servers/{server_id}/config/profile
|
||||
Update difficulty profile. Admin only.
|
||||
|
||||
```json
|
||||
{
|
||||
"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.
|
||||
|
||||
```json
|
||||
{
|
||||
"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.
|
||||
|
||||
```json
|
||||
{
|
||||
"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:**
|
||||
```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 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:**
|
||||
```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 PBO (removes file from disk). Admin only.
|
||||
|
||||
### GET /servers/{server_id}/missions/rotation
|
||||
Get current mission rotation (ordered list).
|
||||
|
||||
**Response 200:**
|
||||
```json
|
||||
{
|
||||
"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.
|
||||
|
||||
```json
|
||||
{
|
||||
"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.
|
||||
|
||||
```json
|
||||
{
|
||||
"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": [
|
||||
{
|
||||
"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.
|
||||
|
||||
```json
|
||||
{ "reason": "AFK" }
|
||||
```
|
||||
|
||||
### POST /servers/{server_id}/players/{player_num}/ban
|
||||
Ban a player via RCon. Admin only.
|
||||
|
||||
```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.
|
||||
|
||||
```json
|
||||
{
|
||||
"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:**
|
||||
```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
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## RCon Endpoints
|
||||
|
||||
### POST /servers/{server_id}/rcon/command
|
||||
Send raw RCon/admin command. Admin only.
|
||||
|
||||
```json
|
||||
{ "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.
|
||||
|
||||
```json
|
||||
{ "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).
|
||||
|
||||
```json
|
||||
{
|
||||
"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
|
||||
|
||||
```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"]`.
|
||||
|
||||
**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.)
|
||||
```json
|
||||
{
|
||||
"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.
|
||||
```json
|
||||
{
|
||||
"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).
|
||||
```json
|
||||
{
|
||||
"type": "players",
|
||||
"server_id": 1,
|
||||
"data": {
|
||||
"players": [
|
||||
{ "player_num": 1, "name": "PlayerOne", "ping": 45, "verified": true }
|
||||
],
|
||||
"count": 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Metrics Update
|
||||
Sent every 5 seconds.
|
||||
```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
|
||||
Sent for significant events (crash, restart, etc.)
|
||||
```json
|
||||
{
|
||||
"type": "event",
|
||||
"server_id": 1,
|
||||
"data": {
|
||||
"event_type": "crashed",
|
||||
"detail": { "exit_code": 1 },
|
||||
"timestamp": "2026-04-16T10:30:00Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Pong
|
||||
```json
|
||||
{ "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 |
|
||||
Reference in New Issue
Block a user