feat(infra): auto-commit con 56 cambios

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-21 14:22:55 +02:00
parent c1071a82b3
commit 32c7336bf6
56 changed files with 5307 additions and 100 deletions
@@ -0,0 +1,110 @@
"""Renderiza la configuración YAML de Glance a partir de servicios normalizados.
Glance (https://github.com/glanceapp/glance) es un dashboard self-hosted. Este
módulo transforma una lista de servicios en el YAML que Glance espera: una página
con un widget `monitor` por categoría que hace health-check de cada servicio y lo
pinta verde/rojo. Parte del sistema `local_hub`.
"""
import yaml
_HEADER = (
"# Generado por render_glance_config_py_infra — NO editar a mano. "
"Fuente: apps/local_hub/local_services.yaml\n"
)
def render_glance_config(
services: list[dict],
title: str = "Procesos locales",
host_suffix: str = "localhost",
) -> str:
"""Construye el YAML de configuración de Glance para una lista de servicios.
Función pura y determinista: agrupa los servicios por su clave ``category``,
crea un widget ``type: monitor`` por categoría (ordenadas alfabéticamente) y
dentro de cada uno un site por servicio (ordenados por ``title``). Cada site
apunta a ``http://<subdomain>.<host_suffix>`` (campo ``url``, lo que abre el
usuario al clicar). Si el servicio trae un ``health_path`` distinto de
``"/"``, el site añade además ``check-url`` = ``url + health_path``: es la
ruta que Glance usa para el health-check (muchas APIs dan 404 en ``/`` pero
200 en ``/api/health``), sin cambiar el enlace que ve el usuario.
Args:
services: lista de dicts de servicio normalizados. Cada uno debe traer al
menos ``subdomain`` (si falta, el servicio se ignora sin lanzar) y
``title``; opcionalmente ``category`` (default ``"General"``),
``icon`` (se omite del site si está vacío o ausente) y ``health_path``
(si es distinto de ``"/"``, el site emite ``check-url`` = url +
health_path; si es ``"/"`` o falta, no se emite ``check-url``).
title: nombre de la página de Glance (campo ``name`` de la página).
Default ``"Procesos locales"``.
host_suffix: sufijo de host para las URLs de los sites. Default
``"localhost"`` -> ``http://<subdomain>.localhost``.
Returns:
String con el YAML completo de Glance, terminado en ``\\n``.
"""
# Agrupa por categoría, ignorando servicios sin subdomain.
by_category: dict[str, list[dict]] = {}
for svc in services:
subdomain = svc.get("subdomain")
if not subdomain:
continue
category = svc.get("category") or "General"
by_category.setdefault(category, []).append(svc)
widgets: list[dict] = []
for category in sorted(by_category.keys()):
svcs = sorted(
by_category[category],
key=lambda s: (s.get("title") or "", s.get("subdomain") or ""),
)
sites: list[dict] = []
for svc in svcs:
url = f"http://{svc['subdomain']}.{host_suffix}"
site: dict = {
"title": svc.get("title") or svc["subdomain"],
"url": url,
}
# El health-check apunta al health_path del servicio (no a "/").
# Muchas APIs devuelven 404 en la raiz pero 200 en su ruta de salud
# (ej. /api/health), asi Glance las pinta verde correctamente. El
# campo `url` (lo que abre el usuario al clicar) sigue siendo la raiz.
health = svc.get("health_path") or "/"
if health and health != "/":
site["check-url"] = url + health
icon = svc.get("icon")
if icon:
site["icon"] = icon
sites.append(site)
widgets.append(
{
"type": "monitor",
"title": category,
"cache": "1m",
"sites": sites,
}
)
config = {
"pages": [
{
"name": title,
"columns": [
{
"size": "full",
"widgets": widgets,
}
],
}
]
}
body = yaml.safe_dump(
config,
sort_keys=False,
default_flow_style=False,
allow_unicode=True,
)
return _HEADER + body