Files
arma-server-web-manager/phases/phase-04-server-settings.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

7.3 KiB
Raw Blame History

Phase 4 — Server Settings Subdomains

Status: PENDING Depends on: Phase 3 complete (CFG parser + ServerConfigStorage must exist) Next phase: phase-05-mod-management.md


Goal

Five settings services that read/write Arma .cfg files and their REST endpoints. All follow the same read→model→return / receive→model→write pattern.

After this phase: general, network, security, logging settings and difficulty profiles work end-to-end.


Java Source Files to Read

<JAVA_SRC> = E:\TestScript\ARMA-Server-Web-Gui\src\main\java\pl\bartlomiejstepien\armaserverwebgui\

File What to extract
<JAVA_SRC>domain/server/general/GeneralServiceImpl.java getGeneralProperties(), saveGeneralProperties()
<JAVA_SRC>domain/server/general/model/GeneralProperties.java Field names/types
<JAVA_SRC>domain/server/network/ServerNetworkServiceImpl.java getNetworkProperties(), saveNetworkProperties()
<JAVA_SRC>domain/server/network/model/NetworkProperties.java Fields
<JAVA_SRC>domain/server/network/model/KickTimeoutType.java Enum values
<JAVA_SRC>domain/server/logging/LoggingServiceImpl.java getLoggingProperties(), getLatestLogs()
<JAVA_SRC>domain/server/logging/model/LoggingProperties.java Fields
<JAVA_SRC>domain/server/difficulty/DifficultyServiceImpl.java Full implementation (DB + filesystem)
<JAVA_SRC>domain/server/difficulty/model/DifficultyProfile.java Fields
<JAVA_SRC>domain/server/difficulty/DifficultyScanJob.java Filesystem scan logic
<JAVA_SRC>web/GeneralController.java Exact paths, JSON shapes
<JAVA_SRC>web/ServerNetworkRestController.java Exact paths, JSON shapes
<JAVA_SRC>web/LoggingRestController.java Properties endpoints (SSE already done in Phase 3)
<JAVA_SRC>web/ServerSecurityRestController.java Exact paths, JSON shapes
<JAVA_SRC>web/DifficultyRestController.java Exact paths, JSON shapes
<JAVA_SRC>web/request/SaveGeneralProperties.java Request fields
<JAVA_SRC>web/request/NetworkPropertiesRequest.java Request fields
<JAVA_SRC>web/request/SaveServerSecurityRequest.java Request fields
<JAVA_SRC>web/response/GeneralPropertiesResponse.java Response fields
<JAVA_SRC>web/response/NetworkPropertiesResponse.java Response fields
<JAVA_SRC>web/model/DifficultyProfileApiModel.java API model fields

Output Files to Create

src/domain/server/general/__init__.py
src/domain/server/general/general_service.py
src/domain/server/general/models.py
src/domain/server/network/__init__.py
src/domain/server/network/network_service.py
src/domain/server/network/models.py
src/domain/server/logging_settings/__init__.py
src/domain/server/logging_settings/logging_service.py
src/domain/server/logging_settings/models.py
src/domain/server/security_settings/__init__.py
src/domain/server/security_settings/security_service.py
src/domain/server/security_settings/models.py
src/domain/server/difficulty/__init__.py
src/domain/server/difficulty/difficulty_service.py
src/domain/server/difficulty/jobs.py            (DifficultyScanJob — registered in Phase 7)
src/domain/server/difficulty/models.py
src/web/schemas/general.py
src/web/schemas/network.py
src/web/schemas/security_settings.py
src/web/schemas/difficulty.py
src/web/general_router.py
src/web/network_router.py
src/web/logging_router.py
src/web/security_router.py
src/web/difficulty_router.py

Update src/main.py: register all 5 routers.


Implementation Notes

Common service pattern (general / network / logging / security)

class GeneralService:
    def __init__(self, config_storage: ServerConfigStorage):
        self.storage = config_storage

    async def get_properties(self) -> GeneralProperties:
        cfg = self.storage.read()
        return GeneralProperties(hostname=cfg.hostname, ...)

    async def save_properties(self, props: GeneralProperties) -> None:
        cfg = self.storage.read()
        cfg.hostname = props.hostname
        # ... apply all fields
        self.storage.write(cfg)

Apply the same pattern to Network, Logging, and Security services.

Difficulty service — DB + filesystem

DifficultyServiceImpl.java manages profiles in both the DB and <server_dir>/Users/*.Arma3Profile files.

class DifficultyService:
    async def get_profiles(self) -> list[DifficultyProfile]:
        # Read from difficulty_profile table + scan filesystem for .Arma3Profile files

    async def save_profile(self, profile: DifficultyProfile) -> None:
        # Write to DB + write .Arma3Profile via cfg_writer

    async def delete_profile(self, id: int | None, name: str | None) -> None:
        # Remove from DB + delete file

    async def set_active(self, id: int) -> None:
        # Mark active in DB; only one can be active at a time

DifficultyScanJob: scans <server_dir>/Users/ for new .Arma3Profile files and inserts them into the DB if not already present. Register this job with APScheduler in Phase 7 — do not register it here.

REST endpoints

General — permissions: GENERAL_SETTINGS_VIEW, GENERAL_SETTINGS_SAVE

GET  /api/v1/general/properties   → GeneralPropertiesResponse
POST /api/v1/general/properties   body: SaveGeneralProperties → 200

Network — permissions: NETWORK_SETTINGS_VIEW, NETWORK_SETTINGS_SAVE

GET  /api/v1/network/properties   → NetworkPropertiesResponse
POST /api/v1/network/properties   body: NetworkPropertiesRequest → 200

Logging — permission: LOGS_VIEW

GET  /api/v1/logging/properties        → LoggingPropertiesResponse
POST /api/v1/logging/properties        body: LoggingPropertiesRequest → 200
GET  /api/v1/logging/latest-logs       → list[str]

Note: GET /api/v1/logging/logs-sse was registered in Phase 3.

Security settings — permissions: SECURITY_SETTINGS_VIEW, SECURITY_SETTINGS_SAVE

GET  /api/v1/security   → ServerSecurityResponse
POST /api/v1/security   body: SaveServerSecurityRequest → 200

Difficulty — permissions: DIFFICULTY_VIEW, DIFFICULTY_ADD, DIFFICULTY_UPDATE, DIFFICULTY_DELETE

GET    /api/v1/difficulties            → list[DifficultyProfileApiModel]
POST   /api/v1/difficulties            body: DifficultyProfileApiModel → 201
PUT    /api/v1/difficulties/{id}       body: DifficultyProfileApiModel → 200
DELETE /api/v1/difficulties/{id}                                       → 200
DELETE /api/v1/difficulties?name={n}                                   → 200

Completion Checklist

  • GET /api/v1/general/properties returns correct camelCase JSON
  • POST /api/v1/general/properties persists to .cfg file
  • GET /api/v1/network/properties returns correct JSON
  • POST /api/v1/network/properties persists to .cfg file
  • GET /api/v1/logging/properties returns correct JSON
  • GET /api/v1/logging/latest-logs returns list of log line strings
  • GET /api/v1/security returns correct JSON
  • GET /api/v1/difficulties returns profiles list
  • POST /api/v1/difficulties creates profile in DB and .Arma3Profile file
  • DELETE /api/v1/difficulties/{id} removes profile from DB and filesystem
  • All endpoints return 403 without required permission

Contract for Phase 5

Phase 5 has no direct dependency on Phase 4. It imports only from Phases 13.