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,65 @@
"""Game-agnostic file operations."""
from __future__ import annotations
import re
from pathlib import Path
def get_server_dir(server_id: int) -> Path:
"""Return the absolute directory path for a server's data."""
from config import settings
base = Path(settings.servers_dir).resolve()
return base / str(server_id)
def ensure_server_dirs(server_id: int, layout: list[str] | None = None) -> None:
"""
Create servers/{id}/ and any subdirectories from adapter layout.
layout example: ["server", "battleye", "mpmissions"]
"""
server_dir = get_server_dir(server_id)
server_dir.mkdir(parents=True, exist_ok=True)
if layout:
for subdir in layout:
(server_dir / subdir).mkdir(parents=True, exist_ok=True)
def safe_delete_file(path: Path) -> bool:
"""Delete a file if it exists. Returns True if deleted."""
try:
path.unlink(missing_ok=True)
return True
except OSError:
return False
def sanitize_filename(filename: str) -> str:
"""
Sanitize a filename for safe disk storage.
Rules:
- Strip path separators (/ \\ and ..)
- Allow only alphanumeric, dots, hyphens, underscores, @ signs
- Collapse consecutive dots (prevent ../ tricks)
- Truncate to 255 characters
- Raise ValueError if the result is empty
"""
# Take only the basename — strip any directory components
filename = filename.replace("\\", "/").split("/")[-1]
# Remove null bytes and control characters
filename = re.sub(r"[\x00-\x1f\x7f]", "", filename)
# Allow only safe characters: alphanum, dot, hyphen, underscore, @
filename = re.sub(r"[^\w.\-@]", "_", filename)
# Collapse consecutive dots to prevent tricks like ".../.."
filename = re.sub(r"\.{2,}", ".", filename)
# Truncate
filename = filename[:255]
if not filename or filename in (".", ".."):
raise ValueError(f"Filename '{filename}' is not safe for storage")
return filename