Cutover hecho el 3-ago a las 10:50 UTC. feadulta.com y www.feadulta.com ya
apuntan a 188.40.120.157, proxied. Ventana real ~3 min 10 s: dump 67 s, saneo
2 s, import 114 s, delta de uploads 6 s (0 ficheros, como estaba medido).
Cuadro al digito con produccion: 43.893 posts, 1.228 usuarios, 4.416 terms,
135.959 postmeta. Los options salen 958 frente a 975 porque son transients (hay
580 y caducan solos). Con trafico real: 581 respuestas 200, 62 301, 16 302, cero
5xx y cero errores PHP en 20 minutos, 61 IPs distintas atendidas.
Se anaden los cuatro scripts que faltaban, que hasta hoy solo existian como
comandos sueltos del ensayo: 01-traer-dump, 02-sanear-dump, 03-importar,
04-delta-uploads y 05-mover-dns. El runbook ya los citaba por nombre.
Tres cosas encontradas al ejecutar, anotadas en los propios scripts:
- docker exec -i dentro de un ssh 'bash -s' <<EOF se come el resto del script,
porque hereda el stdin del heredoc. Las consultas van sin -i y con </dev/null.
- Smart Slider cachea el HTML del slider con URLs absolutas, una fila por idioma
en wp_nextend2_section_storage con application='cache'. Al verificar el import
con la constante del staging todavia puesta quedaron 5 filas apuntando a
nuevo.feadulta.com. Se borran y se regeneran solas.
- El cliente de MariaDB de la jaula de CDMON exige TLS y el servidor local no lo
tiene: con --skip-ssl si se puede consultar la BD de produccion directamente,
que es como se comprobo que no habia cambiado nada desde el dump de las 05:09.
Pendiente y fuera de nuestro alcance: purgar la cache de Cloudflare (el token
solo tiene permiso de DNS) y darle a Inma su contrasena nueva. Y los certificados
de Let's Encrypt para www y el apex, que fallan porque el WAF de Cloudflare
devuelve 403 a la validacion; no bloquea mientras la zona este en Full, pero por
eso mismo no se puede subir a Full (strict) todavia.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Aviso de Mixbot (2-ago): los bots de la carta publican por wp-nuevo.feadulta.com,
no por www, porque el WAF de Cloudflare los bloquea por www (regla "bloquear bots
1"). Ese hostname no tiene registro propio, lo cubre el wildcard, y Traefik solo
enruta nuevo.feadulta.com: al mover el A del apex los bots dejarian de publicar.
La carta 738 se compone el martes.
Se anade el paso 0 al runbook. Hay que hacerlo en el PANEL: por API el PATCH de
fqdn da 422 y actualizar SERVICE_FQDN_WORDPRESS cambia la variable pero no
regenera los routers (comprobado hoy). Plan B documentado: los bots pueden pasar
a nuevo.feadulta.com, que es grey cloud y por tanto no pasa por Cloudflare ni le
aplica ninguna regla del WAF.
Decision de Rafa: el bloqueo de login lo lleva Wordfence y sale
limit-login-attempts-reloaded. Wordfence hace lo mismo y ademas firewall de
aplicacion, escaner de malware y 2FA; los dos juntos son dos contadores
compitiendo. Retirado del staging (7 plugins activos, sitio en pie) y anadido al
post-import-seguridad.sh (paso 3c) porque el dump lo revive en cada import, junto
con sus 12 opciones huerfanas. Nuevo paso 3d que verifica que Wordfence sigue
activo y lo reinstala si faltara.
Inma confirma que el modo SSL de la zona es Full, no Flexible: cae el riesgo de
bucle de redireccion. Marcado como resuelto en los verdes previos.
Anadida la seccion del correo: el cutover no lo toca (MX a otra IP), pero la
caducidad del 07/08 si, y ediciones@/contenido@ son de donde sale la carta.
Anotado tambien que Cloudflare devuelve 403 a la IP de Rafa para cualquier
hostname de feadulta: un 403 desde su maquina no prueba nada, la verificacion de
los bots la tienen que hacer ellos.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Buscando si Wordfence habia que reinstalarlo limpio (#183 comment-492) resulta
que Wordfence NO esta instalado en produccion. Lo que si queda es una opcion
llamada 'wordfence_core_options' que no es suya: es el payload del atacante,
disfrazado con ese nombre para pasar por legitimo.
Cifrado con un cesar de una letra (ejw->div, tuzmf->style, isfg->href). Una vez
descifrado:
<div style="position: absolute;margin-top: -110px;">
<div style="position: absolute; left: -7585px;">
<a href="https://1xbetjap.com/ja/casino/">
Un div fuera de pantalla con enlaces a un casino japones, con el campo output
apuntando a wp_footer. Esto explica los "dos POST a wp-admin/options.php" del
comment-492, que se habian atribuido a que el atacante toco la configuracion de
Wordfence: lo que hacia era crear esta opcion.
Hoy esta inerte porque quien la leia era el plugin wp-highlits, en cuarentena.
Pero viaja dentro del dump, asi que CADA import la reintroduce en el servidor
nuevo: por eso va aqui y no como limpieza manual de una vez.
Se preserva el valor en /data/feadulta-migracion/evidencia-183/ antes de borrar.
Barrido de la BD del destino: es la unica. 0 en post_content, 0 enlaces ocultos
fuera de pantalla en el contenido, 0 referencias a las IPs del atacante.
Nota sobre el escaneo previo del dump: lo di por limpio buscando <script, eval(,
base64_decode y gzinflate. Este payload no lleva ninguno de esos patrones y paso
el filtro. El escaneo por firmas conocidas no acredita limpieza.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Con los tiempos REALES medidos en el ensayo del 31-jul, no estimados:
dump 25s + subida 74s + saneo 2s + import 107s = camino critico ~3min30s.
- docs/cutover/RUNBOOK-cutover-wordpress.md: los pasos en orden, copiables, con
el rollback y los tres verdes obligatorios previos (freeze, CDMON vivo, modo
SSL de la zona).
- scripts/cutover/post-import-seguridad.sh: rota la contrasena de los 6
administradores (las lee del .env del perfil, nunca van en el repo), retira el
rol a pabloarias/josek/andrey y limpia los cron huerfanos de Yoast y WP
Profile Builder. Es un script y no una tarea manual porque el dump revive esas
cuentas en CADA import.
- tools/e2e/sites/nuevo.json: la suite toma el host de un JSON, asi que apuntarla
al staging es anadir un fichero.
Hallazgo del ensayo: el origen es MariaDB 11.8.6 y el destino MySQL 8.4.10; el
dump trae 20 tablas con collations uca1400_* que MySQL rechaza. El saneo las
mapea antes de importar. Sin ese paso, el import aborta.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Este repo local tenia origin apuntando al Gitea local (localhost:3000), que Rafa
declaro archivado el 2026-06-28 (commit 962f33a, desarrollo movido a
gitea.feadulta.com). Ese memo nunca llego a este checkout: main quedo congelado
y todo el trabajo real de las ultimas 3+ semanas se fue commiteando solo en la
rama fix/multiidioma-portada-132 (ya fusionada a main sin perdida, commit
2504666), mientras que ademas se acumulaban 78 cambios sin commitear en el
working tree que nunca llegaron a NINGUN historial de git.
Este commit consolida esos cambios sueltos: TTS multi-voz (tts_*.py), scripts
de traduccion (translate_haiku.py, pretranslate_en_haiku.py, sync_translations_to_prod.py),
mu-plugins nuevos desplegados a prod (fea-beta-feedback, fea-cloudflare-realip,
fea-legacy-redirect, fea-gsc-verification, fea-support-campaign, fea-ui, etc.),
scripts de mantenimiento de enlaces/cartas, capturas E2E (tools/e2e/shot_*.cjs)
y documentacion de sesiones recientes.
Excluido deliberadamente (no es codigo versionable): tts-voices/ (3.3GB de
muestras de audio para clonacion de voz, anadido a .gitignore), logs/ (logs de
ejecucion, anadido a .gitignore), y 2 ficheros vacios accidentales + 2 copias
duplicadas sueltas en la raiz que ya existen en su ubicacion correcta.
Con Mixbot publicando la carta ES directamente en prod, el WP local dejaba de
recibir el contenido nuevo y se desalineaba (visto con la carta 735, ID 54495
apuntando a otro post en local). El nuevo script descubre el cluster de la
carta parseando sus propios enlaces (fea_parse_carta_sections, sin depender de
_carta_id) y clona cada post a local preservando ID, reutilizando
fea_translate_helper.php en la dirección contraria a sync_translations_to_prod.py.
Añade el subcomando 'carta_sections' al helper (aditivo, no toca el resto).
Voz clonada por autor (Fray Marcos, Pagola, Sicre, Arregi) en vez de una
única voz genérica para todo el TTS. minimax_tts.py resuelve la voz por
post_author (AUTHOR_VOICES) y cae a Nico si no hay clon; fea_post_io.php
propaga autor/voz; tts_produce.py y sync_audio_to_prod.py (local→prod,
workaround glibc rota del servidor) usan la voz correcta en vez de forzar
Nico.
Incluye fix de trim_after_author_signature: la heurística de "firma final
del autor" confundía encabezados litúrgicos iniciales (p.ej. "CORPUS (A)")
con la firma, truncando el texto a sintetizar a un par de palabras. Ahora
solo cuenta como firma un párrafo con >200 caracteres de contenido real
por delante.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Reports internal links whose language prefix/slug doesn't match the target
post's actual language (SIN_TRADUCIR / MISMATCH), so link bugs like the
silence/silence-2 cross-language mixup on carta 734 get caught before they
reach prod.
Replica el «Buscador avanzado» del Joomla viejo sin servicios externos:
- fea-search-fulltext.php: motor MySQL FULLTEXT (MATCH AGAINST, Boolean Mode,
orden por relevancia); comprueba que existe el índice fea_ft y degrada al
buscador nativo si no, sin romper.
- fea-search-advanced.php: formulario con filtros por autor, categoría
(lista curada de secciones), cita bíblica (meta _cita_evangelio por prefijo)
y rango de fechas; pre_get_posts, chips de filtros, byline en tarjetas,
i18n es/en/fr/it/pt. Excluye pseudo-autores «Testamento».
- create_buscar_page.php: crea la página /buscar + traducciones (idempotente).
- tools/e2e: suite Playwright de verificación.
Desplegado y verificado en prod (índice fea_ft + página /buscar + traducciones).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Cuando created_by de Joomla no está en user_map, post_author cae a 1
('Fe Adulta'). Se añade un aviso por consola apuntando a fix_k2_authors.php
para corregirlo tras el delta. (El resto de cambios del fichero, de sesiones
previas, quedan sin commitear.)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- fea-search.php: barra de búsqueda visible en móvil (donde el menú
colapsa y oculta el buscador del menú); multiidioma por Polylang.
- set_search_template.php: template FSE 'search' con resultados en
rejilla de tarjetas (reutiliza el layout de archive #63) en vez del
patrón del tema que los mostraba 'todos seguidos'.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Artículos K2 cuyo created_by de Joomla no tenía usuario WP caían a
post_author=1 ('Fe Adulta'). Herramientas:
- fix_k2_authors.php: crea el usuario (nombre real de ew4r_users) y
reasigna post_author (idempotente, dry-run/APPLY).
- gen_avatars_initials.py: recrea el generador de avatares de iniciales
del #62 (paleta determinista + DejaVuSans-Bold).
- import_avatars_143.php: alta de attachment + foto_perfil con backup.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mapa de 73 libros bíblicos ES->{en,fr,it,pt} y reemplazo determinista del
token de libro en cada cita biblica del post_title (inicio, compuestos '/',
ordinales '2 Timoteo', prefijos de fiesta). Idempotente, title-only,
cotejo insensible a acentos. Aplicado: local 2103, prod wp-nuevo 1605.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pipeline de los 23 posts de Mardones (anterior/*.htm → posts), repunte de
presentaciones de Galarreta, y scripts de sync a prod de las 5 issues del día.
Incluye retoques de fecha (2012-10-10) y eliminación de target=_blank en el
homenaje 17990.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- autores_biblicos.php: 2ª pasada directa de reasignación de autor.
- quitar_multimedia.php: #4 como fichero reutilizable.
- trad_cats.php: quitado require_once con ruta local (wp eval-file ya carga WP).
Sync a prod ejecutado y verificado: código + 440 lecturas + grupo D + 8 usuarios
bíblicos + reasignación de autores y categorías. 0 fatales en las 5 portadas.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- trad_cats.php: traduce categorías estructurales (Nuevo/Antiguo Testamento, etc.)
- fix_catnames.php: corrige nombres de términos traducidos
- autores_biblicos.php: crea usuarios NT/AT por idioma y reasigna autor de lecturas
- reasign_cats.php: reasigna a cada post no-ES las categorías del ES traducidas a
su idioma (crea término espejo si falta). Idempotente/resumible (_cats_reasignadas).
7977 posts procesados, 0 con categoría en idioma incorrecto.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
build_lectionary_index.py construye un índice por referencia bíblica
descargando un ciclo litúrgico de evangelizo.ws (combina idiomas por
reading_code, robusto a fiestas trasladadas por país). lecturas_apply.py
casa las lecturas ES por referencia y apply_lecturas_wp.php crea+asocia
las traducciones en Polylang. 440/501 lecturas cubiertas y publicadas.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Descarga el texto litúrgico católico oficial en es/en/fr/it/pt por fecha
litúrgica (leccionario), para traducir las lecturas bíblicas de las cartas
sin LLM. Parsea las secciones (1ª lectura, salmo, 2ª lectura, evangelio).
Grupo C de #132.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Unificar backup/ + backups/ + Akeeba sueltos en backups/ etiquetados por fecha
(pre-incidente Enero, Marzo, INFECTED 25-mayo, clean 25-mayo, wp-local-dumps)
- Mover scripts de setup-inicial (Feb 2026) y logs de migración a archive/
- Mover kickstart.* a tools/akeeba-kickstart/
- Borrar joomla-php83-old-local-20260525/ (6.3 GB) y google-chrome.deb
- Conservar solo 2 de 8 dumps WP del 25-mayo (los hotfixes ya están consolidados)
- README de inventario con MD5 en backups/README.md y archive/README.md
- .gitignore: trackear los README dentro de carpetas gitignored
Toma el output de regenerar_clasificacion_csv.py y añade en BD las cats
1645-1650 que el CSV indique y no estén ya. Modo 'add' (default, conservador)
solo añade; modo 'replace' (opt-in) también borra cats sobrantes.
Recalcula wp_term_taxonomy.count tras los cambios.
Aplicado en local 2026-05-25 modo 'add': +183 atribuciones (1645 +1,
1646 +17, 1647 +29, 1648 +11, 1649 +93, 1650 +32). Cubre delta marzo-mayo
2026 + sub-clasificación posicional en evangelio.
Refs #42.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Regla del editor confirmada contra CSV histórico: dentro de "Evangelio y
comentarios al Evangelio" el orden es siempre:
pos 1 → lectura del evangelio
pos 2 → comentario editorial
pos 3+ → comentarios
Mejora diff contra CSV marzo 2026: 8.655 → 9.286 mismas cats
(de 1.599 a 968 divergencias, -631 resueltas).
Residual mayoritario son posts que ya no aparecen en las cartas
actuales (links eliminados, k2 ids sin mapping) o granularidad fina
no implementada (lectura dentro de eucaristía, secciones especiales).
Refs #42.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Reproduce el CSV histórico (marzo 2026) recorriendo todas las cartas
(cat 6+21+22) y extrayendo los links agrupados por encabezado. Paridad
con mu-plugins/fea-carta-portada.php (#38).
Cobertura: 5 secciones core (comentario, articulo, eucaristia,
multimedia, effa). NO cubre granularidad fina del CSV histórico
(lectura, comentario_editorial, otro, noticia) ni encabezados de
fiestas especiales — esos quedan agrupados en la cat más cercana.
Usa pymysql para no depender del CLI mysql (que rompe el parsing
cuando post_content tiene tabs/newlines). Modo --diff compara contra
un CSV de referencia y reporta divergencias.
Resuelve #42.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Re-procesa los 724 avatares col_*.png (imágenes horizontales con foto+texto)
recortándolos cuadrados centrados en la cara antes de mostrarlos como avatar
circular. Resuelve la queja del #39 sobre las fotos "stretcheadas".
- face_crop_avatar.py: Haar cascade frontal+profile, padding 0.6×, fallback
inteligente para imágenes horizontales (recorta lado izquierdo), reduce
side cuando padding no cabe (no invade lado del texto)
- regen_avatars.php: borra thumbnails viejos + regenera con la nueva imagen
para todos los attachments de uploads/autores/joomla/
Resultado: 100 caras detectadas + 624 fallbacks (mayoría placeholders sin
foto real) + 0 fail. Attachment IDs preservados.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Usado para resolver issue #39 — 736 avatares de autor importados desde
joomla-php83/images/quienes_somos/col_*.png. Crea attachments WP con
wp_insert_attachment + wp_generate_attachment_metadata y asigna foto_perfil
a cada user_id según matching exacto (login==slug) o strong (login es token).
Preserva foto_perfil ya asignado. TSV de entrada esperado en
/tmp/avatar_assignments.tsv con columnas: aid, login, posts, source, filename.
Ficheros pre-copiados a uploads/autores/joomla/.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- scripts/fix_image_paths.php: reescribe src/href "images/..." en wp_posts
a "/fea/wp-content/uploads/..." cuando el fichero existe en uploads.
Cubre comillas dobles/simples, urldecode antes de chequear filesystem.
Resuelto contra issue #34 (458 posts, 465 refs) y completado con #36
(124 posts, 128 refs tras recuperar 127 assets del backup producción).
- tools/e2e/: pipeline 3-tier para validar WP local con coste mínimo en
tokens de Claude — Playwright (Tier 1, deterministas) + Gemma 4 vision
en LM Studio (Tier 2, bajo demanda) + Claude solo lee report.md.
Issue de diseño #37, suite inicial en sites/feadulta.json (13 URLs).
- .gitignore: excluir tools/e2e/node_modules y out/.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- fea-homepage.php: nuevo shortcode [fea_prefooter] — 3 columnas (Ediciones |
Noticias de alcance | Publicidad) sobre el footer. Imagen noticias_2025.jpg
apunta a wp-content/uploads/recursos/ (fix ruta Joomla rota). Columna central
y vídeo de la semana son dinámicos (último post de las categorías respectivas).
- scripts/import_new_k2_items.py: importa 169 K2 items nuevos (id > 17873)
- scripts/fix_imported_k2_metas.py: asigna metas/cats/Polylang a esos 169 posts
- scripts/import_new_cartas.py: importa 8 cartas nuevas (ew4r_content id > 9043,
catid 27/40/41) y asigna _carta_id a los artículos K2 por fecha
- scripts/import_new_content.py: importa 58 ítems ew4r_content no-carta
(multimedia, noticias, tablón, etc.) con mapping catid→WP term
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Incluye: mu-plugins custom (fea-homepage, carta-semana), scripts de
migración/traducción/deploy, documentación de auditoría, análisis de
cartas y HTML de evangelios exportados desde Joomla.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>