- Step-by-step backend setup: venv, secret key generation (openssl + Fernet), .env configuration with annotated minimum values - VS Code launch.json snippets for backend (debugpy/uvicorn) and frontend (Chrome with source maps) - Vite proxy detail (/api → :8000, /ws → ws://:8000) - Backend pytest commands alongside existing frontend test section - Playwright headed/UI mode instructions for E2E debugging - Updated unit test count to 173; removed stale E2E count - CLAUDE.md quick start trimmed to point at README for full setup
6.2 KiB
6.2 KiB
Languard Server Manager
Quick Start
# Backend (from backend/, venv must be active)
uvicorn main:app --host 0.0.0.0 --port 8000 --reload
# Frontend (from frontend/)
npm run dev
- Backend API: http://localhost:8000 (docs: http://localhost:8000/docs)
- Frontend: http://localhost:5173 (Vite proxies /api and /ws to :8000)
- Default admin:
admin/ (random, printed at first startup) - Full setup instructions (secrets, venv, debug configs): see README.md
Architecture
FastAPI + SQLite backend, React 19 + TypeScript + Vite frontend. See ARCHITECTURE.md for full details.
Key Rules
- Frontend types must match API response shapes, NOT database schema columns
- There is no REST endpoint for logs — logs are only pushed via WebSocket events
- WebSocket
onEventcallback is the mechanism for receiving real-time log entries - Config updates use optimistic locking (config_version) — 409 on conflict
- Sensitive config fields are encrypted at rest with Fernet
Current Implementation Status
Backend: Fully implemented (42+ endpoints)
All routers, services, repositories, game adapter system, WebSocket, background threads, and scheduled cleanup are complete.
Frontend: Fully implemented (baseline)
| Route | Status | Notes |
|---|---|---|
/login |
Complete | Zod + react-hook-form validation |
/ |
Complete | Dashboard with server grid + Start/Stop/Restart quick actions |
/servers/:id |
Complete | 7-tab detail page (overview, config, players, bans, missions, mods, logs) |
/servers/new |
Complete | 4-step wizard with per-step validation via trigger() |
/settings |
Complete | Password change + admin user management |
Frontend Type Mapping (API → Frontend)
| API Resource | Frontend Type | Key Fields |
|---|---|---|
| Server (enriched) | Server in useServers.ts |
game_port, current_players, max_players, cpu_percent, ram_mb |
| Mission | Mission in useServerDetail.ts |
name, filename, size_bytes, terrain |
| Mod | Mod in useServerDetail.ts |
name, path, size_bytes, enabled, display_name, workshop_id |
| Ban | Ban in useServerDetail.ts |
id, server_id, guid, name, reason, banned_by, banned_at, expires_at, is_active, game_data |
| Player | Player in useServerDetail.ts |
id, slot_id, name, guid, ip, ping |
| LogFile | LogFile in useServerDetail.ts |
filename, size_bytes, modified_at |
UX Enhancement Plan — ALL PHASES COMPLETE
Plan file: .claude/plan/arma3-ux-enhancement.md
| Phase | Feature | Status |
|---|---|---|
| 1 | Config field UI widgets (textarea/toggle/select/tag-list per field) | Done |
| 2 | Mission rotation table + multi-file upload | Done |
| 3 | Mod display names (mod.cpp) + split-pane selector | Done |
| 4 | Player Kick/Ban from Players tab via RCon | Done |
| 5 | Historical log file browser + live log level filter | Done |
Endpoints added:
GET /api/servers/{id}/config/schema— per-field widget hintsGET|PUT /api/servers/{id}/missions/rotation— mission rotation with optimistic lockingPOST /api/servers/{id}/players/{slot_id}/kick— kick via RConPOST /api/servers/{id}/players/{slot_id}/ban— ban via RCon + DB recordGET /api/servers/{id}/logfiles— list.rptlog filesGET /api/servers/{id}/logfiles/{filename}/download— download log fileDELETE /api/servers/{id}/logfiles/{filename}— delete log file
Test Commands
# Frontend unit tests
cd frontend && npx vitest run
# Frontend type check
cd frontend && npx tsc --noEmit
# Backend (no test suite yet)
Key Implementation Notes
BanRepository.create()takesexpires_at(ISO string), notduration_minutes— convert in serviceslot_idis stored as a string in theplayerstable — cast withstr(slot_id)in queries- Config field names in
ServerConfigPydantic model:password_admin(notadmin_password),battleye(notbattle_eye),disable_von(notvon) - Arma 3 log files are located at
{exe_path_parent}/server/*.rpt(next to the .exe), NOT in languard'sservers/{id}/data directory. Code that finds log files must usePath(server["exe_path"]).parentto resolve the log directory. - Config UI schema now covers all ~80 Arma 3 fields across 5 sections (server, basic, profile, launch, rcon) with per-field widget hints (text, toggle, select, number, password, tag-list, hidden, textarea, key-value). The
missionsfield in the server section is markedhiddenbecause mission rotation is managed via the dedicated Missions tab. - Arma 3 per-mission params:
ServerConfig.missionsis nowlist[MissionRotationItem](adds optionalparams: dict). A newdefault_mission_paramsfield holds server-wide defaults. Config version bumped to"1.1.0"._render_server_cfg()now emits aclass Missions { ... }block when the rotation is non-empty;class Paramsinside each mission uses per-mission params → global defaults → omit (in that priority order). TheMissionRotationEntry.paramsis edited per-row in the Missions tab viaMissionParamsEditor;default_mission_paramsis edited in the Config tab via thekey-valuewidget. - Config version migration:
migrate_config("1.0.0", ...)backfillsparams: {}on each existing rotation entry and addsdefault_mission_params: {}.normalize_section()does the same on reads for stored rows that pre-date the migration run.
Mods Tab — Implementation Notes
- Mods go in
{server_data_dir}/{server_id}/mods/@ModName(e.g.D:/ImContainer/Arma3Server/1/mods/@CBA_A3/) - Enabled mods config schema:
{"enabled_mods": [{"name": "@CBA_A3", "is_server_mod": false}]}- Old string-list format is auto-migrated to the dict format on read
is_server_mod: true→-serverMod=arg;false→-mod=arg
list_available_mods()scans{server_dir}/mods/for@*directoriesset_enabled_mods()stores the new dict format; validates names against disk- Server start reads mods from
game_configsviaconfig_repo, NOT from the deadserver_modstable - Directory scaffold: all 4 Arma3 subdirs (
server/,battleye/,mpmissions/,mods/) are created on server create and backfilled on startup; each gets aREADME.txtif not already present