feat: Phase 1 — Config UI Schema system with per-field widget routing
- Backend: add Arma3ConfigGenerator.get_ui_schema() with widget hints per field - Backend: add ServerService.get_config_schema() and GET /config/schema endpoint - Frontend: add FieldSchema/ConfigSchema types + useServerConfigSchema hook - Frontend: new TagListEditor component for dynamic string-list editing - Frontend: ConfigEditor now routes each field to correct widget (text/number/password/textarea/select/toggle/tag-list) - Frontend: password fields have show/hide toggle; toggles render as checkbox; tag-list uses TagListEditor - Tests: 8 new tests covering hook and TagListEditor; all 136 tests green
This commit is contained in:
@@ -382,6 +382,37 @@ class Arma3ConfigGenerator:
|
||||
args.extend(mod_args)
|
||||
return args
|
||||
|
||||
def get_ui_schema(self) -> dict:
|
||||
return {
|
||||
"server": {
|
||||
"hostname": {"widget": "text", "label": "Server Hostname"},
|
||||
"max_players": {"widget": "number", "label": "Max Players", "min": 1, "max": 1000},
|
||||
"password": {"widget": "password", "label": "Player Password"},
|
||||
"password_admin": {"widget": "password", "label": "Admin Password"},
|
||||
"motd_lines": {"widget": "textarea", "label": "Message of the Day (one line per row)"},
|
||||
"forced_difficulty": {"widget": "select", "label": "Difficulty Preset",
|
||||
"options": ["Recruit", "Regular", "Veteran", "Custom"]},
|
||||
"battleye": {"widget": "toggle", "label": "BattleEye Anti-Cheat"},
|
||||
"disable_von": {"widget": "toggle", "label": "Disable Voice over Net (VoN)"},
|
||||
"verify_signatures": {"widget": "number", "label": "Verify Signatures (0=off, 1=on, 2=strict)",
|
||||
"min": 0, "max": 2},
|
||||
"persistent": {"widget": "toggle", "label": "Persistent (keep running when empty)"},
|
||||
"admin_uids": {"widget": "tag-list", "label": "Admin Steam UIDs",
|
||||
"placeholder": "76561198000000000"},
|
||||
},
|
||||
"basic": {
|
||||
"max_custom_file_size": {"widget": "number", "label": "Max Custom File Size (bytes)"},
|
||||
},
|
||||
"launch": {
|
||||
"extra_params": {"widget": "tag-list", "label": "Additional Startup Parameters",
|
||||
"placeholder": "-limitFPS=100"},
|
||||
},
|
||||
"rcon": {
|
||||
"rcon_password": {"widget": "password", "label": "RCon Password"},
|
||||
"max_ping": {"widget": "number", "label": "RCon Port"},
|
||||
},
|
||||
}
|
||||
|
||||
def preview_config(
|
||||
self,
|
||||
server_id: int,
|
||||
|
||||
@@ -134,6 +134,15 @@ def get_config(
|
||||
return _ok(ServerService(db).get_config(server_id))
|
||||
|
||||
|
||||
@router.get("/{server_id}/config/schema")
|
||||
def get_config_schema(
|
||||
server_id: int,
|
||||
db: Annotated[Connection, Depends(get_db)] = None,
|
||||
_user: Annotated[dict, Depends(get_current_user)] = None,
|
||||
):
|
||||
return _ok(ServerService(db).get_config_schema(server_id))
|
||||
|
||||
|
||||
@router.get("/{server_id}/config/preview")
|
||||
def get_config_preview(
|
||||
server_id: int,
|
||||
|
||||
@@ -396,6 +396,14 @@ class ServerService:
|
||||
data[field] = "***"
|
||||
return sections
|
||||
|
||||
def get_config_schema(self, server_id: int) -> dict:
|
||||
server = self.get_server(server_id)
|
||||
adapter = GameAdapterRegistry.get(server["game_type"])
|
||||
config_gen = adapter.get_config_generator()
|
||||
if hasattr(config_gen, "get_ui_schema"):
|
||||
return config_gen.get_ui_schema()
|
||||
return {}
|
||||
|
||||
def get_config_section(self, server_id: int, section: str) -> dict:
|
||||
server = self.get_server(server_id)
|
||||
adapter = GameAdapterRegistry.get(server["game_type"])
|
||||
|
||||
Reference in New Issue
Block a user