11 KiB
HOWTO — operar MercadoDeVida en desarrollo y producción local
Esta guía levanta el monolito completo en una máquina y lo publica en la red local (LAN):
| Servicio | Puerto | Descripción |
|---|---|---|
| Backend | 3000 | API Fastify, health y Swagger |
| Frontend | 3003 | Tienda principal para clientes |
| Admin | 3004 | Backoffice |
| Storefront | 3005 | Storefront SEO/ISR |
| PostgreSQL | 5432 | Base de datos de infraestructura |
| Redis | 6379 | Cache/infraestructura |
El punto de entrada operativo es:
cd /ruta/al/repo/project
./scripts/monolith.sh <dev|prod> <comando>
Comandos disponibles:
start instala, migra y levanta todos los servicios
restart detiene los procesos gestionados y hace start completo
status muestra PID, estado del proceso, HTTP y URL LAN
stop detiene los cuatro procesos HTTP gestionados
logs sigue todos los logs; Ctrl-C deja servicios ejecutándose
urls imprime URLs locales y LAN sin cambiar procesos
Los PID y logs se guardan en project/.runtime/<modo>/ y no se versionan.
1. Requisitos
- Node.js 22 o superior.
- npm.
- Docker Desktop/Engine con
docker compose. - Puertos libres:
3000,3003,3004,3005. - Para acceso desde otro dispositivo: ambos equipos en la misma LAN y firewall permitiendo conexiones entrantes a esos puertos.
Comprobar:
node --version
docker --version
docker compose version
El script crea project/.env sólo si falta, usando valores locales de desarrollo. Para una instalación real de producción, proporcionar un .env propio y secretos externos al repositorio.
Variables importantes del backend:
DATABASE_URL=postgres://...
REDIS_URL=redis://...
HOST=0.0.0.0
PORT=3000
COOKIE_SECURE=false
COOKIE_SECURE=falsesólo es apropiado para HTTP dentro de una LAN de confianza. En producción pública debe usarse HTTPS yCOOKIE_SECURE=truedetrás de un reverse proxy.
2. Desarrollo
Levantar
cd project
./scripts/monolith.sh dev start
El comando comprueba puertos, levanta PostgreSQL/Redis, instala dependencias, aplica migraciones y arranca backend y las tres aplicaciones Next con binding LAN.
Reiniciar
./scripts/monolith.sh dev restart
Estado
./scripts/monolith.sh dev status
status muestra PID, proceso, HTTP y URL; devuelve código distinto de cero si algún servicio falla.
Logs
./scripts/monolith.sh dev logs
Logs individuales:
tail -F .runtime/dev/backend.log
tail -F .runtime/dev/admin.log
tail -F .runtime/dev/frontend.log
tail -F .runtime/dev/storefront.log
Detener
./scripts/monolith.sh dev stop
PostgreSQL y Redis continúan. Para detenerlos:
docker compose stop postgres redis
Para eliminar contenedores conservando volúmenes:
docker compose down
No usar docker compose down -v salvo que se quiera eliminar la base local.
3. Producción local/LAN
Este modo ejecuta builds optimizados y next start. Es adecuado para una máquina persistente en una LAN. No sustituye HTTPS, supervisor del sistema, backups ni reverse proxy de una producción pública.
Build + migraciones + levantar
cd project
./scripts/monolith.sh prod start
Usa npm ci, aplica migraciones, construye backend/admin/frontend/storefront y arranca todo en 0.0.0.0. Al finalizar ejecuta un smoke test post-arranque que verifica que ninguno de los servicios Next está sirviendo modo dev (sin HMR, sin react-refresh, sin banner (Turbopack)). Si detecta un dev server activo, aborta el arranque con un mensaje claro.
Redeploy / reinicio completo
cd project
./scripts/monolith.sh prod restart
Detiene únicamente PIDs registrados en .runtime/prod/; nunca usa pkill global. Si un puerto está ocupado por un proceso ajeno, falla y muestra el PID.
3.1 Errores comunes — jamás arranques dev en un host de producción
Regla de oro: en cualquier host expuesto al público (sea LAN, VPS o servidor de tienda), usa siempre ./scripts/monolith.sh prod. El modo dev está reservado para desarrollo local en 127.0.0.1.
Por qué next dev no es un atajo válido en producción
- Expone internals: el bundle del cliente incluye
react-refresh,__webpack_require__, el cliente HMR y los mensajes "Download the React DevTools". Cualquier visitante ve el código fuente sin minificar. - Hackeable: el endpoint
/_next/hmrqueda abierto y acepta WebSocket upgrades. Esto filtra nombres de archivos del servidor, permite lecturas no autenticadas del árbol de fuentes y rompe asunciones de seguridad (CSP, cookies Secure, CORS). - Rota el PWA: Chrome exige un
manifest.webmanifestválido.next devno genera el manifest correctamente en algunos setups y dispara errores CORS cuando un auth-proxy (Authelia) intenta proteger la ruta. - Rendimiento ~10× peor: Turbopack compila cada request bajo demanda y mantiene cachés en memoria que pueden llegar a GB. Sin monitorización, esto degrada el TPV hasta hacerlo inutilizable en horas punta.
- Rompe el ciclo de release: los artefactos
.next/de dev no son válidos paranext start. Si mezclasdevystarten el mismo árbol, los reinicios de prod fallan silenciosamente porque los PIDs no se registran.
Señales de que estás corriendo dev por accidente
- El log del servicio dice
▲ Next.js 16.3.1 (Turbopack)— la coletilla(Turbopack)solo aparece ennext dev. - La consola del navegador muestra intentos de WebSocket a
/_next/hmry mensajes[HMR] connectedrepetidos. - Verás
Download the React DevToolsal cargar cualquier página. lsof -nP -iTCP:3002 -sTCP:LISTENmuestra un PID denode .../next/dist/bin/next deven lugar denext start.
Runbook de recuperación rápida (si ya estás sirviendo dev en prod)
# 1) Identificar el proceso dev en :3002 (TPV) y :3001 (admin)
lsof -nP -iTCP:3002 -sTCP:LISTEN
lsof -nP -iTCP:3001 -sTCP:LISTEN
# 2) Detenerlo (sustituye <PID> por el del paso anterior)
kill -TERM <PID>
# 3) Confirmar que el puerto queda libre
lsof -nP -iTCP:3002 -sTCP:LISTEN # debe devolver nada
# 4) Limpiar runtime stale (incluye dev huérfano)
rm -f project/.runtime/dev/*.pid
# 5) Arrancar prod (con smoke test post-arranque)
cd project && ./scripts/monolith.sh prod start
# 6) Confirmar: el log dirá "▲ Next.js 16.3.1" SIN "(Turbopack)"
tail project/.runtime/prod/tpv.log
# debe terminar con la línea de Ready y NADA de WebSocket /_next/hmr
Si el smoke test falla igualmente, abre un ticket: hay otro proceso (posiblemente externo al script) ocupando el puerto y monolith.sh no puede broad-kill por seguridad.
Estado, logs y stop
./scripts/monolith.sh prod status
./scripts/monolith.sh prod logs
./scripts/monolith.sh prod stop
./scripts/monolith.sh prod urls
Logs individuales:
tail -F .runtime/prod/backend.log
tail -F .runtime/prod/admin.log
tail -F .runtime/prod/frontend.log
tail -F .runtime/prod/storefront.log
4. Acceso desde otro dispositivo de la LAN
IP detectada actualmente:
192.168.18.93
URLs:
- Tienda principal:
http://192.168.18.93:3003/ - Backoffice:
http://192.168.18.93:3004/ - Storefront SEO:
http://192.168.18.93:3005/ - Backend health:
http://192.168.18.93:3000/health - Swagger/OpenAPI:
http://192.168.18.93:3000/docs
La IP DHCP puede cambiar. Consultar la actual:
./scripts/monolith.sh prod urls
Forzar una IP concreta:
LAN_IP=192.168.18.93 ./scripts/monolith.sh prod restart
Diagnóstico desde el host:
curl -i http://127.0.0.1:3000/health
curl -I http://192.168.18.93:3003/
Desde el otro dispositivo:
curl -i http://192.168.18.93:3000/health
Si local funciona pero LAN no:
- confirmar
statuscon HTTP 200; - confirmar que ambos dispositivos están en la misma red y no una guest aislada;
- permitir conexiones entrantes de Node/Docker en el firewall;
- comprobar que el router no tiene client isolation;
- inspeccionar listeners:
lsof -nP -iTCP -sTCP:LISTEN | grep -E ':(3000|3003|3004|3005)\b'
5. Infraestructura y base de datos
docker ps
docker compose ps
docker exec mdv-dev-postgres pg_isready -U mdv -d mercadodevida
npm run db:status
Aplicar migraciones manualmente:
node --env-file-if-exists=.env node_modules/node-pg-migrate/bin/node-pg-migrate.js up --migrations-dir migrations
Logs:
docker logs -f mdv-dev-postgres
docker logs -f mdv-dev-redis
6. Validación antes/después del deploy
Desde la raíz:
./scripts/verify.sh
Desde project/:
npm run lint
npm run lint:boundaries
npm run typecheck
npm test
npm run build
(cd apps/admin && npm run lint && npm run typecheck && npm run build)
(cd frontend && npm run lint && npm run build)
(cd storefront && npm run lint && npm run typecheck && npm run build)
Smoke test:
./scripts/monolith.sh prod status
curl -fsS http://127.0.0.1:3000/health
curl -fsS -o /dev/null http://127.0.0.1:3003/
curl -fsS -o /dev/null http://127.0.0.1:3004/
curl -fsS -o /dev/null http://127.0.0.1:3005/
Comprobaciones adicionales anti-dev (TPV-DEV-IN-PROD):
# 1) El log del TPV NO debe contener "(Turbopack)"
! grep -F '(Turbopack)' project/.runtime/prod/tpv.log
# 2) /_next/hmr debe devolver 404 o 426 (no 200/101/405)
code=$(curl -sS -o /dev/null -w '%{http_code}' http://127.0.0.1:3002/_next/hmr)
[[ "$code" == "404" || "$code" == "426" ]]
# 3) El HTML inicial NO debe contener marcadores de dev
! curl -fsS http://127.0.0.1:3002/ | grep -qE '/__next_hmr|react-refresh|Download the React DevTools'
Repetir para :3001 (admin), :3003 (frontend) y :3004 (storefront) si también están en producción. Si cualquiera falla, sigue el runbook de §3.1 antes de continuar.
7. Problemas frecuentes
Puerto ocupado por proceso no gestionado
lsof -nP -iTCP:3000 -sTCP:LISTEN
ps -p <PID> -o pid,ppid,command
Detenerlo explícitamente y repetir. El script no mata procesos no registrados.
Backend no arranca
tail -100 .runtime/prod/backend.log
docker exec mdv-dev-postgres pg_isready -U mdv -d mercadodevida
Revisar DATABASE_URL en .env.
Un frontend usa localhost desde otro dispositivo
LAN_IP=<IP_DEL_HOST> ./scripts/monolith.sh prod restart
NEXT_PUBLIC_API_URL se incorpora durante el build, por lo que cambiar IP requiere reconstruir producción.
Cambiar puertos
BACKEND_PORT=3100 FRONTEND_PORT=3103 ADMIN_PORT=3104 STOREFRONT_PORT=3105 \
./scripts/monolith.sh prod restart
Usar los mismos overrides en todos los comandos posteriores.