# 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 ``. --- ## 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 \| None` | | Lombok `@Builder` | `@dataclass` or Pydantic with defaults | | `@Value("${prop}")` | `settings.prop` (Pydantic BaseSettings) | | `ResponseEntity` 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 |