Files
arma-server-web-manager/PLAN.md
Khoa (Revenovich) Tran Gia e02db3ddde feat: add Java→Python migration plan with 9 self-contained phase files
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
2026-04-14 15:06:56 +07:00

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 FKmod_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 FKaswg_user, authority_id BIGINT FKauthority)
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 |