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

180 lines
7.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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)
```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 `<server_dir>/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 `<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.