# 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: ```bash cd /ruta/al/repo/project ./scripts/monolith.sh ``` Comandos disponibles: ```text 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//` 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: ```bash 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: ```dotenv DATABASE_URL=postgres://... REDIS_URL=redis://... HOST=0.0.0.0 PORT=3000 COOKIE_SECURE=false ``` > `COOKIE_SECURE=false` sólo es apropiado para HTTP dentro de una LAN de confianza. En producción pública debe usarse HTTPS y `COOKIE_SECURE=true` detrás de un reverse proxy. --- ## 2. Desarrollo ### Levantar ```bash 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 ```bash ./scripts/monolith.sh dev restart ``` ### Estado ```bash ./scripts/monolith.sh dev status ``` `status` muestra PID, proceso, HTTP y URL; devuelve código distinto de cero si algún servicio falla. ### Logs ```bash ./scripts/monolith.sh dev logs ``` Logs individuales: ```bash 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 ```bash ./scripts/monolith.sh dev stop ``` PostgreSQL y Redis continúan. Para detenerlos: ```bash docker compose stop postgres redis ``` Para eliminar contenedores conservando volúmenes: ```bash 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 ```bash 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 ```bash 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/hmr` queda 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.webmanifest` válido. `next dev` no 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 para `next start`. Si mezclas `dev` y `start` en 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 en `next dev`. - La consola del navegador muestra intentos de WebSocket a `/_next/hmr` y mensajes `[HMR] connected` repetidos. - Verás `Download the React DevTools` al cargar cualquier página. - `lsof -nP -iTCP:3002 -sTCP:LISTEN` muestra un PID de `node .../next/dist/bin/next dev` en lugar de `next start`. #### Runbook de recuperación rápida (si ya estás sirviendo dev en prod) ```bash # 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 por el del paso anterior) kill -TERM # 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 ```bash ./scripts/monolith.sh prod status ./scripts/monolith.sh prod logs ./scripts/monolith.sh prod stop ./scripts/monolith.sh prod urls ``` Logs individuales: ```bash 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: ```text 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: ```bash ./scripts/monolith.sh prod urls ``` Forzar una IP concreta: ```bash LAN_IP=192.168.18.93 ./scripts/monolith.sh prod restart ``` Diagnóstico desde el host: ```bash curl -i http://127.0.0.1:3000/health curl -I http://192.168.18.93:3003/ ``` Desde el otro dispositivo: ```bash curl -i http://192.168.18.93:3000/health ``` Si local funciona pero LAN no: 1. confirmar `status` con HTTP 200; 2. confirmar que ambos dispositivos están en la misma red y no una guest aislada; 3. permitir conexiones entrantes de Node/Docker en el firewall; 4. comprobar que el router no tiene client isolation; 5. inspeccionar listeners: ```bash lsof -nP -iTCP -sTCP:LISTEN | grep -E ':(3000|3003|3004|3005)\b' ``` --- ## 5. Infraestructura y base de datos ```bash docker ps docker compose ps docker exec mdv-dev-postgres pg_isready -U mdv -d mercadodevida npm run db:status ``` Aplicar migraciones manualmente: ```bash node --env-file-if-exists=.env node_modules/node-pg-migrate/bin/node-pg-migrate.js up --migrations-dir migrations ``` Logs: ```bash docker logs -f mdv-dev-postgres docker logs -f mdv-dev-redis ``` --- ## 6. Validación antes/después del deploy Desde la raíz: ```bash ./scripts/verify.sh ``` Desde `project/`: ```bash 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: ```bash ./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): ```bash # 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 ```bash lsof -nP -iTCP:3000 -sTCP:LISTEN ps -p -o pid,ppid,command ``` Detenerlo explícitamente y repetir. El script no mata procesos no registrados. ### Backend no arranca ```bash 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 ```bash LAN_IP= ./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 ```bash 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.