Files
languard-servers-manager/MODULES.md
Tran G. (Revernomad) Khoa 6511353b55 feat: implement full backend + frontend server detail, settings, and create server pages
Backend:
- Complete FastAPI backend with 42+ REST endpoints (auth, servers, config,
  players, bans, missions, mods, games, system)
- Game adapter architecture with Arma 3 as first-class adapter
- WebSocket real-time events for status, metrics, logs, players
- Background thread system (process monitor, metrics, log tail, RCon poller)
- Fernet encryption for sensitive config fields at rest
- JWT auth with admin/viewer roles, bcrypt password hashing
- SQLite with WAL mode, parameterized queries, migration system
- APScheduler cleanup jobs for logs, metrics, events

Frontend:
- Server Detail page with 7 tabs (overview, config, players, bans,
  missions, mods, logs)
- Settings page with password change and admin user management
- Create Server wizard (4-step; known bug: silent validation failure)
- New hooks: useServerDetail, useAuth, useGames
- New components: ServerHeader, ConfigEditor, PlayerTable, BanTable,
  MissionList, ModList, LogViewer, PasswordChange, UserManager
- WebSocket onEvent callback for real-time log accumulation
- 120 unit tests passing (Vitest + React Testing Library)

Docs:
- Added .gitignore, CLAUDE.md, README.md
- Updated FRONTEND.md, ARCHITECTURE.md with current implementation state
- Added .env.example for backend configuration

Known issues:
- Create Server form: "Next" buttons don't validate before advancing,
  causing silent submit failure when fields are invalid
- Config sub-tabs need UX redesign for non-technical users
2026-04-17 11:58:34 +07:00

11 KiB

Module Reference

Backend Modules

main.py — Application Factory

  • create_app() — FastAPI app with lifespan, CORS middleware, rate limiter, exception handler
  • lifespan() — Startup: init DB, register adapters, create WebSocket manager, create broadcast thread, create ThreadRegistry, recover processes, reattach threads, seed admin, start scheduler. Shutdown: stop threads, stop broadcast, stop scheduler.

config.py — Settings

  • Settings class (Pydantic BaseSettings) with LANGUARD_ env prefix
  • 13 configurable settings: SECRET_KEY, ENCRYPTION_KEY, DB_PATH, SERVERS_DIR, HOST, PORT, CORS_ORIGINS, log/metrics/player retention days, JWT expiry, Arma3 default exe

database.py — Database Engine

  • get_engine() — Creates SQLite engine with WAL mode and foreign keys
  • run_migrations(engine) — Applies numbered .sql migration files from core/migrations/
  • get_db() — FastAPI dependency yielding a DB connection
  • get_thread_db() — Thread-local DB connection for background threads

dependencies.py — FastAPI Dependencies

  • get_current_user(token) — Decodes JWT, validates user exists
  • require_admin(user) — Returns 403 if user role is not admin
  • get_server_or_404(server_id, db) — Loads server row or raises 404
  • get_adapter_for_server(server_id, db) — Loads server + resolves its game adapter

adapters/protocols.py — Adapter Protocols

7 runtime-checkable Protocol classes:

  • ConfigGenerator — Render config files, build launch args
  • ProcessConfig — Allowed executables, port conventions, directory layout
  • LogParser — Parse log lines, resolve latest log file
  • RemoteAdmin — Connect, send commands, get/kick/ban players
  • MissionManager — List, upload, delete mission files
  • ModManager — Scan mods, set enabled mods, build CLI args
  • BanManager — Sync bans between DB and file
  • Composite GameAdapter — Aggregates all protocols, plus has_capability(), get_additional_routers(), get_custom_thread_factories()

adapters/registry.py — Game Adapter Registry

  • GameAdapterRegistry — Class-level singleton dict (game_type → adapter instance)
  • Methods: register(), get(), all(), list_game_types(), is_registered()

adapters/exceptions.py — Typed Exceptions

  • AdapterError, ConfigWriteError, ConfigValidationError, ConfigMigrationError, LaunchArgsError, RemoteAdminError, ExeNotAllowedError

adapters/__init__.py — Adapter Loading

  • initialize_adapters() — Imports built-in adapters (Arma3), then scans importlib.metadata entry points under "languard.adapters" for third-party plugins

adapters/arma3/ — Arma 3 Adapter

All 7 capabilities implemented:

Module Class Purpose
adapter.py Arma3Adapter Composite adapter declaring all capabilities
config_generator.py Arma3ConfigGenerator 5 Pydantic config models, writes server.cfg/basic.cfg/Arma3Profile/beserver.cfg, builds launch args
process_config.py Arma3ProcessConfig Allowed executables, port conventions (game+1/+2/+3), directory layout
log_parser.py RPTParser Regex-based .rpt log parser, log file resolver
rcon_client.py BERConClient BattlEye RCon v2 UDP protocol implementation
remote_admin.py Arma3RemoteAdmin + Arma3RemoteAdminFactory Implements RemoteAdmin protocol using BERConClient
mission_manager.py Arma3MissionManager .pbo upload, delete, list, rotation config generation
mod_manager.py Arma3ModManager @-prefixed mod scanning, enabled-mod persistence, -mod/-serverMod args
ban_manager.py Arma3BanManager BattlEye bans.txt file sync + DB sync

core/auth/ — Authentication

Module Purpose
router.py 7 endpoints: login, logout, me, change password, user CRUD
service.py AuthService — login, create_user, change_password, seed_admin_if_empty
schemas.py Pydantic models: LoginRequest, CreateUserRequest, ChangePasswordRequest
utils.py JWT creation/validation (HS256), bcrypt password hashing

core/servers/ — Server Management

Module Purpose
router.py Server CRUD, lifecycle (start/stop/restart/kill), config read/write/preview, RCon command
players_router.py Player list, player history
bans_router.py Ban CRUD with bans.txt file sync
missions_router.py Mission list, .pbo upload (500MB), delete
mods_router.py List mods, set enabled mods
service.py ServerService — orchestrates all lifecycle operations, config writes, thread management
schemas.py Pydantic models: CreateServerRequest, UpdateServerRequest, StopServerRequest
process_manager.py ProcessManager singleton — subprocess.Popen lifecycle, PID recovery via psutil

core/games/ — Game Type Discovery

Module Purpose
router.py 4 endpoints: list game types, get game details, config schema, defaults

core/system/ — System Health

Module Purpose
router.py /health (public) + /status (authed)

core/websocket/ — Real-Time Events

Module Purpose
router.py /ws endpoint with JWT auth via query param
manager.py WebSocketManager — asyncio connection registry with server_id subscriptions
broadcast_thread.py BroadcastThread — bridges queue.Queue to asyncio event loop

core/threads/ — Background Threads

Module Purpose
base_thread.py BaseServerThread — abstract base with stop event, thread-local DB, exception backoff
thread_registry.py ThreadRegistry — manages per-server thread bundles, start/stop/reattach
log_tail.py LogTailThread — tails log files, parses lines, persists to DB, broadcasts
process_monitor.py ProcessMonitorThread — detects crashes, triggers auto-restart
metrics_collector.py MetricsCollectorThread — psutil CPU/RAM collection every 10s
remote_admin_poller.py RemoteAdminPollerThread — polls player list via RCon, syncs join/leave events

core/jobs/ — Scheduled Tasks

Module Purpose
scheduler.py APScheduler BackgroundScheduler singleton
cleanup_jobs.py 3 cron jobs: old logs (daily 03:00), old metrics (6h), old events (weekly Sun 04:00)

core/dal/ — Data Access Layer

Module Purpose
base_repository.py BaseRepository — thin wrapper around SQLAlchemy text() queries
server_repository.py ServerRepository — CRUD, status updates, running servers, restart count
config_repository.py ConfigRepository — Per-section upsert with Fernet encryption and optimistic locking
player_repository.py PlayerRepository — Upsert/clear players, player_history queries
ban_repository.py BanRepository — Ban CRUD with active/inactive flag
event_repository.py EventRepository — Insert server events, query, cleanup
log_repository.py LogRepository — Insert parsed log entries, query with filters, cleanup
metrics_repository.py MetricsRepository — Insert CPU/RAM metrics, query by time range, cleanup

core/utils/ — Utilities

Module Purpose
crypto.py Fernet field-level encryption: encrypt(), decrypt(), is_encrypted()
file_utils.py get_server_dir(), ensure_server_dirs(), sanitize_filename(), safe_delete_file()
port_checker.py Port availability checks with cross-server conflict detection

Frontend Modules

src/main.tsx — Entry Point

Renders <App /> into #root with React StrictMode.

src/App.tsx — Root Component

  • Creates QueryClient with stale time 10s, retry 2, refetchOnWindowFocus false
  • Wraps app in QueryClientProvider, BrowserRouter, ReactQueryDevtools
  • Routes: /loginLoginPage, /*ProtectedLayout (auth guard)
  • ProtectedLayout checks isAuthenticated from Zustand, redirects to /login if false

src/lib/api.ts — API Client

  • Axios instance with baseURL from VITE_API_URL env, 30s timeout
  • Request interceptor: reads languard_token from localStorage, adds Authorization: Bearer header
  • Response interceptor: on 401, checks URL prefix — non-auth 401s clear token and redirect to /login; auth 401s (login) are left for the component to handle
  • Exports ApiResponse<T> type: { success, data, error? }

src/store/auth.store.ts — Auth Store (Zustand + persist)

  • Persisted to localStorage under key languard-auth
  • onRehydrateStorage sets isAuthenticated: true if token exists
  • partialize stores only token and user
  • setAuth(token, user) also writes languard_token to localStorage
  • clearAuth() removes both storage keys

src/store/ui.store.ts — UI Store (Zustand, in-memory)

  • sidebarOpen, activeServerId, notifications[]
  • addNotification(type, message) creates toast with auto-remove (5s timeout)
  • removeNotification(id) for manual dismiss

src/hooks/useServers.ts — Server Data Hooks

7 TanStack Query hooks: useServers, useServer, useStartServer, useStopServer, useRestartServer, useCreateServer, useDeleteServer

  • Server interface with all fields
  • useServers refetches every 30s
  • Mutations invalidate relevant cache keys on success

src/hooks/useWebSocket.ts — WebSocket Hook

  • Connects to ws://localhost:8000/ws?token=...&server_id=...
  • Exponential backoff reconnect (2s → 30s max)
  • Close code 4001 = explicit logout (no reconnect)
  • Invalidates TanStack Query caches on server_status, metrics, log, players events
  • Cleans up on unmount (closes WebSocket, clears reconnect timeout)

src/pages/LoginPage.tsx — Login Page

  • React Hook Form + Zod validation (username/password required)
  • apiClient.post("/api/auth/login") on submit
  • setAuth(token, user) + navigate to / on success
  • Error display from catch block
  • Loading state: button changes to "Signing in..."

src/pages/DashboardPage.tsx — Dashboard Page

  • useServers() for data, useWebSocket() for real-time updates
  • Loading state, error state, empty state, content with server grid
  • Server cards rendered via ServerCard inside Link components
  • "X servers configured" counter

src/components/layout/Sidebar.tsx — Sidebar

  • "Languard Server Manager" branding
  • Dashboard link with LayoutDashboard icon
  • Dynamic server list from useServers() with StatusLed dots
  • Settings link at bottom
  • Active server highlighting via URL params

src/components/servers/ServerCard.tsx — Server Card

  • Displays: server name, StatusLed, game type badge
  • 3-column stat grid: Players (current/max), Port, Restarts
  • Action buttons based on server status:
    • Stopped: Start button only
    • Running: Stop + Restart buttons
    • Starting/Restarting: Stop + Restart (disabled)
  • e.preventDefault() + e.stopPropagation() on buttons to prevent parent Link navigation
  • Success/error notifications via useUIStore

src/components/ui/StatusLed.tsx — Status LED

  • Colored dot with CSS class status-led-{status} (5 statuses)
  • Sizes: sm (6px), md (8px)
  • Optional label text via showLabel prop
  • Uses clsx for conditional class merging