Converts Spring Boot 4.0.3 ARMA Server Web GUI to FastAPI/Python. Each phase file is fully self-contained: lists Java source files to read, output files to create, implementation patterns, REST endpoint contracts, and a completion checklist. A future agent can execute any single phase without rescanning the Java project. Phases: - 01: Foundation — SQLAlchemy models, Alembic, settings, base schemas - 02: Auth & Users — JWT middleware, RBAC, user CRUD - 03: CFG parser + server process — server.cfg round-trip, start/stop - 04: Server settings — general/network/logging/security/difficulty - 05: Mod management — mod CRUD, presets, settings, WebSocket progress - 06: Steam integration — SteamCMD queue, Workshop API, python-a2s - 07: Missions, CDLC, Discord, APScheduler jobs - 08: Middleware & polish — global exception handler, SPA redirect, structlog - 09: Testing — pytest-asyncio, respx, 80% coverage target
134 lines
5.4 KiB
Markdown
134 lines
5.4 KiB
Markdown
# ASWG Python Conversion — Master Plan
|
|
|
|
**Source project**: `E:\TestScript\ARMA-Server-Web-Gui` (Java 25 / Spring Boot 4.0.3)
|
|
**Target project**: `E:\TestScript\Arma_Server_Web_Manager` (Python / FastAPI)
|
|
**Goal**: Identical REST API so the Angular 21 frontend works without any changes.
|
|
**Server port**: 8085 (same as Java app)
|
|
|
|
---
|
|
|
|
## Phase Status Tracker
|
|
|
|
Update each phase's Status field when starting (`IN_PROGRESS`) and finishing (`DONE`).
|
|
Each phase file is self-contained — read only the phase file for the current phase.
|
|
|
|
| Phase | File | Status |
|
|
|-------|------|--------|
|
|
| 1 — Skeleton + DB | `phases/phase-01-skeleton-db.md` | PENDING |
|
|
| 2 — Auth + Security | `phases/phase-02-auth-security.md` | PENDING |
|
|
| 3 — CFG Parser + Server Process | `phases/phase-03-cfg-parser-process.md` | PENDING |
|
|
| 4 — Server Settings Subdomains | `phases/phase-04-server-settings.md` | PENDING |
|
|
| 5 — Mod Management | `phases/phase-05-mod-management.md` | PENDING |
|
|
| 6 — Steam Integration | `phases/phase-06-steam-integration.md` | PENDING |
|
|
| 7 — Missions, CDLC, Discord, Jobs | `phases/phase-07-missions-cdlc-discord-jobs.md` | PENDING |
|
|
| 8 — Middleware + Polish | `phases/phase-08-middleware-polish.md` | PENDING |
|
|
| 9 — Testing | `phases/phase-09-testing.md` | PENDING |
|
|
|
|
---
|
|
|
|
## Global Technology Stack
|
|
|
|
```toml
|
|
# pyproject.toml dependencies
|
|
fastapi = ">=0.115"
|
|
uvicorn = {extras = ["standard"], version = ">=0.30"}
|
|
pydantic = ">=2.7"
|
|
pydantic-settings = ">=2.3"
|
|
sqlalchemy = ">=2.0"
|
|
alembic = ">=1.13"
|
|
aiosqlite = ">=0.20"
|
|
PyJWT = ">=2.8"
|
|
passlib = {extras = ["bcrypt"], version = ">=1.7"}
|
|
cryptography = ">=43"
|
|
httpx = ">=0.27"
|
|
psutil = ">=5.9"
|
|
apscheduler = ">=3.10"
|
|
cachetools = ">=5.3"
|
|
python-a2s = ">=1.7"
|
|
structlog = ">=24"
|
|
jproperties = ">=2.1"
|
|
|
|
[dev]
|
|
pytest = ">=8.2"
|
|
pytest-asyncio = ">=0.23"
|
|
pytest-cov = ">=5.0"
|
|
respx = ">=0.21"
|
|
ruff = ">=0.4"
|
|
```
|
|
|
|
---
|
|
|
|
## Java Source Root Abbreviation
|
|
|
|
All Java files are under:
|
|
`E:\TestScript\ARMA-Server-Web-Gui\src\main\java\pl\bartlomiejstepien\armaserverwebgui\`
|
|
|
|
In phase files this is written as `<JAVA_SRC>`.
|
|
|
|
---
|
|
|
|
## Database Schema (all 12 tables)
|
|
|
|
```sql
|
|
installed_mod (id BIGINT PK, workshop_file_id BIGINT, name VARCHAR, directory_path VARCHAR,
|
|
preview_url VARCHAR, created_date TIMESTAMP, enabled BOOLEAN, server_mod BOOLEAN,
|
|
last_workshop_update_date TIMESTAMP, last_workshop_update_attempt_date TIMESTAMP,
|
|
dependencies_ids VARCHAR)
|
|
mod_preset (id BIGINT PK, name VARCHAR UNIQUE)
|
|
mod_preset_entry (id BIGINT PK, mod_preset_id BIGINT FK→mod_preset, mod_id BIGINT, mod_name VARCHAR)
|
|
difficulty_profile (id BIGINT PK, name VARCHAR UNIQUE, active BOOLEAN)
|
|
mission (id BIGINT PK, name VARCHAR UNIQUE, template VARCHAR, enabled BOOLEAN, difficulty VARCHAR)
|
|
mod_settings (id BIGINT PK, name VARCHAR, content TEXT)
|
|
invalid_jwt_token (id BIGINT PK, jwt VARCHAR UNIQUE, invalidated_datetime TIMESTAMP,
|
|
expiration_datetime TIMESTAMP)
|
|
aswg_user (id BIGINT PK, username VARCHAR UNIQUE, password VARCHAR, locked BOOLEAN,
|
|
created_datetime TIMESTAMP, last_success_login_datetime TIMESTAMP)
|
|
authority (id BIGINT PK, code VARCHAR UNIQUE)
|
|
aswg_user_authority (user_id BIGINT FK→aswg_user, authority_id BIGINT FK→authority)
|
|
cdlc (id BIGINT PK, name VARCHAR UNIQUE, enabled BOOLEAN)
|
|
job_execution (id BIGINT PK, job_name VARCHAR, start_date TIMESTAMP, finish_date TIMESTAMP,
|
|
status VARCHAR, message VARCHAR)
|
|
```
|
|
|
|
---
|
|
|
|
## API Contract Rules (CRITICAL — do not deviate)
|
|
|
|
The Angular 21 frontend will NOT be changed. Every endpoint must match exactly:
|
|
- URL path, HTTP method, request field names (camelCase), response field names (camelCase)
|
|
- HTTP status codes: 200 OK, 201 Created, 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 409 Conflict, 500 Internal Server Error
|
|
- Error response shape: `{"code": "ERROR_CODE", "message": "Human readable message"}`
|
|
|
|
Apply to all Pydantic schemas:
|
|
```python
|
|
from pydantic import ConfigDict
|
|
from pydantic.alias_generators import to_camel
|
|
|
|
class BaseSchema(BaseModel):
|
|
model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)
|
|
```
|
|
|
|
---
|
|
|
|
## Global Java→Python Concept Map
|
|
|
|
| Java / Spring | Python / FastAPI |
|
|
|---|---|
|
|
| `@RestController` + `@RequestMapping` | `APIRouter(prefix="/api/v1/...")` |
|
|
| `@GetMapping` / `@PostMapping` / `@DeleteMapping` / `@PutMapping` | `@router.get()` / `.post()` / `.delete()` / `.put()` |
|
|
| `@RequestBody` | Pydantic model as function parameter |
|
|
| `@PathVariable` | Path param `{name}` in route string |
|
|
| `@RequestParam` | `Query(...)` |
|
|
| `@RequestPart` multipart | `UploadFile` + `Form(...)` |
|
|
| `@HasPermission*` annotation | `Depends(has_permission(AswgAuthority.XYZ))` |
|
|
| `@Service` / `@Component` | Plain class, instantiated in `dependencies.py` |
|
|
| `@Transactional` | `async with session.begin():` |
|
|
| JPA `@Entity` | SQLAlchemy `Base` subclass |
|
|
| `Optional<T>` | `T \| None` |
|
|
| Lombok `@Builder` | `@dataclass` or Pydantic with defaults |
|
|
| `@Value("${prop}")` | `settings.prop` (Pydantic BaseSettings) |
|
|
| `ResponseEntity<T>` with status | Return value + `status_code=` on route |
|
|
| `SseEmitter` | `StreamingResponse(media_type="text/event-stream")` |
|
|
| Spring WebSocket | FastAPI `WebSocket` route parameter |
|
|
| `@ConditionalOnProperty` | `if settings.prop:` guard at startup/registration |
|