Files
feadulta/mirror-antiguo/README.md
rafa 275aff1430 Mirror del Joomla antiguo: versionar los scripts y reponer los assets que faltaban
Los scripts del mirror (00-90) vivian solo en el disco. Van al repo; los datos que
generan no (16 GB entre crawl, snapshot del origen y Joomla restaurado) -> .gitignore.

Nuevo 91-repone-assets404.sh: repone los ficheros que el crawl no capturo porque se
referencian SOLO desde CSS y el crawler seguia enlaces HTML (system.css, los fondos de
fe_adulta_1, ratingstars.gif de K2). Salian como 404 en los logs de nginx del Hetzner.

Descarga por HTTP desde el Joomla local aislado, nunca del filesystem -- mismo principio
que el crawl, para no arrastrar los .php comprometidos del #183 -- y escanea PHP embebido
antes de copiar a site/.

Resultado sobre las 286 rutas unicas con 404 del log: 196 repuestas y verificadas en
produccion (196/196 en 200 tras el rsync), 82 que dan 301->404 tambien en el origen (ya
estaban rotas en la web original) y 8 rutas basura /%22/... de HTML mal formado.

Refs #180

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 14:04:31 -04:00

4.4 KiB

mirror-antiguo — mirror estático del Joomla legacy de feadulta

Construcción del mirror HTML read-only de antiguo.feadulta.com (issue rafa/feadulta#180, remediación de #183).

Regla de diseño: el mirror se genera solo por HTTP, nunca copiando el filesystem. Así es imposible arrastrar los .php comprometidos del incidente. Lo que se captura es lo que Joomla renderiza.

Origen del crawl

No es producción: es el Joomla legacy restaurado en local.

Contenedor joomla-mirror-web (php:7.4-apache), red joomla-migration_joomla-net, IP 172.20.0.5
Acceso http://127.0.0.1:8086 o http://antiguo.feadulta.com (entrada en /etc/hosts → 172.20.0.5)
BD joomla_mirror dentro del contenedor joomla-mysql
Snapshot fuente source/antiguo-20260729.tar.gz + source/fejoomla3-20260729.sql.gz, hashes en MANIFEST-source.sha256

⚠️ La imagen base necesitó mysqli, pdo_mysql, gd, zip y mod_rewrite instalados en caliente: se pierden si el contenedor se recrea, no si solo se para/arranca. Para cambiar memoria o política de reinicio usar docker update, nunca docker rm + docker run.

⚠️ WSL2 se apaga cuando no queda ninguna sesión abierta desde Windows, y al apagarse para los contenedores. Antes de un proceso de horas, dejar un proceso ancla vivo en WSL.

Datos del sitio

  • SEF: sef=1, sef_rewrite=1, sef_suffix=1 → las URLs terminan en .html.
  • Un solo idioma de contenido publicado: es-ES (sef es); todo el contenido es language='*'. El sitio hace 301 de /x/es/x, así que el inventario se genera ya con /es/.
  • Solo 2 categorías K2 y un único ítem de menú de K2: buscadoravanzado (Itemid 138). Las URLs de ítem son /es/buscadoravanzado/item/<id>-<alias>.html.
  • 16.269 ítems K2 publicados · 9.079 artículos com_content · 216 ítems de menú · 68 categorías.

Método

El inventario no se reconstruye a mano: lo genera el propio router de Joomla. _genurls.php (en la raíz del sitio restaurado) arranca el framework por HTTP y llama a JRoute::_() y K2HelperRoute::getItemRoute(), así que las URLs son idénticas a las que el sitio imprime.

Cuatro pases, todos acotados por listas — nunca por recursión libre (ver post-mortem del 2026-07-29 en #180 comment-497):

Pase Qué captura Script
A Las 25.437 URLs del inventario 21-crawl-html.sh
B Recursos (css/js/img/mp3/pdf) referenciados por el HTML capturado 30-extract-links.py + 31-fetch-assets.sh
C Huecos: rutas de menú alternativas, páginas de autor de K2, /anterior, /ediciones 43-pendientes.sh
D /anterior, la web estática anterior a Joomla (HTML plano, recursión finita) 37-pase-d-anterior.sh

raw/ es inmutable. site/ es el derivado servible (45-normalize-links.py).

Trampas encontradas (y cómo se resuelven)

  1. Paginación de K2: los enlaces acumulan &start= en vez de reemplazarlo → espacio de URLs infinito. Es lo que tumbó la VM el 29-jul. Se evita capturando por inventario, y el --reject-regex incluye start|limitstart|limit|print|tmpl|format|searchword|task|orderby|filter.
  2. Alias con ? literal: ~198 artículos tienen el signo de interrogación dentro del alias (...-dios-nos-ama?.html). Para cualquier cliente HTTP eso es el separador de query, así que la ruta real es la parte anterior al ? y wget guarda un fichero sin extensión. Es correcto para servirlo estáticamente, pero nginx necesita default_type text/html en esa ubicación o el navegador se lo descargará en vez de mostrarlo.
  3. Assets con cache-busting (core.js?a32fb…): wget mete la query en el nombre del fichero. El paso de normalización deja también una copia con el nombre limpio, que es la que pedirá el servidor estático.
  4. Menús cuyo componente ya no existe (com_surveys, com_breezingforms): JRoute no les construye ruta y devuelven 404. Ya son 404 en producción; para el resto de ítems de menú la ruta autoritativa es la columna path de #__menu (41-menu-paths.sh).

Scripts

Numerados por orden de ejecución en scripts/. 23-estado.sh da el estado en cualquier momento; 22-monitor.sh vigila el crawl y lo aborta si el servidor pasa de 100 respuestas 500 o si la RAM libre baja de 800 MB. 60-restaurar-entorno.sh rearranca los contenedores parados durante el crawl.