381 lines
11 KiB
Markdown
381 lines
11 KiB
Markdown
# 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.
|