Files

11 KiB
Raw Permalink Blame History

TPV-DEV-IN-PROD — Diseño técnico

Arquitecto: design · Estado: ready for build

1 · Diagnóstico (root cause)

El host de producción tpv-mv.rikrdo.com está sirviendo el TPV con next dev (Turbopack) en lugar de next start. La app funciona superficialmente, pero expone internals de desarrollo y rompe la experiencia PWA.

1.1 Evidencia

Fuente Hallazgo
Consola del navegador (tpv-mv.rikrdo.com) [HMR] connected repetido, web-socket.ts:50 WebSocket … /_next/hmr fallando, "Download the React DevTools". Solo next dev emite HMR.
project/.runtime/dev/tpv.log ▲ Next.js 16.3.1 (Turbopack) ← banner exclusivo de next dev. 324 líneas con tráfico real.
project/.runtime/prod/tpv.log ▲ Next.js 16.3.1 (sin Turbopack) → arranque correcto de next start, 5 líneas, sin PID asociado, sin tráfico posterior. Proceso prod nunca se mantuvo.
project/apps/pos/.next/BUILD_ID Existe (UjpwEJ1_CRR9xKXBHOcBu, 26 ago 23:25). El build de prod existe; el problema es de arranque, no de build.
project/apps/pos/package.json dev: next dev --port 3002, start: next start --port 3002. Definición correcta.
project/scripts/monolith.sh Distingue dev:tpv (Turbopack) vs prod:tpv (no Turbopack). Lógica correcta.
project/apps/pos/next.config.ts Tiene turbopack: { root: __dirname } y allowedDevOrigins (incluye tpv-mv.rikrdo.com). Estas opciones son dev-only; su presencia confirma que la config se diseñó pensando en dev.
git log reciente No hay commits que toquen monolith.sh, next.config.ts o el arranque. El bug no vino de un cambio de código.

1.2 Cadena causal

  1. Operador del host remoto ejecutó monolith.sh dev start (o equivalente: cd project/apps/pos && npm run dev) en lugar de monolith.sh prod start.
  2. El proceso next dev se ató al puerto 3002 (único en el host).
  3. Traefik enruta tpv-mv.rikrdo.com → :3002 → recibe la app de TPV en modo dev.
  4. El proceso next start (prod) que pudo haber existido antes fue desplazado, o nunca se arrancó, o murió silenciosamente.
  5. Resultado: prod domain sirve dev internals; HMR, React DevTools y WebSocket a /_next/hmr quedan expuestos al público.

1.3 Por qué pasó

  • El script monolith.sh y la doc (HOWTO-monolith.md) ya dicen "prod = next start" y "dev = next dev". El conocimiento está correcto.
  • No existe un smoke test post-deploy que verifique que el proceso activo es next start, no next dev. Un operador que arranca con dev por confusión o Shortcut no recibe señal hasta que aparece un bug raro (HMR, favicon, manifest).
  • No hay una advertencia explícita que diga "dev mode en host público = expones dev internals y rompes PWA". Las consecuencias están implícitas.

2 · Enfoque del fix

Refuerzo de la guardarraíl operacional en 3 capas, todas centradas en detectar el caso "dev corriendo donde debería ir prod" en el momento del despliegue, no horas después en la consola del cliente.

Capa A — Smoke test obligatorio post monolith.sh prod start

Añadir una función smoke_check_no_dev_markers() que, tras wait_http para los 4 servicios Next.js (admin, tpv, frontend, storefront — backend no usa Next), ejecute:

  1. curl -fs http://127.0.0.1:$PORT/ | grep -F ... — grep negativo sobre la respuesta HTML inicial. Falla si encuentra cualquiera de:
    • /__next_hmr
    • react-refresh
    • Download the React DevTools
    • webpack-hmr
    • __webpack_require__
    • /_next/static/chunks/_devPagesManifest
  2. curl -sS -o /dev/null -w '%{http_code}' http://127.0.0.1:$PORT/_next/hmr — esperar HTTP 404 o 426 en el endpoint HMR. Cualquier otro código (200, 101, 405) indica que un dev server está escuchando.
  3. grep -F '(Turbopack)' "$log" — el log de arranque del proceso no debe contener el sufijo (Turbopack), exclusivo de next dev.

La función se invoca desde start_all después del loop spawn_service solo cuando MODE == "prod". Si cualquiera falla: exit 1, mensaje claro "DEV MODE DETECTED ON PROD START — refusing to continue", y se eliminan los PIDs que monolith.sh acaba de registrar (deja el sistema en estado limpio).

Por qué aquí: captura el bug en el momento del deploy, antes de que Traefik apunte tráfico de usuarios a la instancia incorrecta.

Capa B — Documentación endurecida

  • docs/HOWTO-monolith.md: añadir sección "§3.1 Errores comunes — jamás arranques dev en un host de producción" con:
    • Por qué next dev no es un atajo válido: expone HMR, React DevTools, source maps y rutas internas; consume ~10× memoria; no compila para prod; rompe PWA manifest y cookies Secure assumptions.
    • Comando correcto: ./scripts/monolith.sh prod start (o prod restart).
    • Señales de que estás en dev por accidente: el log dirá (Turbopack) y verás intentos de WebSocket a /_next/hmr en la consola del navegador.
  • docs/pos/POS_OPERATIONS.md §1.3 (POS app deployment): añadir una línea inline "Nunca npm run dev ni monolith.sh dev en el host público — usar siempre monolith.sh prod."
  • docs/HOWTO-monolith.md §6 (Validación antes/después del deploy): añadir el smoke test al checklist post-deploy.

Capa C — Runbook de remediación (operacional, no código)

Añadir a docs/HOWTO-monolith.md un mini-runbook "Si ya estás sirviendo dev en prod, recuperación rápida":

# 1) Identificar el proceso dev en :3002
lsof -nP -iTCP:3002 -sTCP:LISTEN

# 2) Detenerlo
kill -TERM <PID>

# 3) Limpiar runtime stale (incluye dev huérfano)
rm -f project/.runtime/dev/*.pid

# 4) Arrancar prod
cd project && ./scripts/monolith.sh prod start

# 5) Confirmar: el log dirá "▲ Next.js 16.3.1" SIN "(Turbopack)"
#    y el smoke test post-start debe pasar.
tail project/.runtime/prod/tpv.log

3 · Alcance (scope)

Dentro:

  • project/scripts/monolith.sh — añadir smoke_check_no_dev_markers + invocación en start_all cuando MODE == "prod".
  • docs/HOWTO-monolith.md — §3.1 + actualización de §6.
  • docs/pos/POS_OPERATIONS.md — nota inline en §1.3.
  • work/artifacts/TPV-DEV-IN-PROD/implementer.md con evidencia de los comandos y outputs.

Fuera (deliberado):

  • No se modifica next.config.ts: turbopack.root y allowedDevOrigins son opciones dev-only que next start ignora sin warning. Tocarlas añade ruido sin valor; el fix correcto es operacional.
  • No se modifica package.json (dev/start scripts ya correctos).
  • No se toca Authelia / Traefik (config fuera del repo). La capa C deja claro que la recuperación es operacional.
  • No se mete un check en CI: el smoke test es post-deploy; verificar en CI no garantiza el comportamiento del host real.

4 · Decisiones de diseño y por qué

  • Smoke test basado en grep -F sobre HTML, no en introspección del proceso. Es black-box: no depende de flags internas de Next. Funciona aunque la versión de Next cambie. Si Next 17 reorganiza los markers HMR, hay que actualizar el patrón — pero el coste de un grep es despreciable y el acierto es evidente.
  • Test cubre los 4 servicios Next, no solo TPV. El bug es sistémico: cualquier servicio Next del repo es susceptible. Limitar al TPV dejaría una bomba de tiempo en admin/frontend/storefront.
  • El smoke test falla ruidosamente (exit 1) en lugar de warn. Warn se ignora; un fail del deploy obliga a reaccionar. El runbook de remediación está a 1 scroll.
  • No se automatiza el "kill dev before prod" más allá de lo que ya hace clear_stale_deployments. Automatizar un kill "inteligente" de procesos ajenos es peligroso (false positives en puertos compartidos). Mejor detectar y abortar.
  • Capa C (runbook) es código-en-documentos, no automatización. Mantenerlo como doc evita código que pueda fallar cuando el operador lo necesita.

5 · Acceptance criteria

  1. bash -n project/scripts/monolith.sh → exit 0 (sintaxis válida).
  2. ./scripts/monolith.sh dev start arranca en dev y no ejecuta el smoke test (es dev, HMR es esperado).
  3. ./scripts/monolith.sh prod start arranca en prod y ejecuta el smoke test; pasa si los 4 servicios Next se sirvieron con next start.
  4. Forzar npm run dev (en lugar del script) en el host y luego intentar monolith.sh prod start → el smoke test debe fallar con mensaje claro.
  5. docs/HOWTO-monolith.md contiene §3.1 con la sección de error común y el runbook de recuperación.
  6. docs/pos/POS_OPERATIONS.md §1.3 contiene la advertencia inline.
  7. ./scripts/verify.sh → exit 0.
  8. work/artifacts/TPV-DEV-IN-PROD/implementer.md documenta los comandos ejecutados y los outputs observados.

6 · Verificación esperada

  • bash -n project/scripts/monolith.sh → exit 0.
  • Búsqueda de los markers HMR en el HTML renderizado por un dev local: curl -fs http://127.0.0.1:3002/ | grep -E '/__next_hmr|react-refresh' → matches (sanity check del test).
  • Smoke test sobre prod local (si hay tiempo): monolith.sh prod start → grep -F en / → no matches → PASS.
  • ./scripts/verify.sh → exit 0.

7 · Riesgos y mitigaciones

Riesgo Mitigación
Next 17 reorganiza markers HMR y rompe el grep Comentario explícito en el script listando los markers y la fuente. Si cambia, hay que actualizar el patrón — pero el cambio será visible en cualquier deploy.
El smoke test genera falsos positivos si el HTML inicial aún no incluye el HMR client (timing) Reintento: si el primer curl no encuentra el marker pero el proceso es next dev, el segundo marker (/_next/hmr 404/426) lo detecta. Doble cobertura.
Operador preocupado por tiempo extra en monolith.sh prod start El smoke test son 4 curls + 4 greps = <500ms. Despreciable comparado con el build.
Cambio en monolith.sh rompe otros features que dependen de su output Smoke test solo afecta a exit code; los logs siguen imprimiendo lo mismo. monolith.sh status/stop/logs/urls no se tocan.

8 · Resumen para el implementer

  1. Editar project/scripts/monolith.sh:
    • Añadir función smoke_check_no_dev_markers() antes de start_all.
    • Definir array NEXT_SERVICES=(admin tpv frontend storefront) y array HMR_MARKERS=('/__next_hmr' 'react-refresh' 'Download the React DevTools' 'webpack-hmr' '__webpack_require__' '_devPagesManifest').
    • En start_all, tras el loop spawn_service, agregar: [[ "$MODE" == "prod" ]] && smoke_check_no_dev_markers.
    • Si falla: echo "[FAIL] ..." >&2; stop_all; exit 1.
  2. Editar docs/HOWTO-monolith.md:
    • Añadir §3.1 "Errores comunes — jamás arranques dev en un host de producción" + runbook de recuperación.
    • En §6, añadir bullet "Smoke test post-start verde (incluye verificación HMR-free)".
  3. Editar docs/pos/POS_OPERATIONS.md §1.3: añadir una línea de advertencia.
  4. Ejecutar bash -n project/scripts/monolith.sh (sintaxis) y ./scripts/verify.sh (full).
  5. Documentar todo en work/artifacts/TPV-DEV-IN-PROD/implementer.md.

9 · Próximo stage

build (implementer): ejecutar §8.