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
This commit is contained in:
Tran G. (Revernomad) Khoa
2026-04-17 11:58:34 +07:00
parent 620429c9b8
commit 6511353b55
119 changed files with 13752 additions and 5000 deletions

View File

@@ -0,0 +1,135 @@
"""
Arma3RemoteAdmin — implements the RemoteAdmin protocol using BERConClient.
"""
from __future__ import annotations
import logging
from adapters.arma3.rcon_client import BERConClient
from adapters.exceptions import RemoteAdminError
logger = logging.getLogger(__name__)
class Arma3RemoteAdmin:
"""
RemoteAdmin protocol implementation for Arma3 BattlEye RCon.
Args:
server_id: Database server ID.
host: RCon host (usually 127.0.0.1).
port: RCon port (usually game_port + 3).
password: RCon password.
"""
def __init__(
self,
server_id: int,
host: str,
port: int,
password: str,
) -> None:
self._server_id = server_id
self._client = BERConClient(host=host, port=port, password=password)
# ── RemoteAdmin protocol ──
def connect(self) -> None:
"""Connect to RCon. Raises RemoteAdminError on failure."""
try:
self._client.connect()
except ConnectionError as exc:
raise RemoteAdminError(str(exc)) from exc
def disconnect(self) -> None:
self._client.disconnect()
def is_connected(self) -> bool:
return self._client.is_connected
def get_players(self) -> list[dict]:
"""Fetch current player list."""
try:
return self._client.get_players()
except Exception as exc:
raise RemoteAdminError(f"get_players failed: {exc}") from exc
def send_command(self, command: str, timeout: float = 5.0) -> str | None:
"""Send an arbitrary RCon command."""
try:
return self._client.send_command(command)
except Exception as exc:
raise RemoteAdminError(f"send_command failed: {exc}") from exc
def kick_player(self, player_number: int, reason: str = "") -> bool:
"""Kick a player by their in-game slot number."""
command = f"kick {player_number}"
if reason:
command += f" {reason}"
try:
self._client.send_command(command)
return True
except Exception as exc:
logger.warning("[%s] kick_player failed for player %d: %s", self._server_id, player_number, exc)
return False
def ban_player(self, player_uid: str, duration_minutes: int = 0, reason: str = "") -> bool:
"""Add a GUID ban. duration_minutes=0 means permanent."""
duration = duration_minutes if duration_minutes > 0 else 0
command = f"addBan {player_uid} {duration} {reason}"
try:
self._client.send_command(command)
return True
except Exception as exc:
logger.warning("[%s] ban_player failed: %s", self._server_id, exc)
return False
def say_all(self, message: str) -> bool:
"""Broadcast a message to all players."""
try:
self._client.send_command(f"say -1 {message}")
return True
except Exception as exc:
logger.warning("[%s] say_all failed: %s", self._server_id, exc)
return False
def shutdown(self) -> bool:
"""Shutdown the game server via RCon."""
try:
self._client.send_command("#shutdown")
return True
except Exception as exc:
logger.warning("[%s] shutdown failed: %s", self._server_id, exc)
return False
def keepalive(self) -> None:
"""Send keepalive if idle."""
self._client.keepalive()
class Arma3RemoteAdminFactory:
"""
RemoteAdmin factory for Arma3.
Implements the RemoteAdmin protocol (create_client, get_startup_delay, etc.).
"""
def create_client(self, host: str, port: int, password: str) -> Arma3RemoteAdmin:
"""Create a new Arma3RemoteAdmin client instance."""
return Arma3RemoteAdmin(
server_id=0, # Will be set by caller
host=host,
port=port,
password=password,
)
def get_startup_delay(self) -> float:
"""Seconds to wait after server start before connecting."""
return 30.0
def get_poll_interval(self) -> float:
"""Seconds between player list polls."""
return 10.0
def get_player_data_schema(self):
"""Pydantic model for players.game_data JSON."""
return None