ci: Coolify deployment config — docker-compose, env example, docs, dockerignore
This commit is contained in:
@@ -0,0 +1,67 @@
|
|||||||
|
# ═══════════════════════════════════════════════════════════════
|
||||||
|
# .dockerignore — Moraworld Imports
|
||||||
|
# ═══════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
# Git
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
|
||||||
|
# Node / pnpm
|
||||||
|
node_modules
|
||||||
|
.pnpm-store
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
|
||||||
|
# Build outputs
|
||||||
|
dist
|
||||||
|
build
|
||||||
|
.next
|
||||||
|
out
|
||||||
|
coverage
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# IDE / OS
|
||||||
|
.vscode
|
||||||
|
.idea
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
*.pem
|
||||||
|
|
||||||
|
# Test / CI
|
||||||
|
.nyc_output
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# Docker
|
||||||
|
Dockerfile*
|
||||||
|
docker-compose*
|
||||||
|
coolify-compose.yml
|
||||||
|
.dockerignore
|
||||||
|
|
||||||
|
# Docs
|
||||||
|
*.md
|
||||||
|
!README.md
|
||||||
|
|
||||||
|
# Turbo
|
||||||
|
.turbo
|
||||||
|
|
||||||
|
# Uploads (runtime only)
|
||||||
|
uploads
|
||||||
|
|
||||||
|
# Prisma
|
||||||
|
packages/database/prisma/migrations/*
|
||||||
|
!packages/database/prisma/migrations/.gitkeep
|
||||||
|
|
||||||
|
# Misc
|
||||||
|
*.orig
|
||||||
|
*.bak
|
||||||
|
*.tmp
|
||||||
|
*.temp
|
||||||
|
.cache
|
||||||
+87
-129
@@ -1,132 +1,90 @@
|
|||||||
# ══════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════
|
||||||
# Moraworld Imports — Variables de entorno
|
# Moraworld Imports — Variables de entorno para Coolify (producción)
|
||||||
# Sincronizado con documentacion.html v1.1
|
# ═══════════════════════════════════════════════════════════════
|
||||||
# ══════════════════════════════════════════════════════════════
|
# Copia este archivo a .env en Coolify → Project → Environment Variables
|
||||||
|
# ═══════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
# ─── Base de datos ───────────────────────────────────────────
|
# ─── Base ──────────────────────────────────────────────────────
|
||||||
# Opción A — Coolify (PostgreSQL gestionado):
|
NODE_ENV=production
|
||||||
# DATABASE_URL="postgresql://USER:PASSWORD@HOST:5432/NOMBRE_BD?schema=moraworld"
|
NEXT_PUBLIC_API_URL=https://api.tu-dominio.com
|
||||||
|
NEXT_PUBLIC_WS_URL=https://api.tu-dominio.com
|
||||||
|
|
||||||
|
# ─── Base de datos (Coolify managed PostgreSQL) ────────────────
|
||||||
|
# Coolify inyecta DATABASE_URL automáticamente si creas un servicio Postgres
|
||||||
|
# y lo enlazas al proyecto. Formato:
|
||||||
|
# postgresql://usuario:password@host:puerto/database?schema=imports
|
||||||
|
DATABASE_URL=postgresql://moraworld:supersecret@postgres:5432/moraworld?schema=imports
|
||||||
|
|
||||||
|
# ─── Redis (Coolify managed Redis) ────────────────────────────
|
||||||
|
# Coolify inyecta REDIS_URL automáticamente.
|
||||||
|
# redis://:password@host:puerto/db
|
||||||
|
REDIS_URL=redis://:supersecret@redis:6379/2
|
||||||
|
|
||||||
|
# ─── Auth / JWT ───────────────────────────────────────────────
|
||||||
|
# Genera con: openssl rand -base64 48
|
||||||
|
JWT_SECRET=tu-jwt-secret-base64-de-48-caracteres-minimo
|
||||||
|
JWT_REFRESH_SECRET=tu-refresh-secret-base64-distinto-del-de-arriba
|
||||||
|
AUDIT_HMAC_SECRET=tu-hmac-secret-para-auditoria-32-chars-min
|
||||||
|
|
||||||
|
# ─── CORS ──────────────────────────────────────────────────────
|
||||||
|
# URLs permitidas (separadas por coma, sin espacios al final)
|
||||||
|
CORS_ORIGINS=https://app.tu-dominio.com,https://www.tu-dominio.com
|
||||||
|
|
||||||
|
# ─── Almacenamiento S3 / MinIO (opcional — para fotos/facturas) ────
|
||||||
|
# Si no configuras S3_ENDPOINT, usa disco local (./uploads)
|
||||||
|
S3_ENDPOINT=https://s3.tu-proveedor.com
|
||||||
|
S3_ACCESS_KEY=minioadmin
|
||||||
|
S3_SECRET_KEY=minioadmin
|
||||||
|
S3_BUCKET=moraworld-imports
|
||||||
|
S3_REGION=us-east-1
|
||||||
|
S3_PUBLIC_URL=https://cdn.tu-dominio.com
|
||||||
|
|
||||||
|
# ─── Stripe (pagos — Checkout Session) ────────────────────────
|
||||||
|
STRIPE_WEBHOOK_SECRET=whsec_... # De Stripe Dashboard → Webhooks
|
||||||
|
|
||||||
|
# ─── Integraciones (claves se guardan en BD vía Admin → Integraciones) ──
|
||||||
|
# Estas NO van en .env; se configuran en la UI tras el primer deploy.
|
||||||
|
# Zeptomail, SENAE, Amazon SP-API, Twilio, WhatsApp Business, INEN, Couriers, etc.
|
||||||
|
|
||||||
|
# ─── Opcional: Worker / Cron ──────────────────────────────────
|
||||||
|
# ENABLE_WORKER=true
|
||||||
|
# CRON_SENAE_POLL=0 */30 * * * *
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════
|
||||||
|
# NOTAS DE DESPLIEGUE EN COOLIFY
|
||||||
|
# ═══════════════════════════════════════════════════════════════
|
||||||
#
|
#
|
||||||
# Opción B — Local con docker compose:
|
# 1. Crea un Project en Coolify → "New Resource" → "Docker Compose"
|
||||||
DATABASE_URL="postgresql://moraworld:moraworld_dev@localhost:5432/moraworld?schema=public"
|
# Apunta a este repo, archivo: coolify-compose.yml
|
||||||
|
|
||||||
# ─── Redis (colas de notificaciones, sesiones) ───────────────
|
|
||||||
# Opción A — Coolify:
|
|
||||||
# REDIS_URL="redis://:PASSWORD@HOST:6379"
|
|
||||||
# REDIS_URL="rediss://:PASSWORD@HOST:6380" # si TLS
|
|
||||||
#
|
#
|
||||||
# Opción B — Local:
|
# 2. Añade servicios gestionados:
|
||||||
REDIS_URL="redis://localhost:6379"
|
# - PostgreSQL 16 (nombre lógico: postgres)
|
||||||
|
# - Redis 7 (nombre lógico: redis)
|
||||||
# ─── API (NestJS) ────────────────────────────────────────────
|
# Coolify inyectará DATABASE_URL y REDIS_URL automáticamente.
|
||||||
API_PORT=3001
|
#
|
||||||
API_URL="http://localhost:3001"
|
# 3. En "Environment Variables" del proyecto, pega el contenido
|
||||||
|
# de este archivo (ajusta valores reales).
|
||||||
# JWT — Autenticación OAuth 2.0 + JWT + MFA (doc §05)
|
#
|
||||||
JWT_SECRET="change-me-in-production-min-32-chars-long-abc123"
|
# 4. Dominios (Coolify → Project → Domains):
|
||||||
JWT_EXPIRES_IN="15m"
|
# - api.tu-dominio.com → servicio: api, puerto: 3001
|
||||||
JWT_REFRESH_EXPIRES_IN="7d"
|
# - app.tu-dominio.com → servicio: web, puerto: 3000
|
||||||
|
# - ws.tu-dominio.com → servicio: api, puerto: 3001 (WebSocket)
|
||||||
# ─── Web (Next.js) ───────────────────────────────────────────
|
# Coolify genera certificados TLS automáticos (Let's Encrypt).
|
||||||
WEB_PORT=3000
|
#
|
||||||
NEXT_PUBLIC_API_URL="http://localhost:3001"
|
# 5. WebSocket: el gateway corre en /ws bajo el mismo puerto 3001.
|
||||||
|
# Configura el dominio ws.tu-dominio.com apuntando al servicio api.
|
||||||
# ─── CORS (coma separada; en Coolify usar dominio real) ──────
|
#
|
||||||
CORS_ORIGINS="http://localhost:3000"
|
# 6. Health checks:
|
||||||
|
# - API: GET /api/health
|
||||||
# ─── Moraworld — Bodega NJ (doc §02) ─────────────────────────
|
# - Web: GET /status
|
||||||
# Dirección: 150 N Day St, City of Orange, NJ 07050, EE.UU.
|
#
|
||||||
WAREHOUSE_ADDRESS_STREET="150 N Day St"
|
# 7. Primer deploy → entra a https://app.tu-dominio.com/admin/configuracion
|
||||||
WAREHOUSE_ADDRESS_CITY="City of Orange"
|
# y guarda las claves de integración (Zeptomail, SENAE, Stripe, etc.).
|
||||||
WAREHOUSE_ADDRESS_STATE="NJ"
|
#
|
||||||
WAREHOUSE_ADDRESS_ZIP="07050"
|
# 8. Stripe Webhook URL en Dashboard:
|
||||||
WAREHOUSE_ADDRESS_COUNTRY="US"
|
# https://api.tu-dominio.com/api/payments/webhook?tenant=mi-tenant-slug
|
||||||
|
#
|
||||||
# ─── Amazon SP-API (doc §14 — registro por enlace) ───────────
|
# 9. Para actualizar: push a main → Coolify auto-deploy (si configurado)
|
||||||
# https://developer-docs.amazon.com/sp-api/docs
|
# o "Deploy" manual en la UI.
|
||||||
AMAZON_SP_CLIENT_ID=""
|
#
|
||||||
AMAZON_SP_CLIENT_SECRET=""
|
# ═══════════════════════════════════════════════════════════════
|
||||||
AMAZON_SP_REFRESH_TOKEN=""
|
|
||||||
AMAZON_SP_MARKETPLACE_ID="ATVPDKIKX0DER" # US marketplace
|
|
||||||
|
|
||||||
# ─── WebService SENAE (doc §11 — declaración aduanera) ───────
|
|
||||||
# SOAP / REST — Portal electrónico aduana Ecuador
|
|
||||||
SENAE_ENDPOINT="https://declaraciones.aduana.gob.ec/wsDeclaraciones"
|
|
||||||
SENAE_USERNAME=""
|
|
||||||
SENAE_PASSWORD=""
|
|
||||||
SENAE_CERT_PATH="" # Certificado digital PEM
|
|
||||||
SENAE_CERT_PASSPHRASE=""
|
|
||||||
|
|
||||||
# ─── Pasarela de pagos (doc §09 paso 8 / §18) ────────────────
|
|
||||||
# Stripe (internacional)
|
|
||||||
STRIPE_SECRET_KEY=""
|
|
||||||
STRIPE_WEBHOOK_SECRET=""
|
|
||||||
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=""
|
|
||||||
|
|
||||||
# PayPhone (Ecuador)
|
|
||||||
PAYPHONE_APP_ID=""
|
|
||||||
PAYPHONE_TOKEN=""
|
|
||||||
PAYPHONE_STORE_ID=""
|
|
||||||
|
|
||||||
# ─── WhatsApp Business API (doc §16 / §18) ───────────────────
|
|
||||||
# Notificaciones automáticas + botón flotante de contacto
|
|
||||||
WHATSAPP_API_URL="https://graph.facebook.com/v19.0"
|
|
||||||
WHATSAPP_PHONE_NUMBER_ID=""
|
|
||||||
WHATSAPP_ACCESS_TOKEN=""
|
|
||||||
# Número NJ (operaciones) y Cuenca (aduana) — doc §21
|
|
||||||
WHATSAPP_NJ_NUMBER="+15550000001"
|
|
||||||
WHATSAPP_CUENCA_NUMBER="+5939900000001"
|
|
||||||
|
|
||||||
# ─── Email (doc §16) ─────────────────────────────────────────
|
|
||||||
# SendGrid
|
|
||||||
SENDGRID_API_KEY=""
|
|
||||||
EMAIL_FROM="noreply@moraworldimports.com"
|
|
||||||
|
|
||||||
# AWS SES (alternativa)
|
|
||||||
# AWS_SES_REGION="us-east-1"
|
|
||||||
# AWS_SES_ACCESS_KEY=""
|
|
||||||
# AWS_SES_SECRET_KEY=""
|
|
||||||
|
|
||||||
# ─── SMS Gateway (doc §16) ───────────────────────────────────
|
|
||||||
SMS_PROVIDER="twilio" # twilio | vonage
|
|
||||||
TWILIO_ACCOUNT_SID=""
|
|
||||||
TWILIO_AUTH_TOKEN=""
|
|
||||||
TWILIO_PHONE_NUMBER=""
|
|
||||||
|
|
||||||
# ─── Couriers internacionales (doc §18) ──────────────────────
|
|
||||||
# FedEx
|
|
||||||
FEDEX_CLIENT_ID=""
|
|
||||||
FEDEX_CLIENT_SECRET=""
|
|
||||||
FEDEX_ACCOUNT_NUMBER=""
|
|
||||||
|
|
||||||
# DHL
|
|
||||||
DHL_API_KEY=""
|
|
||||||
DHL_API_SECRET=""
|
|
||||||
|
|
||||||
# UPS
|
|
||||||
UPS_CLIENT_ID=""
|
|
||||||
UPS_CLIENT_SECRET=""
|
|
||||||
|
|
||||||
# ─── S3 / Almacenamiento de archivos (doc §05) ───────────────
|
|
||||||
# Fotos de paquetes, facturas, documentos SENAE
|
|
||||||
# AWS S3
|
|
||||||
S3_BUCKET="moraworld-files"
|
|
||||||
S3_REGION="us-east-1"
|
|
||||||
S3_ACCESS_KEY=""
|
|
||||||
S3_SECRET_KEY=""
|
|
||||||
|
|
||||||
# MinIO local (docker-compose — desarrollo):
|
|
||||||
# S3_ENDPOINT="http://localhost:9000"
|
|
||||||
# S3_BUCKET="moraworld"
|
|
||||||
# S3_ACCESS_KEY="minioadmin"
|
|
||||||
# S3_SECRET_KEY="minioadmin"
|
|
||||||
# S3_REGION="us-east-1"
|
|
||||||
# S3_FORCE_PATH_STYLE="true"
|
|
||||||
|
|
||||||
# ─── SIEM / Monitoreo (doc §05 ISO A.12) ─────────────────────
|
|
||||||
# Datadog / New Relic / Sentry
|
|
||||||
SENTRY_DSN=""
|
|
||||||
# DATADOG_API_KEY=""
|
|
||||||
|
|
||||||
# ─── Entorno ─────────────────────────────────────────────────
|
|
||||||
NODE_ENV="development" # development | staging | production
|
|
||||||
|
|||||||
+193
@@ -0,0 +1,193 @@
|
|||||||
|
# Despliegue en Coolify — Moraworld Imports
|
||||||
|
|
||||||
|
> **TL;DR** — Coolify gestiona Postgres + Redis. Solo necesitas el repo, `coolify-compose.yml` y las variables de `.env.example`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1️⃣ Requisitos previos
|
||||||
|
|
||||||
|
- Servidor con **Docker 24+** y **Coolify** instalado (`curl -fsSL https://get.coolify.io | bash`)
|
||||||
|
- Dominio propio con DNS apuntando al servidor (A/AAAA + wildcard `*.tu-dominio.com`)
|
||||||
|
- Cuenta GitHub/GitLab con el repo clonado
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2️⃣ Crear Project en Coolify
|
||||||
|
|
||||||
|
1. **Projects → New Project** → nombre: `moraworld-imports`
|
||||||
|
2. **Resources → New → Docker Compose**
|
||||||
|
- **Repository**: `https://github.com/lizandrogd/moraworld-imports`
|
||||||
|
- **Branch**: `main`
|
||||||
|
- **Compose file**: `coolify-compose.yml`
|
||||||
|
- **Build pack**: `Dockerfile` (auto-detectado)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3️⃣ Servicios gestionados (managed services)
|
||||||
|
|
||||||
|
En el projecto → **Services → Add Service**:
|
||||||
|
|
||||||
|
| Servicio | Tipo | Nombre lógico | Notas |
|
||||||
|
|----------|------|---------------|-------|
|
||||||
|
| **PostgreSQL** | `PostgreSQL 16` | `postgres` | Coolify inyecta `DATABASE_URL` |
|
||||||
|
| **Redis** | `Redis 7` | `redis` | Coolify inyecta `REDIS_URL` |
|
||||||
|
| **MinIO** (opcional) | `MinIO` | `minio` | Solo si no usas S3 externo; expón puertos 9000/9001 |
|
||||||
|
|
||||||
|
> **Importante**: Los nombres lógicos (`postgres`, `redis`) deben coincidir con los hostnames usados en `DATABASE_URL`/`REDIS_URL` si los escribes a mano. Coolify los resuelve automáticamente.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4️⃣ Variables de entorno
|
||||||
|
|
||||||
|
En **Project → Environment Variables**, añade **una por una** (no pegues el bloque completo):
|
||||||
|
|
||||||
|
| Variable | Ejemplo / Generación |
|
||||||
|
|----------|----------------------|
|
||||||
|
| `NODE_ENV` | `production` |
|
||||||
|
| `NEXT_PUBLIC_API_URL` | `https://api.tu-dominio.com` |
|
||||||
|
| `NEXT_PUBLIC_WS_URL` | `https://api.tu-dominio.com` |
|
||||||
|
| `JWT_SECRET` | `openssl rand -base64 48` |
|
||||||
|
| `JWT_REFRESH_SECRET` | `openssl rand -base64 48` (distinto) |
|
||||||
|
| `AUDIT_HMAC_SECRET` | `openssl rand -hex 32` |
|
||||||
|
| `CORS_ORIGINS` | `https://app.tu-dominio.com,https://www.tu-dominio.com` |
|
||||||
|
| `S3_ENDPOINT` | `https://s3.tu-proveedor.com` (o vacío para disco local) |
|
||||||
|
| `S3_ACCESS_KEY` | `minioadmin` |
|
||||||
|
| `S3_SECRET_KEY` | `minioadmin` |
|
||||||
|
| `S3_BUCKET` | `moraworld-imports` |
|
||||||
|
| `S3_REGION` | `us-east-1` |
|
||||||
|
| `S3_PUBLIC_URL` | `https://cdn.tu-dominio.com` |
|
||||||
|
| `STRIPE_WEBHOOK_SECRET` | `whsec_...` (de Stripe Dashboard) |
|
||||||
|
|
||||||
|
> **No pongas** claves de Zeptomail, SENAE, Amazon, Twilio, WhatsApp, etc. aquí. Se configuran en **Admin → Integraciones** tras el primer deploy.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5️⃣ Dominios y TLS
|
||||||
|
|
||||||
|
En **Project → Domains** añade:
|
||||||
|
|
||||||
|
| Dominio | Servicio | Puerto | TLS |
|
||||||
|
|---------|----------|--------|-----|
|
||||||
|
| `api.tu-dominio.com` | `api` | `3001` | ✅ Auto |
|
||||||
|
| `app.tu-dominio.com` | `web` | `3000` | ✅ Auto |
|
||||||
|
| `ws.tu-dominio.com` | `api` | `3001` | ✅ Auto (WebSocket) |
|
||||||
|
|
||||||
|
Coolify genera certificados Let's Encrypt automáticos y renueva cada 60 días.
|
||||||
|
|
||||||
|
> **WebSocket**: el gateway corre en `/ws` bajo el mismo puerto 3001. El dominio `ws.tu-dominio.com` apunta al mismo servicio `api`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6️⃣ Health Checks
|
||||||
|
|
||||||
|
Coolify usa los health checks del `docker-compose` para saber cuándo el servicio está listo:
|
||||||
|
|
||||||
|
- **API**: `GET http://localhost:3001/api/health` → 200 OK
|
||||||
|
- **Web**: `GET http://localhost:3000/status` → 200 OK (crea `apps/web/public/status.txt` con `ok` si no existe)
|
||||||
|
|
||||||
|
Si el health check falla, Coolify reintenta según `retries` y `start_period`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7️⃣ Primer deploy y post-configuración
|
||||||
|
|
||||||
|
1. **Deploy** → espera a que ambos servicios estén "Healthy"
|
||||||
|
2. Entra a `https://app.tu-dominio.com`
|
||||||
|
3. Regístrate (primer usuario = `SUPER_ADMIN` automático si BD vacía)
|
||||||
|
4. Ve a **Admin → Configuración → Integraciones** y guarda:
|
||||||
|
- **Zeptomail**: `zeptomail_api_key`, `email_from`, `email_from_name`
|
||||||
|
- **SENAE**: `senae_endpoint`, `senae_api_key`, `senae_ruc`, `senae_agent_code`
|
||||||
|
- **Stripe**: `stripe_public_key`, `stripe_secret_key`, `stripe_webhook_secret`
|
||||||
|
- **Amazon SP-API**: `amazon_client_id`, `amazon_client_secret`, `amazon_refresh_token`
|
||||||
|
- **Twilio SMS**: `sms_account_sid`, `sms_auth_token`, `sms_from_number`
|
||||||
|
- **WhatsApp Business**: `whatsapp_api_token`, `whatsapp_phone_number_id`
|
||||||
|
- **Couriers** (FedEx/DHL/UPS), **INEN**, **Bodega**, etc. según necesites
|
||||||
|
5. **Stripe Webhook** en Dashboard → `https://api.tu-dominio.com/api/payments/webhook?tenant=mi-tenant-slug`
|
||||||
|
- Eventos: `checkout.session.completed`, `checkout.session.expired`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8️⃣ Actualizaciones
|
||||||
|
|
||||||
|
**Auto-deploy** (recomendado):
|
||||||
|
- Project → Settings → **Auto Deploy** = ON
|
||||||
|
- Push a `main` → Coolify detecta, build y despliega sin downtime (rolling update)
|
||||||
|
|
||||||
|
**Manual**:
|
||||||
|
- Project → Deploy → **Force rebuild** si cambiaste Dockerfile o dependencias
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9️⃣ Backups y recuperación
|
||||||
|
|
||||||
|
- **PostgreSQL**: Coolify → Service `postgres` → Backups → Schedule (diario/semanal)
|
||||||
|
- **Redis**: No crítico (cache/sesiones), pero puedes snapshottear
|
||||||
|
- **MinIO**: Versiona el bucket o replica a otro S3
|
||||||
|
- **Código**: GitHub es la fuente de verdad
|
||||||
|
|
||||||
|
Para restaurar: detén servicios → restaura dump SQL → `pnpm prisma db push` → reinicia.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔟 Troubleshooting rápido
|
||||||
|
|
||||||
|
| Síntoma | Causa probable | Solución |
|
||||||
|
|---------|----------------|----------|
|
||||||
|
| API 502 / health fail | `DATABASE_URL` mal formada | Revisa variable; usa hostname `postgres` |
|
||||||
|
| WebSocket no conecta | Dominio `ws.` no apunta a `api` | Añade dominio `ws.tu-dominio.com` → servicio `api` |
|
||||||
|
| CORS error | `CORS_ORIGINS` sin tu frontend | Añade `https://app.tu-dominio.com` |
|
||||||
|
| Stripe webhook 404 | URL mal escrita en Dashboard | Debe ser `/api/payments/webhook?tenant=<slug>` |
|
||||||
|
| Prisma migrate fail | `prisma db push` en prod | Usa `prisma migrate deploy` en CI si migras schema |
|
||||||
|
| Uploads 404 | `S3_ENDPOINT` vacío pero archivos en disco | Configura MinIO/S3 o sirve `/uploads` vía reverse proxy |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📁 Estructura de archivos Coolify
|
||||||
|
|
||||||
|
```
|
||||||
|
moraworld-imports/
|
||||||
|
├── coolify-compose.yml # ← Coolify usa este archivo
|
||||||
|
├── .env.example # ← Copia valores a Coolify UI
|
||||||
|
├── apps/
|
||||||
|
│ ├── api/Dockerfile # Multi-stage, standalone node
|
||||||
|
│ └── web/Dockerfile # Next.js standalone output
|
||||||
|
├── packages/database/ # Prisma schema
|
||||||
|
└── COOLIFY.md # ← Este archivo
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔐 Secrets management
|
||||||
|
|
||||||
|
Coolify encripta las variables marcadas como **Secret** en la UI.
|
||||||
|
Para rotar secrets:
|
||||||
|
1. Genera nuevo valor local
|
||||||
|
2. Actualiza en Coolify → Environment Variables
|
||||||
|
3. Redeploy (rolling, cero downtime)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Monitoring básico
|
||||||
|
|
||||||
|
- **Logs**: Coolify → Service → Logs (stdout/stderr)
|
||||||
|
- **Metrics**: Coolify → Service → Metrics (CPU, RAM, Network)
|
||||||
|
- **Uptime**: Configura Uptime Kuma / Better Uptime apuntando a `/api/health` y `/status`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Checklist pre-producción
|
||||||
|
|
||||||
|
- [ ] `JWT_SECRET` y `JWT_REFRESH_SECRET` únicos, 48+ chars base64
|
||||||
|
- [ ] `AUDIT_HMAC_SECRET` 32+ chars hex
|
||||||
|
- [ ] `CORS_ORIGINS` solo tus dominios reales (sin `*`)
|
||||||
|
- [ ] `DATABASE_URL` apunta a Postgres gestionado por Coolify
|
||||||
|
- [ ] `REDIS_URL` apunta a Redis gestionado por Coolify
|
||||||
|
- [ ] Dominios `api.`, `app.`, `ws.` creados en Coolify con TLS
|
||||||
|
- [ ] Health checks pasan en ambos servicios
|
||||||
|
- [ ] Integraciones guardadas en Admin UI
|
||||||
|
- [ ] Stripe webhook registrado y probado
|
||||||
|
- [ ] Backup automático de Postgres configurado
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**¿Problemas?** Revisa logs en Coolify → Service → Logs. La mayoría son variables de entorno mal escritas o health checks que tardan más de `start_period`.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ok
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
# Coolify Deployment — docker-compose for production (managed DB/Redis by Coolify)
|
||||||
|
|
||||||
|
services:
|
||||||
|
api:
|
||||||
|
image: ghcr.io/lizandrogd/moraworld-imports/api:latest
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: apps/api/Dockerfile
|
||||||
|
args:
|
||||||
|
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL}
|
||||||
|
container_name: moraworld-api
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "3001:3001"
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=production
|
||||||
|
- PORT=3001
|
||||||
|
- DATABASE_URL=${DATABASE_URL}
|
||||||
|
- REDIS_URL=${REDIS_URL}
|
||||||
|
- JWT_SECRET=${JWT_SECRET}
|
||||||
|
- JWT_REFRESH_SECRET=${JWT_REFRESH_SECRET}
|
||||||
|
- AUDIT_HMAC_SECRET=${AUDIT_HMAC_SECRET}
|
||||||
|
- CORS_ORIGINS=${CORS_ORIGINS}
|
||||||
|
- S3_ENDPOINT=${S3_ENDPOINT}
|
||||||
|
- S3_ACCESS_KEY=${S3_ACCESS_KEY}
|
||||||
|
- S3_SECRET_KEY=${S3_SECRET_KEY}
|
||||||
|
- S3_BUCKET=${S3_BUCKET}
|
||||||
|
- S3_REGION=${S3_REGION}
|
||||||
|
- S3_PUBLIC_URL=${S3_PUBLIC_URL}
|
||||||
|
- STRIPE_WEBHOOK_SECRET=${STRIPE_WEBHOOK_SECRET}
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "-qO-", "http://localhost:3001/api/health"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
start_period: 15s
|
||||||
|
networks:
|
||||||
|
- moraworld-network
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpus: '1.0'
|
||||||
|
memory: 1G
|
||||||
|
reservations:
|
||||||
|
cpus: '0.5'
|
||||||
|
memory: 512M
|
||||||
|
|
||||||
|
web:
|
||||||
|
image: ghcr.io/lizandrogd/moraworld-imports/web:latest
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: apps/web/Dockerfile
|
||||||
|
args:
|
||||||
|
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL}
|
||||||
|
container_name: moraworld-web
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=production
|
||||||
|
- PORT=3000
|
||||||
|
- NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL}
|
||||||
|
- NEXT_PUBLIC_WS_URL=${NEXT_PUBLIC_WS_URL}
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "-qO-", "http://localhost:3000/status"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
start_period: 15s
|
||||||
|
networks:
|
||||||
|
- moraworld-network
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpus: '1.0'
|
||||||
|
memory: 1G
|
||||||
|
reservations:
|
||||||
|
cpus: '0.5'
|
||||||
|
memory: 512M
|
||||||
|
|
||||||
|
# Worker opcional: jobs asíncronos (ej. polling couriers, cron SENAE)
|
||||||
|
# worker:
|
||||||
|
# image: ghcr.io/lizandrogd/moraworld-imports/api:latest
|
||||||
|
# command: ["node", "dist/worker.js"]
|
||||||
|
# environment: *api_env
|
||||||
|
# networks: [moraworld-network]
|
||||||
|
# deploy:
|
||||||
|
# resources:
|
||||||
|
# limits:
|
||||||
|
# cpus: '0.5'
|
||||||
|
# memory: 512M
|
||||||
|
|
||||||
|
networks:
|
||||||
|
moraworld-network:
|
||||||
|
driver: bridge
|
||||||
|
name: moraworld-network
|
||||||
Reference in New Issue
Block a user