gioser

sergio/takana

> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…

TOML 1572Shell 203Rust 109Markdown 993728 Definitions

⎇ 3137d47f165b900042653af6db5513180fe471c8 scripts/mudanza/formatos/caddy.py Python · 74 lines · 3.3 KB History Who touched what raw

traducir: CENTRO de traducción con modelo pivote y plugins — N+M en vez de N×M 3137d47f1 · Sergio ·

1"""Plugin de formato: ESCRITOR de Caddy (SDD 29)."""
2import os, sys
3sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
4from formatos import escritor                           # noqa: E402
5
6
7def _direcciones(s):
8    """En nginx dos `server` con el MISMO nombre se distinguen por su `listen`; en Caddy, por el
9    ESQUEMA de la dirección. Sin traducir eso, los bloques colisionan y caddy rechaza el fichero
10    entero con «ambiguous site definition» — encontrado validando con el binario real, no leyendo."""
11    solo_80 = bool(s.puertos) and all(p == "80" for p in s.puertos)
12    pref = "http://" if solo_80 else ""
13    return ", ".join(pref + n for n in s.nombres) or (":80" if solo_80 else ":443")
14
15
16def _emitir_acciones(acciones, ind):
17    L = []
18    for a in acciones:
19        if a.tipo == "proxy":
20            L.append(f"{ind}reverse_proxy {a.valor.rstrip('/').replace('http://','').replace('https://','')}")
21        elif a.tipo == "archivos":
22            L.append(f"{ind}root * {a.valor}")
23        elif a.tipo == "intentar":
24            L.append(f"{ind}try_files {a.valor}")
25        elif a.tipo == "redir":
26            p = a.valor.split()
27            code = p[0] if p and p[0].isdigit() else ""
28            dest = " ".join(p[1:]) if code else a.valor
29            L.append(f"{ind}redir {dest} {code}".rstrip() if dest else f"{ind}respond {code}")
30    return L
31
32
33@escritor("caddy")
34def escribir(cfg):
35    A = cfg.acta
36    L = [f"# Generado por scripts/mudanza/traducir.py desde una configuración de {cfg.formato_origen}.",
37         "# ⚠ NO es una traducción completa. Abajo va el ACTA de lo que NO se pudo traducir, con su",
38         "#   línea de origen. Desplegar esto sin resolverlo es desplegar un servidor incompleto.",
39         ""]
40    for s in cfg.sitios:
41        L.append(f"{_direcciones(s)} {{")
42        L.append(f"\t# desde la línea {s.origen} del {cfg.formato_origen} original")
43        if s.tls.get("cert"):
44            L.append(f"\ttls {s.tls.get('cert','')} {s.tls.get('key','')}")
45        if s.raiz:
46            L.append(f"\troot * {s.raiz}")
47        L += _emitir_acciones(s.acciones, "\t")
48        for r in s.rutas:
49            if r.clase == "regex":
50                # Caddy no tiene un equivalente directo de `location ~`. Se DECLARA en el acta en vez
51                # de emitir algo parecido: un `handle` de prefijo no es lo mismo que una regex, y
52                # parecerse es peor que faltar.
53                A.anota(r.origen, "SIN-TRADUCIR", f"ruta por regex: {r.patron}",
54                        "Caddy no tiene equivalente directo de `location ~`; se escribe con "
55                        "`@matcher path_regexp` y hay que revisar el orden de evaluación")
56                continue
57            cuerpo = _emitir_acciones(r.acciones, "\t\t")
58            if cuerpo:
59                L.append(f"\thandle {r.patron} {{    # línea {r.origen}")
60                L += cuerpo
61                L.append("\t}")
62        if s.raiz:
63            L.append("\tfile_server")
64        L.append("}")
65        L.append("")
66
67    pend = A.sin_traducir()
68    if pend:
69        L.append("# ══ ACTA: lo que NO se tradujo — hay que resolverlo a mano ══")
70        for e in pend:
71            L.append(f"#   línea {e['linea']:>4}  {e['directiva'][:70]}")
72            if e["motivo"]:
73                L.append(f"#             └─ {e['motivo'][:96]}")
74    return "\n".join(L) + "\n"