feat(infra): auto-commit con 56 cambios
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
---
|
||||
name: render_caddyfile
|
||||
kind: function
|
||||
lang: py
|
||||
domain: infra
|
||||
version: "1.1.0"
|
||||
purity: pure
|
||||
signature: "def render_caddyfile(services: list[dict], dashboard: dict | None = None) -> str"
|
||||
description: "Parte del sistema local_hub: transforma una lista de servicios normalizados en el texto de un fragmento de Caddyfile que mapea cada subdominio *.localhost a su puerto local via reverse_proxy HTTP plano (loopback, sin TLS). Cada servicio es un dict con subdomain (str) y port (int); el resto de claves se ignoran. Los bloques de servicio se ordenan por subdominio alfabetico para que la salida sea estable y reproducible (clave para diffs y tests). Un dashboard opcional emite su bloque PRIMERO porque es la pagina principal. Ignora servicios sin subdomain o sin port (los salta, no lanza) y no deduplica. Pura: solo stdlib, sin I/O ni red, determinista."
|
||||
tags: [local-hub, caddy, caddyfile, reverse-proxy, infra, python]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: []
|
||||
params:
|
||||
- name: services
|
||||
desc: "lista de dicts de servicio normalizados. Cada uno debe tener al menos subdomain (str, sin el sufijo .localhost) y port (int, puerto local). Otras claves se ignoran. Los servicios sin subdomain o sin port se saltan silenciosamente. No se deduplica: eso es trabajo del discover."
|
||||
- name: dashboard
|
||||
desc: "dict opcional {subdomain, port} para la pagina principal del hub (ej. {\"subdomain\": \"home\", \"port\": 8585}). Si se pasa, su bloque va el primero de la salida. None = no se emite bloque de dashboard. Si le falta subdomain o port, se ignora igual que un servicio invalido."
|
||||
output: "string con el Caddyfile completo: empieza por una cabecera de comentario (# Generado por render_caddyfile_py_infra ...), luego el bloque del dashboard si aplica, y despues los bloques de servicio ordenados alfabeticamente por subdominio. Cada bloque es 'http://<subdomain>.localhost {\\n reverse_proxy 127.0.0.1:<port>\\n}\\n' con 4 espacios de indentacion. La salida termina con un unico \\n."
|
||||
tested: true
|
||||
tests:
|
||||
- "test_golden_dos_servicios_ordenados"
|
||||
- "test_dashboard_va_primero"
|
||||
- "test_lista_vacia_solo_cabecera"
|
||||
- "test_servicio_sin_port_se_ignora"
|
||||
- "test_servicio_sin_subdomain_se_ignora"
|
||||
- "test_rewrite_host_emite_header_up"
|
||||
- "test_rewrite_host_ausente_o_falso_no_reescribe"
|
||||
test_file_path: "python/functions/infra/render_caddyfile_test.py"
|
||||
file_path: "python/functions/infra/render_caddyfile.py"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
import sys, os
|
||||
sys.path.insert(0, os.path.join("python", "functions", "infra"))
|
||||
from render_caddyfile import render_caddyfile
|
||||
|
||||
services = [
|
||||
{"subdomain": "metabase", "port": 3030},
|
||||
{"subdomain": "grafana", "port": 3000},
|
||||
]
|
||||
dashboard = {"subdomain": "home", "port": 8585}
|
||||
|
||||
print(render_caddyfile(services, dashboard=dashboard))
|
||||
# # Generado por render_caddyfile_py_infra — NO editar a mano. Fuente: apps/local_hub/local_services.yaml
|
||||
# http://home.localhost {
|
||||
# reverse_proxy 127.0.0.1:8585
|
||||
# }
|
||||
# http://grafana.localhost {
|
||||
# reverse_proxy 127.0.0.1:3000
|
||||
# }
|
||||
# http://metabase.localhost {
|
||||
# reverse_proxy 127.0.0.1:3030
|
||||
# }
|
||||
```
|
||||
|
||||
## Cuando usarla
|
||||
|
||||
Cuando, dentro del sistema local_hub, ya tienes la lista de servicios locales
|
||||
normalizada (cada uno con su `subdomain` y `port`) y necesitas materializar el
|
||||
fragmento de Caddyfile que enruta `*.localhost` a sus puertos. Usala justo
|
||||
antes de escribir el archivo a disco y recargar Caddy: esta funcion solo
|
||||
produce el texto (pura), el I/O y el reload van en una funcion impura o pipeline
|
||||
aparte. Tambien util para tests/diffs porque la salida es determinista (bloques
|
||||
ordenados por subdominio).
|
||||
|
||||
## Gotchas
|
||||
|
||||
- El formato es HTTP plano a proposito (`http://...`, sin TLS): todo el trafico
|
||||
es loopback (`127.0.0.1`), no hay nada que cifrar y `*.localhost` no necesita
|
||||
certificado. No es un bug.
|
||||
- No deduplica subdominios: si dos servicios comparten `subdomain`, ambos
|
||||
bloques se emiten y Caddy se quedara con el ultimo. La deduplicacion es
|
||||
responsabilidad del discover que produce `services`.
|
||||
- `rewrite_host` solo cambia la cabecera `Host` que ve el upstream, no la URL
|
||||
que abre el usuario. Actívalo unicamente para servicios que validan el header
|
||||
y rechazan el subdominio (Jupyter devuelve 400, algunos FastAPI/uvicorn con
|
||||
`--forwarded-allow-ips` estricto). Para el resto dejalo ausente/False: añadir
|
||||
`header_up Host` sin necesidad puede romper virtual-hosting del upstream.
|
||||
|
||||
## Capability growth log
|
||||
|
||||
- v1.1.0 (2026-06-20) — añade soporte rewrite_host (header_up Host) para servicios que validan el header Host
|
||||
Reference in New Issue
Block a user