tawasuyu

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 1184Shell 70Rust 62Markdown 501503 Definiciones

⎇ 9b5861d815cfb08418773920067400bd11f9aa66 scripts/mudanza/formatos/nginx.py Python · 131 líneas · 5.7 KB Historia Quién tocó qué crudo

traducir: familia «servicio» — systemd → tarjeta de arje, y el pivote pasa a ser uno POR FAMILIA fefa92ec5 · Sergio ·

1"""Plugin de formato: LECTOR de nginx (SDD 29)."""
2import os, sys
3sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
4from modelo import Config, Sitio, Ruta, Accion          # noqa: E402
5from formatos import lector                             # noqa: E402
6
7# Bloques cuya semántica depende del orden de evaluación o del contexto: no se traducen, y —esto es
8# lo importante— TODO lo de adentro tampoco. Un `return 403` sacado de su `if` deja de ser
9# condicional y pasa a aplicar siempre: no se pierde información, se CAMBIA el sentido.
10OPACOS = {"map", "if", "stream", "upstream", "geo", "split_clients"}
11
12# Ajustes del proceso nginx que no tienen ni necesitan equivalente.
13GLOBALES_IRRELEVANTES = {
14    "worker_processes", "worker_connections", "user", "pid", "include", "sendfile",
15    "keepalive_timeout", "gzip", "default_type", "log_format", "types_hash_max_size",
16    "tcp_nopush", "multi_accept", "server_tokens", "charset",
17}
18
19
20def _tokenizar(texto):
21    out, nivel, buf, ln_ini = [], 0, "", 0
22    for ln, linea in enumerate(texto.split("\n"), 1):
23        s = linea.split("#")[0].strip()
24        if not s:
25            continue
26        if not buf:
27            ln_ini = ln
28        for ch in s:
29            if ch == "{":
30                out.append((ln_ini, nivel, buf.strip(), "abre")); nivel += 1; buf = ""
31            elif ch == "}":
32                if buf.strip():
33                    out.append((ln_ini, nivel, buf.strip(), "dir"))
34                nivel -= 1; out.append((ln_ini, nivel, "", "cierra")); buf = ""
35            elif ch == ";":
36                out.append((ln_ini, nivel, buf.strip(), "dir")); buf = ""
37            else:
38                buf += ch
39        if buf and not buf.endswith(" "):
40            buf += " "
41    return out
42
43
44@lector("nginx", familia="web")
45def leer(texto, nombre=""):
46    cfg = Config(formato_origen="nginx")
47    A = cfg.acta
48    pila, sitio, ruta, opaco = [], None, None, 0
49
50    for ln, _niv, txt, tipo in _tokenizar(texto):
51        cab = txt.split()[0] if txt else ""
52
53        if tipo == "abre":
54            pila.append(cab)
55            if cab in OPACOS:
56                opaco += 1
57                A.anota(ln, "SIN-TRADUCIR", txt + " {",
58                        f"`{cab}` depende del orden de evaluación y del contexto; TODO lo de adentro "
59                        f"queda sin traducir — sacarlo del bloque le cambiaría el sentido")
60            elif cab == "server" and not opaco:
61                sitio = Sitio(origen=ln)
62            elif cab == "location" and not opaco:
63                p = txt.split()
64                if len(p) > 2:   # modificador: ~ ~* ^~ =
65                    clase = {"~": "regex", "~*": "regex", "=": "exacto", "^~": "prefijo"}.get(p[1], "regex")
66                    ruta = Ruta(patron=p[2], clase=clase, origen=ln)
67                else:
68                    ruta = Ruta(patron=p[1] if len(p) > 1 else "/", clase="prefijo", origen=ln)
69            continue
70
71        if tipo == "cierra":
72            c = pila.pop() if pila else ""
73            if c in OPACOS and opaco:
74                opaco -= 1
75            elif c == "location" and sitio is not None and ruta is not None:
76                sitio.rutas.append(ruta); ruta = None
77            elif c == "server" and sitio is not None:
78                cfg.sitios.append(sitio); sitio = None
79            continue
80
81        if opaco:
82            A.anota(ln, "SIN-TRADUCIR", txt,
83                    "está dentro de un bloque que no se pudo traducir: sacarla de ahí dejaría de ser "
84                    "condicional")
85            continue
86
87        p = txt.split()
88        destino = ruta if ruta is not None else sitio
89        if sitio is None:
90            if cab in GLOBALES_IRRELEVANTES or cab.endswith("_log"):
91                A.anota(ln, "no-aplica", txt, "ajuste global del proceso nginx, sin equivalente necesario")
92            elif txt:
93                A.anota(ln, "SIN-TRADUCIR", txt, "directiva global no reconocida")
94            continue
95
96        if cab == "server_name":
97            sitio.nombres += [x for x in p[1:] if x != "_"]
98            A.anota(ln, "traducido", txt)
99        elif cab == "listen":
100            sitio.puertos.append(p[1].split(":")[-1] if len(p) > 1 else "")
101            A.anota(ln, "traducido", txt)
102        elif cab == "root":
103            if ruta is not None:
104                ruta.acciones.append(Accion("archivos", p[1] if len(p) > 1 else ""))
105            else:
106                sitio.raiz = p[1] if len(p) > 1 else ""
107            A.anota(ln, "traducido", txt)
108        elif cab == "index":
109            sitio.indices = p[1:]
110            A.anota(ln, "traducido", txt)
111        elif cab in ("ssl_certificate", "ssl_certificate_key"):
112            sitio.tls["cert" if cab == "ssl_certificate" else "key"] = p[1] if len(p) > 1 else ""
113            A.anota(ln, "traducido", txt)
114        elif cab == "proxy_pass":
115            destino.acciones.append(Accion("proxy", p[1] if len(p) > 1 else ""))
116            A.anota(ln, "traducido", txt)
117        elif cab == "try_files":
118            destino.acciones.append(Accion("intentar", " ".join(p[1:])))
119            A.anota(ln, "traducido", txt)
120        elif cab == "return":
121            destino.acciones.append(Accion("redir", " ".join(p[1:])))
122            A.anota(ln, "traducido", txt)
123        elif cab in ("proxy_set_header", "add_header"):
124            A.anota(ln, "decision", txt,
125                    "las cabeceras de proxy habituales (X-Forwarded-*) las pone el destino por su "
126                    "cuenta; si ésta no es de ésas, hay que escribirla")
127        elif cab in ("client_max_body_size", "expires", "autoindex", "access_log", "error_log"):
128            A.anota(ln, "decision", txt, "tiene equivalente pero con otra forma: revisalo")
129        else:
130            A.anota(ln, "SIN-TRADUCIR", txt, "directiva no reconocida por este lector")
131    return cfg