# 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 `` = `E:\TestScript\ARMA-Server-Web-Gui\src\main\java\pl\bartlomiejstepien\armaserverwebgui\` | File | What to extract | |------|----------------| | `domain/server/general/GeneralServiceImpl.java` | getGeneralProperties(), saveGeneralProperties() | | `domain/server/general/model/GeneralProperties.java` | Field names/types | | `domain/server/network/ServerNetworkServiceImpl.java` | getNetworkProperties(), saveNetworkProperties() | | `domain/server/network/model/NetworkProperties.java` | Fields | | `domain/server/network/model/KickTimeoutType.java` | Enum values | | `domain/server/logging/LoggingServiceImpl.java` | getLoggingProperties(), getLatestLogs() | | `domain/server/logging/model/LoggingProperties.java` | Fields | | `domain/server/difficulty/DifficultyServiceImpl.java` | Full implementation (DB + filesystem) | | `domain/server/difficulty/model/DifficultyProfile.java` | Fields | | `domain/server/difficulty/DifficultyScanJob.java` | Filesystem scan logic | | `web/GeneralController.java` | Exact paths, JSON shapes | | `web/ServerNetworkRestController.java` | Exact paths, JSON shapes | | `web/LoggingRestController.java` | Properties endpoints (SSE already done in Phase 3) | | `web/ServerSecurityRestController.java` | Exact paths, JSON shapes | | `web/DifficultyRestController.java` | Exact paths, JSON shapes | | `web/request/SaveGeneralProperties.java` | Request fields | | `web/request/NetworkPropertiesRequest.java` | Request fields | | `web/request/SaveServerSecurityRequest.java` | Request fields | | `web/response/GeneralPropertiesResponse.java` | Response fields | | `web/response/NetworkPropertiesResponse.java` | Response fields | | `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) ```python 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 `/Users/*.Arma3Profile` files. ```python 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 `/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 1–3.