feat: infraestructura de produccion (docker-compose, nginx, backup)

- docker-compose: 4 apps + api + postgres/redis (red interna, sin puertos
  expuestos), healthchecks, restart policies, volumenes
- nginx: client_max_body_size 10m, headers X-Forwarded, webroot certbot,
  certs montados desde /etc/letsencrypt (renovacion sin copiar)
- scripts/backup.sh: sqlite/pg_dump + uploads con retencion 30 dias
- README con instrucciones de deploy y pendientes
This commit is contained in:
2026-08-21 23:42:24 +00:00
parent 0546f35d2a
commit 0fb92364d8
4 changed files with 222 additions and 0 deletions

78
docker-compose.yml Normal file
View File

@@ -0,0 +1,78 @@
# FRASA Ecosystem — orquestación de producción
# Nota: el API usa SQLite (volumen api_data) hasta la migración de Prisma a Postgres.
# Postgres y Redis quedan listos pero SOLO en la red interna (sin puertos expuestos).
services:
nginx:
image: nginx:alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- /etc/letsencrypt:/etc/letsencrypt:ro # certs siempre al día tras certbot renew
- certbot_webroot:/var/www/certbot # challenge HTTP-01
depends_on:
- api
- landing
- cotizador
- alta-clientes
- cobranza
api:
build: ./api-gateway
restart: unless-stopped
env_file: .env
environment:
NODE_ENV: production
PORT: 3000
DATABASE_URL: "file:/data/dev.db" # TODO: postgres cuando se migre el provider
UPLOADS_DIR: "/data/uploads"
volumes:
- api_data:/data
# Sin puerto expuesto: solo se alcanza via nginx en /api
landing:
build: ./apps/landing
restart: unless-stopped
cotizador:
build: ./apps/cotizador
restart: unless-stopped
alta-clientes:
build: ./apps/alta-clientes
restart: unless-stopped
cobranza:
build: ./apps/cobranza
restart: unless-stopped
postgres:
image: postgres:16-alpine
restart: unless-stopped
env_file: .env
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 5
# Sin puerto expuesto al host
redis:
image: redis:7-alpine
restart: unless-stopped
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 5
# Sin puerto expuesto al host
volumes:
api_data:
postgres_data:
certbot_webroot: