feat(api): per-service config schema endpoint + PUT/POST update+apply for fleet & topology
- GET /topologies/services/{name}/schema serves the declared ServiceConfigField
metadata so the Inspector can auto-render forms.
- PUT /(topologies/{id}/)deckies/{decky}/services/{svc}/config persists the
validated dict (DB + compose); container untouched (Save).
- POST /(topologies/{id}/)deckies/{decky}/services/{svc}/apply persists then
force-recreates <decky>-<svc> so the new env takes effect (Apply, destructive).
- New engine helper update_service_config wires both fleet and topology paths
through the existing _persist_fleet_change / _rerender_topology_compose
machinery; emits decky.<name>.service_config_changed on the bus.
This commit is contained in:
@@ -8,7 +8,7 @@ under ``decnet.web.db.models``.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Optional
|
||||
from typing import Any, Optional
|
||||
|
||||
from pydantic import BaseModel, Field as PydanticField, field_validator
|
||||
|
||||
@@ -65,6 +65,48 @@ class DeckyServicesResponse(BaseModel):
|
||||
services: list[str]
|
||||
|
||||
|
||||
class ServiceConfigFieldDTO(BaseModel):
|
||||
"""Serialized form of ``decnet.services.base.ServiceConfigField``.
|
||||
|
||||
The Inspector form (Fleet + MazeNET) renders inputs from this metadata.
|
||||
"""
|
||||
key: str
|
||||
label: str
|
||||
type: str
|
||||
default: Optional[Any] = None
|
||||
secret: bool = False
|
||||
help: Optional[str] = None
|
||||
enum: Optional[list[str]] = None
|
||||
placeholder: Optional[str] = None
|
||||
|
||||
|
||||
class ServiceSchemaResponse(BaseModel):
|
||||
"""Per-service config schema returned by GET /services/{name}/schema."""
|
||||
name: str
|
||||
ports: list[int]
|
||||
fleet_singleton: bool = False
|
||||
fields: list[ServiceConfigFieldDTO] = PydanticField(default_factory=list)
|
||||
|
||||
|
||||
class DeckyServiceConfigRequest(BaseModel):
|
||||
"""Body for PUT/POST per-service config endpoints.
|
||||
|
||||
The dict is validated against the service's ``config_schema``
|
||||
server-side: unknown keys are silently dropped, declared keys are
|
||||
coerced to their declared type, and out-of-range values raise 400.
|
||||
"""
|
||||
config: dict[str, Any] = PydanticField(default_factory=dict)
|
||||
|
||||
|
||||
class DeckyServiceConfigResponse(BaseModel):
|
||||
"""Post-validation config + apply state for the form to re-sync from."""
|
||||
decky_name: str
|
||||
service_name: str
|
||||
topology_id: Optional[str] = None
|
||||
config: dict[str, Any] = PydanticField(default_factory=dict)
|
||||
recreated: bool = False
|
||||
|
||||
|
||||
class DeckyFileDeleteRequest(BaseModel):
|
||||
"""Best-effort ``rm -f`` of an absolute path inside a decky container."""
|
||||
decky_name: str = PydanticField(..., min_length=1)
|
||||
|
||||
Reference in New Issue
Block a user