Files
mercadodevida/docs/HOWTO-monolith.md

381 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 <dev|prod> <comando>
```
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/<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:
```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 <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
```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 <PID> -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=<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
```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.