# Docker Deployment

Deploy Capyshop in productie met Docker Compose, de voorgebouwde containerimage en PostgreSQL met pgvector.

## Vereisten

- Docker Engine 20+
- Docker Compose v2+

## 1. Docker Compose Bestand Aanmaken

Maak een `docker-compose.yml` in je deployment-directory:

```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:
```

Vervang de voorbeeldwaarden:

| Variable             | Beschrijving                                                                                                           |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `BETTER_AUTH_SECRET` | Geheime sleutel voor sessie-ondertekening. Genereer met `openssl rand -base64 32`.                                     |
| `MASTER_SECRET`      | Applicatie-hoofdgeheim voor versleuteling. Genereer met `openssl rand -base64 32`.                                     |
| `POSTGRES_USER`      | PostgreSQL-gebruikersnaam (moet overeenkomen in `postgres` en `DATABASE_URL`).                                         |
| `POSTGRES_PASSWORD`  | PostgreSQL-wachtwoord (moet overeenkomen in `postgres` en `DATABASE_URL`).                                             |
| `POSTGRES_DB`        | PostgreSQL-databasenaam (moet overeenkomen in `postgres` en `DATABASE_URL`).                                           |
| `TRUSTED_ORIGINS`    | Kommagescheiden lijst van vertrouwde origins voor CSRF-bescherming en OAuth-redirects (bijv. `https://mijnwinkel.nl`). |
| `BASE_URL`           | Publieke URL van de winkel, gebruikt in e-mails, SEO, sitemaps (bijv. `https://mijnwinkel.nl`).                        |

### Optioneel: SMTP-configuratie via Omgevingsvariabelen

Standaard worden SMTP-gegevens beheerd via **Instellingen → E-mail** in het admin-paneel. Als je SMTP liever bij het uitrollen wilt configureren, kun je de onderstaande omgevingsvariabelen instellen. Wanneer ze zijn ingesteld, overschrijven ze de waarden uit het admin-paneel en worden de bijbehorende velden in het E-mailformulier uitgeschakeld.

```yaml
environment:
  # ...andere variabelen
  - SMTP_HOST=smtp.sendgrid.net
  - SMTP_PORT=587
  - SMTP_USER=apikey
  - SMTP_PASSWORD=jouw-smtp-wachtwoord
```

| Variable        | Beschrijving                                                                                              |
| --------------- | --------------------------------------------------------------------------------------------------------- |
| `SMTP_HOST`     | Hostnaam van de mailserver (bijv. `smtp.sendgrid.net`).                                                   |
| `SMTP_PORT`     | Poort van de mailserver. Meestal `587` voor TLS of `465` voor SSL. Moet een geheel getal in `[1, 65535]` zijn. |
| `SMTP_USER`     | SMTP-inlognaam.                                                                                           |
| `SMTP_PASSWORD` | SMTP-inlogwachtwoord. Wordt als platte tekst in de omgeving opgeslagen — gaat niet door de versleutelingslaag van de database. |

Elke variabele is onafhankelijk. Als alleen `SMTP_HOST` is ingesteld, blijven de andere drie SMTP-velden bewerkbaar in de admin-interface en worden ze uit de database gelezen.

### Optioneel: S3-asset-opslag

Standaard worden geuploade afbeeldingen en andere assets lokaal op schijf opgeslagen onder `data/files/`. Om uploads naar een S3-compatibele bucket te sturen en vanaf een CDN te serveren, zet je de opslagmodus op `s3` en configureer je de bucket-credentials.

```yaml
environment:
  # ...andere 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
```

| Variabele                     | Beschrijving                                                                                                                                                  |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ASSETS_STORAGE_MODE`         | `local` (standaard) of `s3`. Bij `s3` zijn de zes `ASSETS_S3_*`-variabelen en `ASSETS_PUBLIC_BASE_URL` verplicht.                                              |
| `ASSETS_S3_ENDPOINT`          | S3-API-endpoint (bijv. `https://s3.amazonaws.com`, of de URL van je provider voor Cloudflare R2, Backblaze B2, MinIO, enz.).                                  |
| `ASSETS_S3_REGION`            | Bucket-regio (bijv. `us-east-1`, `auto` voor R2).                                                                                                             |
| `ASSETS_S3_BUCKET`            | Bucketnaam.                                                                                                                                                   |
| `ASSETS_S3_ACCESS_KEY_ID`     | Access key met lees-/schrijfrechten op de bucket.                                                                                                             |
| `ASSETS_S3_SECRET_ACCESS_KEY` | Secret access key.                                                                                                                                            |
| `ASSETS_PUBLIC_BASE_URL`      | Publieke base-URL die de storefront gebruikt om assets te laden (bijv. je CDN-domein dat naar de bucket wijst).                                               |
| `ASSETS_MAX_BYTES`            | Optionele cumulatieve opslaglimiet over alle bestanden. Accepteert `10gb`, `500mb` of een ruwe byte-telling. Geldt zowel in `local`- als in `s3`-modus. Limieten per upload (5 MB afbeelding / 50 MB video) blijven ongewijzigd. |

Bij het migreren van een bestaande winkel van `local` naar `s3` voer je het meegeleverde migratiescript een keer uit binnen een uitgerolde container om de bestaande bestanden naar de bucket te uploaden en de WebP-varianten voor te genereren:

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

Het script is idempotent — het opnieuw uitvoeren is veilig en het slaat werk over dat al gedaan is. Als je bestaande originelen op de host staan (bijvoorbeeld `/docker/<store>/app_data/files/`), bind-mount dat pad dan op `/app/data/files` binnen de container zodat het script ze kan oppakken; anders valt het terug op wat al in de bucket staat.

## 2. De Applicatie Starten

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

Dit start twee services:

- **postgres** — PostgreSQL 17 met pgvector (`pgvector/pgvector:pg17`), luisterend op poort `5432`.
- **app** — De applicatiecontainer (`capyshop/capyshop:latest`), beschikbaar op poort `3000`.

Bij het opstarten voert de applicatiecontainer automatisch databasemigraties uit (`prisma migrate deploy`) voordat de server wordt gestart.

## 3. Verifieren

```bash
# Controleer of beide containers draaien
docker compose ps

# Bekijk applicatielogs
docker compose logs app
```

De applicatie zou bereikbaar moeten zijn op `http://<jouw-host>:3000`.

## Reverse Proxy

Voor productie-deployments met HTTPS, plaats een reverse proxy (zoals [Traefik](https://traefik.io/), [Caddy](https://caddyserver.com/) of nginx) voor de applicatie. De reverse proxy verzorgt TLS-terminatie en stuurt verkeer door naar poort `3000`.

### Gzip-compressie Inschakelen met Traefik

Als je Traefik als reverse proxy gebruikt, schakel dan gzip-compressie in om de grootte van responses te verkleinen en de laadsnelheid te verbeteren — een factor in de ranking van zoekmachines. Voeg de volgende labels toe aan de `app`-service in je `docker-compose.yml`:

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

Vervang `STORE_NAME` door de naam van de Traefik-router van je winkel. Het eerste label definieert een compress-middleware die gzip toepast op alle responses groter dan 256 bytes. Het tweede label koppelt die middleware aan de router van je winkel.

## Volumes

| Volume / Mount     | Doel                                                              |
| ------------------ | ----------------------------------------------------------------- |
| `postgres_data`    | Bewaart PostgreSQL-gegevens bij het herstarten van containers.    |
| `./data:/app/data` | Bewaart geuploade bestanden en applicatiegegevens bij herstarten. |

## Bouwen vanuit Broncode

Als je de image liever zelf bouwt in plaats van de voorgebouwde versie te gebruiken:

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

De Dockerfile gebruikt een multi-stage build:

1. Installeert alle afhankelijkheden en bouwt de applicatie.
2. Kopieert alleen productie-afhankelijkheden en de build-uitvoer naar de uiteindelijke image.
3. Voert `prisma migrate deploy` uit en start vervolgens de server op poort `3000`.

Om je eigen image te gebruiken, werk het `image`-veld bij in je `docker-compose.yml`, of draai standalone:

```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
```

## Probleemoplossing

| Fout                                | Oorzaak                                                | Oplossing                                                                                                                             |
| ----------------------------------- | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
| `app` stopt direct                  | Ontbrekende of ongeldige omgevingsvariabelen           | Controleer `docker compose logs app` en verifieer alle vereiste omgevingsvariabelen                                                   |
| `ECONNREFUSED` naar postgres        | App gestart voordat de database gereed was             | Herstart de app: `docker compose restart app`                                                                                         |
| `P3009 - failed migrations`         | Een eerdere migratie heeft een vervuilde staat gelaten | `DROP TABLE IF EXISTS _prisma_migrations CASCADE;` in de database, herstart daarna                                                    |
| `type "vector" does not exist`      | pgvector-extensie niet aangemaakt                      | De `pgvector/pgvector:pg17`-image bevat deze, maar voer `CREATE EXTENSION IF NOT EXISTS vector;` uit bij gebruik van een andere image |
| Poort `3000` al in gebruik          | Een ander proces gebruikt de poort                     | Stop het conflicterende proces of wijzig de poortmapping in `docker-compose.yml`                                                      |
| Geuploade bestanden weg na herstart | `./data`-volume niet gekoppeld                         | Zorg ervoor dat de `volumes`-sectie `./data:/app/data` bevat                                                                          |
