# Despliegue con Docker

Despliega Capyshop en produccion usando Docker Compose con la imagen de contenedor preconstruida y PostgreSQL con pgvector.

## Requisitos Previos

- Docker Engine 20+
- Docker Compose v2+

## 1. Crear un Archivo Docker Compose

Crea un `docker-compose.yml` en tu directorio de despliegue:

```yaml
services:
  postgres:
    image: pgvector/pgvector:pg17
    restart: always
    environment:
      POSTGRES_USER: capyshop
      POSTGRES_PASSWORD: changeme
      POSTGRES_DB: capyshop
    volumes:
      - postgres_data:/var/lib/postgresql/data

  app:
    image: capyshop/capyshop:latest
    restart: unless-stopped
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgresql://capyshop:changeme@postgres:5432/capyshop
      - BETTER_AUTH_SECRET=<generate-with-openssl-rand-base64-32>
      - MASTER_SECRET=<generate-with-openssl-rand-base64-32>
      - TRUSTED_ORIGINS=https://mystore.com
      - BASE_URL=https://mystore.com
    volumes:
      - ./data:/app/data
    depends_on:
      - postgres

volumes:
  postgres_data:
```

Reemplaza los valores de ejemplo:

| Variable             | Descripcion                                                                                                                |
| -------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `BETTER_AUTH_SECRET` | Clave secreta para la firma de sesiones. Genera una con `openssl rand -base64 32`.                                         |
| `MASTER_SECRET`      | Secreto maestro de la aplicacion para cifrado. Genera uno con `openssl rand -base64 32`.                                   |
| `POSTGRES_USER`      | Nombre de usuario de PostgreSQL (debe coincidir en `postgres` y `DATABASE_URL`).                                           |
| `POSTGRES_PASSWORD`  | Contrasena de PostgreSQL (debe coincidir en `postgres` y `DATABASE_URL`).                                                  |
| `POSTGRES_DB`        | Nombre de la base de datos de PostgreSQL (debe coincidir en `postgres` y `DATABASE_URL`).                                  |
| `TRUSTED_ORIGINS`    | Lista de origenes confiables separados por comas para proteccion CSRF y redirecciones OAuth (ej., `https://mitienda.com`). |
| `BASE_URL`           | URL publica de la tienda, usada en correos, SEO, sitemaps (ej., `https://mitienda.com`).                                   |

### Opcional: Configuracion de SMTP por Variables de Entorno

Por defecto, las credenciales SMTP se gestionan en **Configuracion → Email** del panel de administracion. Si prefieres configurar el SMTP en el momento del despliegue, puedes definir las siguientes variables de entorno. Cuando estan definidas, sobreescriben los valores del panel y los campos correspondientes en el formulario de Email se deshabilitan.

```yaml
environment:
  # ...otras variables
  - SMTP_HOST=smtp.sendgrid.net
  - SMTP_PORT=587
  - SMTP_USER=apikey
  - SMTP_PASSWORD=tu-contrasena-smtp
```

| Variable        | Descripcion                                                                                       |
| --------------- | ------------------------------------------------------------------------------------------------- |
| `SMTP_HOST`     | Hostname del servidor de correo (ej., `smtp.sendgrid.net`).                                       |
| `SMTP_PORT`     | Puerto del servidor. Generalmente `587` para TLS o `465` para SSL. Debe ser entero en `[1, 65535]`. |
| `SMTP_USER`     | Nombre de usuario de inicio de sesion SMTP.                                                        |
| `SMTP_PASSWORD` | Contrasena de inicio de sesion SMTP. Almacenada en texto plano en el entorno — no pasa por la capa de cifrado de la base de datos. |

Cada variable es independiente. Si solo se define `SMTP_HOST`, los otros tres campos SMTP siguen siendo editables en la interfaz administrativa y se leen de la base de datos.

### Opcional: Almacenamiento de Assets en S3

Por defecto, las imagenes subidas y otros assets se almacenan en disco local en `data/files/`. Para enviar las cargas a un bucket compatible con S3 y servirlas desde un CDN, define el modo de almacenamiento como `s3` y configura las credenciales del bucket.

```yaml
environment:
  # ...otras vars
  - ASSETS_STORAGE_MODE=s3
  - ASSETS_S3_ENDPOINT=https://s3.amazonaws.com
  - ASSETS_S3_REGION=us-east-1
  - ASSETS_S3_BUCKET=my-store-assets
  - ASSETS_S3_ACCESS_KEY_ID=AKIA...
  - ASSETS_S3_SECRET_ACCESS_KEY=...
  - ASSETS_PUBLIC_BASE_URL=https://cdn.mystore.com
  - ASSETS_MAX_BYTES=10gb
```

| Variable                      | Descripcion                                                                                                                                                  |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `ASSETS_STORAGE_MODE`         | `local` (predeterminado) o `s3`. Cuando es `s3`, las seis vars `ASSETS_S3_*` y `ASSETS_PUBLIC_BASE_URL` son obligatorias.                                     |
| `ASSETS_S3_ENDPOINT`          | Endpoint de la API S3 (ej., `https://s3.amazonaws.com`, o la URL de tu proveedor para Cloudflare R2, Backblaze B2, MinIO, etc.).                              |
| `ASSETS_S3_REGION`            | Region del bucket (ej., `us-east-1`, `auto` para R2).                                                                                                        |
| `ASSETS_S3_BUCKET`            | Nombre del bucket.                                                                                                                                           |
| `ASSETS_S3_ACCESS_KEY_ID`     | Access key con permisos de lectura/escritura en el bucket.                                                                                                   |
| `ASSETS_S3_SECRET_ACCESS_KEY` | Secret access key.                                                                                                                                           |
| `ASSETS_PUBLIC_BASE_URL`      | URL publica base que la tienda utiliza para cargar los assets (ej., tu dominio CDN apuntando al bucket).                                                     |
| `ASSETS_MAX_BYTES`            | Limite cumulativo opcional de almacenamiento entre todos los archivos. Acepta `10gb`, `500mb` o un recuento de bytes en bruto. Aplica en modo `local` y `s3`. Los limites por carga (5 MB imagen / 50 MB video) permanecen sin cambios. |

Al migrar una tienda existente de `local` a `s3`, ejecuta el script de migracion incluido una vez dentro de un contenedor desplegado para subir los archivos existentes al bucket y pre-generar las variantes WebP:

```bash
docker exec -it <container> node build/scripts/migrate-assets-to-s3.mjs
```

El script es idempotente — volver a ejecutarlo es seguro e ignora el trabajo que ya esta hecho. Si tus originales existentes residen en el host (por ejemplo, `/docker/<store>/app_data/files/`), monta esa ruta en `/app/data/files` dentro del contenedor para que el script pueda recogerlos; de lo contrario, recurrira a lo que ya este en el bucket.

## 2. Iniciar la Aplicacion

```bash
docker compose up -d
```

Esto inicia dos servicios:

- **postgres** — PostgreSQL 17 con pgvector (`pgvector/pgvector:pg17`), escuchando en el puerto `5432`.
- **app** — El contenedor de la aplicacion (`capyshop/capyshop:latest`), expuesto en el puerto `3000`.

Al iniciar, el contenedor de la aplicacion ejecuta automaticamente las migraciones de base de datos (`prisma migrate deploy`) antes de iniciar el servidor.

## 3. Verificar

```bash
# Verificar que ambos contenedores esten corriendo
docker compose ps

# Revisar los logs de la aplicacion
docker compose logs app
```

La aplicacion deberia ser accesible en `http://<tu-host>:3000`.

## Proxy Inverso

Para despliegues en produccion con HTTPS, coloca un proxy inverso (como [Traefik](https://traefik.io/), [Caddy](https://caddyserver.com/) o nginx) delante de la aplicacion. El proxy inverso se encarga de la terminacion TLS y reenvía el trafico al puerto `3000`.

### Habilitando Compresion Gzip con Traefik

Si utilizas Traefik como proxy inverso, habilita la compresion gzip para reducir el tamano de las respuestas y mejorar la velocidad de carga — un factor en el posicionamiento en motores de busqueda. Agrega las siguientes labels al servicio `app` en tu `docker-compose.yml`:

```yaml
labels:
  - "traefik.http.middlewares.compress.compress.minresponsebodybytes=256"
  - "traefik.http.routers.STORE_NAME-app.middlewares=compress"
```

Reemplaza `STORE_NAME` con el nombre del router Traefik de tu tienda. La primera label define un middleware de compresion que aplica gzip a todas las respuestas mayores a 256 bytes. La segunda label asocia ese middleware al router de tu tienda.

## Volumenes

| Volumen / Montaje  | Proposito                                                           |
| ------------------ | ------------------------------------------------------------------- |
| `postgres_data`    | Persiste los datos de PostgreSQL entre reinicios de contenedores.   |
| `./data:/app/data` | Persiste archivos subidos y datos de la aplicacion entre reinicios. |

## Compilar desde el Codigo Fuente

Si prefieres compilar la imagen tu mismo en lugar de usar la preconstruida:

```bash
docker build -t capyshop .
```

El Dockerfile usa una compilacion multi-etapa:

1. Instala todas las dependencias y compila la aplicacion.
2. Copia solo las dependencias de produccion y el resultado de la compilacion a la imagen final.
3. Ejecuta `prisma migrate deploy` y luego inicia el servidor en el puerto `3000`.

Para usar tu imagen personalizada, actualiza el campo `image` en tu `docker-compose.yml`, o ejecuta de forma independiente:

```bash
docker run -p 3000:3000 \
  -e DATABASE_URL=postgresql://user:pass@host:5432/db \
  -e BETTER_AUTH_SECRET=your-secret \
  -e MASTER_SECRET=your-secret \
  capyshop
```

## Solucion de Problemas

| Error                                   | Causa                                                              | Solucion                                                                                                                 |
| --------------------------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------ |
| `app` se cierra inmediatamente          | Variables de entorno faltantes o invalidas                         | Revisa `docker compose logs app` y verifica todas las variables de entorno requeridas                                    |
| `ECONNREFUSED` hacia postgres           | La aplicacion inicio antes de que la base de datos estuviera lista | Reinicia la aplicacion: `docker compose restart app`                                                                     |
| `P3009 - failed migrations`             | Una migracion anterior dejo un estado inconsistente                | `DROP TABLE IF EXISTS _prisma_migrations CASCADE;` en la base de datos, luego reinicia                                   |
| `type "vector" does not exist`          | La extension pgvector no fue creada                                | La imagen `pgvector/pgvector:pg17` la incluye, pero ejecuta `CREATE EXTENSION IF NOT EXISTS vector;` si usas otra imagen |
| Puerto `3000` ya en uso                 | Otro proceso esta usando el puerto                                 | Detiene el proceso en conflicto o cambia el mapeo de puertos en `docker-compose.yml`                                     |
| Archivos subidos perdidos tras reinicio | Volumen `./data` no montado                                        | Asegurate de que la seccion `volumes` incluya `./data:/app/data`                                                         |
