Files
devplacepy/devplacepy/docs_api/services_group.py
T

155 lines
5.6 KiB
Python

# retoor <retoor@molodetz.nl>
from ._shared import SERVICE_ACTIONS, endpoint, field
from .negotiation import _classify
def _service_control_endpoints():
endpoints = []
for action, summary in SERVICE_ACTIONS:
endpoints.append(
endpoint(
id=f"services-{action.replace('-', '')}",
method="POST",
path=f"/admin/services/{{name}}/{action}",
title=f"Service: {action}",
summary=summary + ".",
auth="admin",
interactive=True,
destructive=True,
params=[
field(
"name",
"path",
required=True,
example="news",
description="Registered service name.",
)
],
sample_response={"ok": True},
)
)
return endpoints
def _field_to_param(spec):
description = spec["label"]
if spec.get("help"):
description = f"{spec['label']} - {spec['help']}"
value = "" if spec.get("secret") else str(spec.get("value", ""))
if spec.get("secret"):
description += " Leave blank to keep the current value."
if spec["type"] == "bool":
return field(
spec["key"], "form", "enum", False, value or "0", description, ["1", "0"]
)
if spec["type"] == "select":
options = [option["value"] for option in spec.get("options") or []]
return field(
spec["key"],
"form",
"enum",
False,
value or (options[0] if options else ""),
description,
options,
)
if spec["type"] in ("int", "float"):
return field(spec["key"], "form", "int", False, value, description)
return field(spec["key"], "form", "string", False, value, description)
def _service_section(service):
enabled = "yes" if service.get("enabled") else "no"
groups = ", ".join(group["name"] for group in service.get("field_groups", []))
lines = [
f"## {service.get('title') or service['name']}",
"",
service.get("description", ""),
"",
f"- Service name: `{service['name']}`",
f"- Enabled: {enabled}",
f"- Run interval: {service.get('interval_seconds', 0)}s",
]
if groups:
lines.append(f"- Configuration groups: {groups}")
return "\n".join(lines)
def build_services_group(services, base):
intro_parts = [
"# Background Services",
"",
"DevPlace runs background services supervised by a single manager. Each service has its "
"own enable flag, run interval, live status, metrics, and a rolling log buffer. Manage "
"them from the admin panel at `/admin/services` or with the endpoints below.",
"",
"Control a service by its `name` (shown per service): start or stop it, trigger a single "
"run, clear its logs, or save its configuration. `GET /admin/services/data` returns the "
"live status, metrics, and log tail for every service.",
"",
"See the [Admin API](/docs/admin.html) for the rest of administration; the Devii assistant "
"is itself a service, configured in [Configuration and CLI](/docs/devii-config.html).",
]
config_endpoints = []
for service in services:
intro_parts.append("")
intro_parts.append(_service_section(service))
params = [
_field_to_param(spec)
for spec in service.get("fields", [])
if not spec["key"].endswith("_enabled")
]
config_endpoints.append(
endpoint(
id=f"services-config-{service['name']}",
method="POST",
path=f"/admin/services/{service['name']}/config",
title=f"Configure {service.get('title') or service['name']}",
summary=f"Save configuration for the {service['name']} service. Empty values keep the current setting.",
auth="admin",
encoding="form",
destructive=True,
params=params,
notes=[
"Use start/stop to enable or disable the service; this saves the remaining settings."
],
)
)
control_endpoints = [
endpoint(
id="services-data",
method="GET",
path="/admin/services/data",
title="Service status",
summary="Live status, metrics, and log tail for every background service.",
auth="admin",
sample_response={
"services": [{"name": "news", "status": "running", "enabled": True}]
},
),
endpoint(
id="services-detail-data",
method="GET",
path="/admin/services/{name}/data",
title="One service status",
summary="Live status, metrics, and log tail for a single background service.",
auth="admin",
params=[field("name", "path", "string", True, "news", "Service name.")],
sample_response={
"service": {"name": "news", "status": "running", "enabled": True}
},
),
*_service_control_endpoints(),
]
endpoints = control_endpoints + config_endpoints
for ep in endpoints:
ep["negotiation"] = _classify(ep)
return {
"slug": "services",
"title": "Background Services",
"admin": True,
"intro": "\n".join(intro_parts),
"endpoints": endpoints,
}