docs: expand README quick start with full dev/debug setup
- 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
This commit is contained in:
45
CLAUDE.md
45
CLAUDE.md
@@ -3,16 +3,17 @@
|
|||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Backend (from backend/)
|
# Backend (from backend/, venv must be active)
|
||||||
python -m uvicorn main:app --host 0.0.0.0 --port 8000 --reload
|
uvicorn main:app --host 0.0.0.0 --port 8000 --reload
|
||||||
|
|
||||||
# Frontend (from frontend/)
|
# Frontend (from frontend/)
|
||||||
npx vite --host
|
npm run dev
|
||||||
```
|
```
|
||||||
|
|
||||||
- Backend API: http://localhost:8000 (docs: http://localhost:8000/docs)
|
- Backend API: http://localhost:8000 (docs: http://localhost:8000/docs)
|
||||||
- Frontend: http://localhost:5173
|
- Frontend: http://localhost:5173 (Vite proxies /api and /ws to :8000)
|
||||||
- Default admin: `admin` / (random, printed at first startup; reset via `python -c "from core.auth.utils import hash_password; print(hash_password('admin123'))"` then update SQLite)
|
- Default admin: `admin` / (random, printed at first startup)
|
||||||
|
- Full setup instructions (secrets, venv, debug configs): see README.md
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
@@ -94,29 +95,13 @@ cd frontend && npx tsc --noEmit
|
|||||||
- **Arma 3 per-mission params**: `ServerConfig.missions` is now `list[MissionRotationItem]` (adds optional `params: dict`). A new `default_mission_params` field holds server-wide defaults. Config version bumped to `"1.1.0"`. `_render_server_cfg()` now emits a `class Missions { ... }` block when the rotation is non-empty; `class Params` inside each mission uses per-mission params → global defaults → omit (in that priority order). The `MissionRotationEntry.params` is edited per-row in the Missions tab via `MissionParamsEditor`; `default_mission_params` is edited in the Config tab via the `key-value` widget.
|
- **Arma 3 per-mission params**: `ServerConfig.missions` is now `list[MissionRotationItem]` (adds optional `params: dict`). A new `default_mission_params` field holds server-wide defaults. Config version bumped to `"1.1.0"`. `_render_server_cfg()` now emits a `class Missions { ... }` block when the rotation is non-empty; `class Params` inside each mission uses per-mission params → global defaults → omit (in that priority order). The `MissionRotationEntry.params` is edited per-row in the Missions tab via `MissionParamsEditor`; `default_mission_params` is edited in the Config tab via the `key-value` widget.
|
||||||
- **Config version migration**: `migrate_config("1.0.0", ...)` backfills `params: {}` on each existing rotation entry and adds `default_mission_params: {}`. `normalize_section()` does the same on reads for stored rows that pre-date the migration run.
|
- **Config version migration**: `migrate_config("1.0.0", ...)` backfills `params: {}` on each existing rotation entry and adds `default_mission_params: {}`. `normalize_section()` does the same on reads for stored rows that pre-date the migration run.
|
||||||
|
|
||||||
## Known Bugs — Mods Tab (fix next session)
|
## Mods Tab — Implementation Notes
|
||||||
|
|
||||||
Three bugs prevent the Mods tab from working correctly:
|
- 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}]}`
|
||||||
### Bug 1 — Save fails with TypeError (critical)
|
- Old string-list format is auto-migrated to the dict format on read
|
||||||
`Arma3ModManager.set_enabled_mods()` calls `config_repo.upsert_section()` with wrong keyword argument names:
|
- `is_server_mod: true` → `-serverMod=` arg; `false` → `-mod=` arg
|
||||||
- Passes `data=` → should be `config_data=`
|
- `list_available_mods()` scans `{server_dir}/mods/` for `@*` directories
|
||||||
- Passes `expected_version=` → should be `expected_config_version=`
|
- `set_enabled_mods()` stores the new dict format; validates names against disk
|
||||||
- Missing required `game_type=` argument
|
- Server start reads mods from `game_configs` via `config_repo`, NOT from the dead `server_mods` table
|
||||||
- Missing required `schema_version=` argument
|
- Directory scaffold: all 4 Arma3 subdirs (`server/`, `battleye/`, `mpmissions/`, `mods/`) are created on server create and backfilled on startup; each gets a `README.txt` if not already present
|
||||||
|
|
||||||
**File:** `backend/adapters/arma3/mod_manager.py`, `set_enabled_mods()` method (~line 127)
|
|
||||||
|
|
||||||
### Bug 2 — Mods not applied on server start (critical)
|
|
||||||
`service.py` `start_server()` reads mods from a `server_mods` JOIN `mods` table (SQLAlchemy query, ~line 246) — but those tables are never populated by the Mods tab UI. The correct source is `config_repo.get_section(server_id, "mods")["enabled_mods"]`. The start flow needs to read from `config_repo` instead of the dead `server_mods` table join.
|
|
||||||
|
|
||||||
**File:** `backend/core/servers/service.py`, `start_server()` method (~line 242–255)
|
|
||||||
|
|
||||||
### Bug 3 — Wrong mod folder location (UX)
|
|
||||||
`list_available_mods()` scans the server root (`get_server_dir()`) for `@*` folders. Mods should live in a `mods/` subfolder: `{server_dir}/mods/@ModName`. Needs:
|
|
||||||
1. Change scan path: `server_dir / "mods"` instead of `server_dir`
|
|
||||||
2. Ensure the `mods/` subdirectory is created by `ensure_server_dirs` (add `"mods"` to the Arma3 `get_server_dir_layout()`)
|
|
||||||
3. Update CLAUDE.md + user docs to say mods go in `D:/ImContainer/Arma3Server/{id}/mods/@ModName`
|
|
||||||
|
|
||||||
**File:** `backend/adapters/arma3/mod_manager.py`, `list_available_mods()` and `_server_dir()` (~line 47–88)
|
|
||||||
Also: `backend/adapters/arma3/adapter.py`, `get_server_dir_layout()` (add `"mods"` entry)
|
|
||||||
125
README.md
125
README.md
@@ -19,25 +19,78 @@ A multi-game server management platform with a Python/FastAPI backend and React/
|
|||||||
- **TanStack Query v5** — server state management
|
- **TanStack Query v5** — server state management
|
||||||
- **Zustand 5** — client state (auth, UI)
|
- **Zustand 5** — client state (auth, UI)
|
||||||
- **Tailwind CSS** — dark neumorphic design system
|
- **Tailwind CSS** — dark neumorphic design system
|
||||||
- **Playwright** — E2E testing (38 tests)
|
- **Playwright** — E2E testing
|
||||||
- **Vitest** + **React Testing Library** — unit tests (167 tests)
|
- **Vitest** + **React Testing Library** — unit tests (173 tests)
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
### Backend
|
### 1 — Backend setup
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd backend
|
cd backend
|
||||||
|
|
||||||
|
# Create and activate a virtual environment
|
||||||
python -m venv venv
|
python -m venv venv
|
||||||
source venv/bin/activate # Windows: venv\Scripts\activate
|
source venv/bin/activate # macOS / Linux
|
||||||
|
# venv\Scripts\activate # Windows (cmd)
|
||||||
|
# venv\Scripts\Activate.ps1 # Windows (PowerShell)
|
||||||
|
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
cp .env.example .env # Edit with your settings
|
|
||||||
uvicorn main:app --reload
|
|
||||||
```
|
```
|
||||||
|
|
||||||
First run prints a generated admin password. Change it immediately via `PUT /api/auth/password`.
|
**Generate required secrets** (one-time):
|
||||||
|
|
||||||
### Frontend
|
```bash
|
||||||
|
# Secret key (JWT signing)
|
||||||
|
openssl rand -hex 32
|
||||||
|
|
||||||
|
# Fernet encryption key (sensitive config fields at rest)
|
||||||
|
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
|
||||||
|
```
|
||||||
|
|
||||||
|
Copy `.env.example` to `.env` and fill in the two keys:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp .env.example .env # then open .env in your editor
|
||||||
|
```
|
||||||
|
|
||||||
|
```ini
|
||||||
|
# .env — minimum required values
|
||||||
|
LANGUARD_SECRET_KEY=<output of openssl command>
|
||||||
|
LANGUARD_ENCRYPTION_KEY=<output of Fernet command>
|
||||||
|
LANGUARD_ARMA3_DEFAULT_EXE=C:/path/to/arma3server_x64.exe
|
||||||
|
```
|
||||||
|
|
||||||
|
**Start the backend** (development — auto-reload on file changes):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uvicorn main:app --host 0.0.0.0 --port 8000 --reload
|
||||||
|
```
|
||||||
|
|
||||||
|
First run prints a randomly-generated admin password to the console. Log in and change it immediately via Settings → Change Password (or `PUT /api/auth/password`).
|
||||||
|
|
||||||
|
- API root: `http://localhost:8000`
|
||||||
|
- Interactive docs: `http://localhost:8000/docs`
|
||||||
|
|
||||||
|
**Debug in VS Code:** add this `launch.json` configuration:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "Backend — uvicorn",
|
||||||
|
"type": "debugpy",
|
||||||
|
"request": "launch",
|
||||||
|
"module": "uvicorn",
|
||||||
|
"args": ["main:app", "--host", "0.0.0.0", "--port", "8000"],
|
||||||
|
"cwd": "${workspaceFolder}/backend",
|
||||||
|
"env": { "PYTHONDONTWRITEBYTECODE": "1" },
|
||||||
|
"jinja": true,
|
||||||
|
"justMyCode": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2 — Frontend setup
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd frontend
|
cd frontend
|
||||||
@@ -45,28 +98,64 @@ npm install
|
|||||||
npm run dev
|
npm run dev
|
||||||
```
|
```
|
||||||
|
|
||||||
Opens at `http://localhost:5173`. The dev server proxies `/api` to the backend on port 8000.
|
The Vite dev server starts at `http://localhost:5173` and automatically proxies:
|
||||||
|
- `/api/*` → `http://localhost:8000` (REST)
|
||||||
|
- `/ws/*` → `ws://localhost:8000` (WebSocket)
|
||||||
|
|
||||||
|
**Debug in VS Code:** install the [JavaScript Debugger](https://marketplace.visualstudio.com/items?itemName=ms-vscode.js-debug) extension (bundled by default), then add:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "Frontend — Vite (Chrome)",
|
||||||
|
"type": "chrome",
|
||||||
|
"request": "launch",
|
||||||
|
"url": "http://localhost:5173",
|
||||||
|
"webRoot": "${workspaceFolder}/frontend/src",
|
||||||
|
"sourceMapPathOverrides": {
|
||||||
|
"/@fs/*": "${workspaceFolder}/frontend/*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Start the Vite dev server first (`npm run dev`), then launch this config to attach Chrome DevTools with source-map support.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Running Tests
|
## Running Tests
|
||||||
|
|
||||||
### Frontend Unit Tests
|
### Backend
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd backend
|
||||||
|
source venv/bin/activate # (if not already active)
|
||||||
|
|
||||||
|
pytest # all tests
|
||||||
|
pytest tests/adapters/arma3/ -v # adapter tests only
|
||||||
|
pytest --tb=short -q # quiet output
|
||||||
|
```
|
||||||
|
|
||||||
|
### Frontend unit tests
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd frontend
|
cd frontend
|
||||||
npm test # Watch mode
|
npm test # single run (CI-friendly)
|
||||||
npx vitest run # Single run
|
npm run test:watch # watch mode during development
|
||||||
npx vitest run --coverage # With coverage
|
npx vitest run --coverage # with coverage report
|
||||||
```
|
```
|
||||||
|
|
||||||
### Frontend E2E Tests
|
### Frontend E2E tests (Playwright)
|
||||||
|
|
||||||
|
Start the backend and the Vite dev server first, then:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd frontend
|
cd frontend
|
||||||
# Start backend + frontend dev server first
|
npm run test:e2e # all E2E tests (headless)
|
||||||
npx playwright test # All tests (mocked + integration)
|
npm run test:e2e:ui # Playwright UI mode (interactive, great for debugging)
|
||||||
npx playwright tests-e2e/integration/ # Full-stack integration tests only
|
npx playwright test --headed # watch tests run in an actual browser
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Integration tests (require a live backend) live in `tests-e2e/integration/`. All other tests use API mocks and run without a backend.
|
||||||
|
|
||||||
## Project Structure
|
## Project Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -101,7 +190,7 @@ languard-servers-manager/
|
|||||||
│ │ ├── hooks/ # useServers, useServerDetail, useAuth, useGames, useWebSocket
|
│ │ ├── hooks/ # useServers, useServerDetail, useAuth, useGames, useWebSocket
|
||||||
│ │ ├── store/ # auth.store, ui.store (Zustand)
|
│ │ ├── store/ # auth.store, ui.store (Zustand)
|
||||||
│ │ ├── lib/ # api.ts (Axios client)
|
│ │ ├── lib/ # api.ts (Axios client)
|
||||||
│ │ └── __tests__/ # Vitest unit tests (~120 tests)
|
│ │ └── __tests__/ # Vitest unit tests (173 tests)
|
||||||
│ ├── tests-e2e/ # Playwright E2E tests
|
│ ├── tests-e2e/ # Playwright E2E tests
|
||||||
│ └── playwright.config.ts
|
│ └── playwright.config.ts
|
||||||
├── API.md # REST + WebSocket API reference
|
├── API.md # REST + WebSocket API reference
|
||||||
|
|||||||
Reference in New Issue
Block a user