135 lines
7.2 KiB
Text
135 lines
7.2 KiB
Text
#!/usr/bin/env nu
|
|
|
|
# ¿ESTE SERVIDOR ES EL SITIO, O SOLO ALGO QUE RESPONDE EN ESE PUERTO?
|
|
#
|
|
# Toda comprobación contra la superficie viva empieza dando por hecho lo único que nunca verifica:
|
|
# que al otro lado del puerto está el sitio. `check-served-live.nu` lo dice en su cabecera — «la
|
|
# superficie viva es la única publicación que hay» — y luego acepta un `--base-url` y se cree lo
|
|
# que le dan. Puede medir un servidor que no es este sitio y decirlo todo en verde.
|
|
#
|
|
# ── QUÉ PASÓ, MEDIDO (2026-07-16) ────────────────────────────────────────────────────────────
|
|
#
|
|
# Un servidor arrancado por una vía degradada —sin el entorno de run.sh— no trae RUSTELO_SITE_ROUTES,
|
|
# así que NO FUSIONA las rutas que el sitio declara en site/config/routes/. Responde 200 en todo lo
|
|
# que la imagen compartida trae compilado y 404 en lo que este sitio aporta. Contra ese servidor:
|
|
# /conceptos daba 404 y /sobre-el-idioma daba 404. De ahí salió media sesión persiguiendo un fallo
|
|
# de rutas que no existía, y dos diagnósticos falsos encadenados («las rutas están rotas», «el
|
|
# conmutador de idioma manda a 404»). Las rutas estaban perfectas. El servidor no era el sitio.
|
|
#
|
|
# ── POR QUÉ NO COMPRUEBA LA CANONICAL, QUE SERÍA MÁS CORTO ──────────────────────────────────
|
|
#
|
|
# Un servidor degradado emite `canonical = http://127.0.0.1:3030` y uno de run.sh emite
|
|
# `https://ontoref.dev`, porque run.sh exporta SITE_BASE_URL. Sería una línea. Y sería
|
|
# `a-check-measures-the-capability-not-a-proxy` (adr-072) en estado puro: SITE_BASE_URL no es
|
|
# «servir las rutas del sitio», es otra variable que resulta que hoy viaja en el mismo autobús.
|
|
# El día que alguien exporte una y no la otra, la puerta dice OK sobre un servidor ciego — y lo
|
|
# dirá con toda la confianza, que es lo que hace peligroso a un proxy.
|
|
#
|
|
# Así que se mide LA CAPACIDAD: pedir las rutas que el sitio declara, y comprobar que el servidor
|
|
# las conoce. Es la pregunta entera y no su sombra.
|
|
#
|
|
# ── Y SE PREGUNTA CON MARCADOR POSITIVO ─────────────────────────────────────────────────────
|
|
#
|
|
# No «el título no es el de la 404»: en este sitio un 404 responde 200 con «ontoref — Article»
|
|
# (medido hoy: /adr/999), la copia del 404 viaja incrustada en todas las páginas, y afirmar por
|
|
# ausencia de un negativo es como se cuelan los falsos verdes. La ruta declara `title_key`, y el
|
|
# .ftl dice qué texto es. Eso es lo que la página tiene que traer: la afirmación positiva de que
|
|
# el servidor sirve ESTA página, no de que no sirve otra.
|
|
#
|
|
# Uso:
|
|
# nu scripts/build/check-live-server.nu --base-url http://localhost:3030 --root .
|
|
|
|
def main [
|
|
--base-url: string = "http://localhost:3030",
|
|
--root: string = ".",
|
|
] {
|
|
let routes_dir = ([$root, "site", "config", "routes"] | path join)
|
|
if not ($routes_dir | path exists) {
|
|
print $"check-live-server: NO MIRÉ — ($routes_dir) no existe, y es lo único que dice qué rutas"
|
|
print " aporta este sitio. Sin eso no puedo distinguir un servidor bueno de uno ciego."
|
|
exit 1
|
|
}
|
|
|
|
# Las rutas del CONSUMIDOR, que son exactamente las que un servidor degradado se salta. Las de la
|
|
# imagen compartida no sirven de testigo: las responde igual sin haber leído nada de este sitio.
|
|
let declared = (
|
|
glob $"($routes_dir)/*.ncl"
|
|
| each {|f|
|
|
let r = (do { ^nickel export --format json $f } | complete)
|
|
if $r.exit_code != 0 {
|
|
print $"check-live-server: NO MIRÉ — ($f) no exporta: ($r.stderr | str trim | lines | first)"
|
|
exit 1
|
|
}
|
|
($r.stdout | from json).routes
|
|
}
|
|
| flatten
|
|
| where {|r| ($r.enabled? | default true) }
|
|
)
|
|
|
|
# CERO RUTAS DECLARADAS ES UN HALLAZGO, NUNCA UN «TODO BIEN». Si el glob no encuentra nada, o el
|
|
# esquema cambió y `routes` ya no se llama así, esta puerta no tiene nada que pedir — y una puerta
|
|
# sin nada que pedir pasa siempre. Es `a-check-can-say-i-did-not-look` (adr-072), y es el modo en
|
|
# que las puertas se mueren solas: en verde.
|
|
if ($declared | is-empty) {
|
|
print $"check-live-server: NO MIRÉ — ($routes_dir) no declaró ni una ruta habilitada."
|
|
print " Una puerta sin nada que pedir pasa siempre. Eso no es un servidor sano: es ignorancia."
|
|
exit 1
|
|
}
|
|
|
|
mut bad = []
|
|
mut ok = []
|
|
for r in $declared {
|
|
let lang = ($r.language? | default "")
|
|
let key = ($r.title_key? | default "")
|
|
if ($key | is-empty) {
|
|
$bad = ($bad | append $"($r.path) — la ruta no declara title_key, así que no tengo marcador positivo que pedir")
|
|
continue
|
|
}
|
|
# El texto que el sitio PROMETE para esa página. Se busca en su idioma: una clave resuelta
|
|
# contra el .ftl equivocado compararía la página con la promesa de otro idioma.
|
|
let ftl_dir = ([$root, "site", "i18n", "locales", $lang, "pages"] | path join)
|
|
let want = (
|
|
glob $"($ftl_dir)/*.ftl"
|
|
| each {|f| open --raw $f | lines | where {|l| $l | str starts-with $"($key) = " } }
|
|
| flatten
|
|
| each {|l| $l | str replace $"($key) = " "" | str trim }
|
|
| get -o 0
|
|
)
|
|
if $want == null or ($want | is-empty) {
|
|
$bad = ($bad | append $"($r.path) — title_key '($key)' no está en ($lang)/pages/*.ftl: la ruta promete un texto que no existe")
|
|
continue
|
|
}
|
|
|
|
let res = (do { ^curl -s -o /tmp/cls-page.html -w "%{http_code}" $"($base_url)($r.path)" } | complete)
|
|
let code = ($res.stdout | str trim)
|
|
let body = (if ("/tmp/cls-page.html" | path exists) { open --raw /tmp/cls-page.html } else { "" })
|
|
let title = ($body | parse --regex '(?s)<title>(?<t>.*?)</title>' | get -o t.0 | default "")
|
|
|
|
if $code != "200" {
|
|
$bad = ($bad | append $"($r.path) [($lang)] — HTTP ($code). El sitio la declara y el servidor no la conoce.")
|
|
} else if not ($title | str contains $want) {
|
|
# El 404 de este sitio responde 200. Por eso el veredicto lo da el TÍTULO contra la promesa,
|
|
# no el código: una página que existe trae lo que su title_key dice; una que no, trae la
|
|
# cáscara genérica y el mismo 200.
|
|
$bad = ($bad | append $"($r.path) [($lang)] — 200, pero el título es «($title)» y su title_key promete «($want)». Es la cáscara, no la página.")
|
|
} else {
|
|
$ok = ($ok | append $"($r.path) [($lang)]")
|
|
}
|
|
}
|
|
|
|
if ($bad | is-not-empty) {
|
|
print "check-live-server: ESTE SERVIDOR NO ES ESTE SITIO — rutas que el sitio declara y él no sirve:"
|
|
for b in $bad { print $" ✗ ($b)" }
|
|
print ""
|
|
print " Casi siempre significa que el servidor se arrancó sin el entorno de run.sh, así que"
|
|
print " RUSTELO_SITE_ROUTES no está y NO fusionó site/config/routes/. Responde 200 en todo lo"
|
|
print " que la imagen compartida trae compilado y 404 en lo que aporta este sitio."
|
|
print ""
|
|
print " ./run-secure.sh # SOPS exec-env → run.sh → el entorno completo"
|
|
print ""
|
|
print " NO midas nada contra este servidor: lo que te diga sobre esta web no es sobre esta web."
|
|
exit 1
|
|
}
|
|
|
|
print $"check-live-server: el servidor sirve las ($ok | length) rutas que este sitio declara — ($ok | str join ', ')"
|
|
}
|