feat: add production docker-compose configuration and deployment documentation for Portainer integration

This commit is contained in:
RaymondVerhoef
2026-05-11 20:30:54 +02:00
parent 2597dc751a
commit 8192422263
2 changed files with 170 additions and 0 deletions

133
DEPLOYMENT_GUIDE.md Normal file
View File

@@ -0,0 +1,133 @@
# 🎯 Overdrachtsdocument Productie-deployment van **Respellion Learning Platform** met Portainer
> **Doel** Dit document geeft een *complete* handleiding zodat een ontwikkelaar of AI-agent de Respellion Learning Platform applicatie kan bouwen, configureren en in productie draaien op een Hetzner-VPS via Portainer.
---
## 1. Project-structuur
```
learning-platform/
├─ Dockerfile # Productie Dockerfile (Nginx + React/Vite)
├─ nginx.conf # Nginx configuratie voor Anthropic API proxy
├─ docker-compose.prod.yml # Productie-compose configuratie (Caddy SSL + Frontend)
└─ src/ # React frontend broncode
```
> **Opmerking**: Deze applicatie is een 100% frontend applicatie ("Single Page Application"). De interne state wordt opgeslagen in `localStorage`. Er is **geen externe backend of database (zoals Postgres)** nodig. De enige netwerk calls gaan via de interne Nginx proxy direct naar de Anthropic API.
---
## 2. Kernbestanden inhoud & rationale
### 2.1 `docker-compose.prod.yml`
```yaml
services:
frontend:
build: .
restart: always
expose:
- "80"
caddy:
image: caddy:2-alpine
restart: always
ports:
- "80:80"
- "443:443"
environment:
- DOMAIN_NAME=${DOMAIN_NAME}
entrypoint:
- /bin/sh
- -c
- |
# Build Caddyfile on container start
CLEAN_DOMAIN=$(printf '%s' "$DOMAIN_NAME" | tr -d ';')
cat > /etc/caddy/Caddyfile <<EOF
${CLEAN_DOMAIN} {
reverse_proxy /* frontend:80
}
EOF
# Run Caddy with the generated file
caddy run --config /etc/caddy/Caddyfile --adapter caddyfile
volumes:
- caddy_data:/data
- caddy_config:/config
depends_on:
- frontend
volumes:
caddy_data:
caddy_config:
```
**Belangrijkste punten**
| Item | Waarom |
|------|--------|
| **`frontend` (Nginx)** | Bevat de React applicatie en proxy-configuratie voor Anthropic. Exposeert intern poort 80. |
| **`caddy` (Reverse Proxy)** | Ontvangt alle externe requests op poort 80 en 443 en haalt volautomatisch een SSL Let's Encrypt certificaat op voor `DOMAIN_NAME`. Stuurt verkeer door naar `frontend:80`. |
| **Volumes `caddy_data/config`** | Slaan de SSL certificaten persistent op zodat Caddy niet bij elke herstart de rate-limits van Let's Encrypt overschrijdt. |
---
### 2.2 `Dockerfile` (Productie)
De applicatie wordt in 2 fasen ("multi-stage") gebouwd:
1. **Node.js Build:** `npm install` en `npm run build` worden uitgevoerd om de statische Vite applicatie te compileren naar de `dist/` map.
2. **Nginx Serve:** Een lightweight Nginx Alpine image wordt gestart. Het pakt de `dist/` bestanden en past `nginx.conf` toe om API-aanroepen (CORS blokkades) te omzeilen door als proxy te fungeren.
---
## 3. Omgevingsvariabelen (te definiëren in Portainer)
| Variabele | Voorbeeld | Toelichting |
|-----------|-----------|-------------|
| `DOMAIN_NAME` | `leren.mijndomein.nl` | De domeinnaam die Caddy zal beveiligen met HTTPS. Zorg dat je DNS A-record naar je VPS-IP wijst. |
*(De Anthropic API key wordt ingevuld via de instellingen in de applicatie (Settings menu) door een admin, en veilig opgeslagen in localStorage van de gebruiker. Hij hoeft dus niet in Portainer te staan!)*
---
## 4. Deploy-procedure (stap-voor-stap)
### Portainer stack aanmaken / bijwerken
| Stap | Actie | Details |
|------|-------|---------|
| **1** | Navigeer naar **Stacks → + Add stack** | Geef een herkenbare naam (bijv. `respellion-platform`). |
| **2** | **Repository URL** invullen | `https://github.com/<gebruiker>/learning-platform.git` |
| **3** | **Compose path** = `docker-compose.prod.yml` | Zorg dat dit exact overeenkomt met de bestandsnaam in de repo. |
| **4** | **Environment variables** toevoegen | Voeg `DOMAIN_NAME` toe. |
| **5** | **Deploy the stack** | Portainer start de applicatie en Caddy haalt direct het SSL certificaat op (max. 60 sec). |
| **6** | **Controle** | Surf naar `https://jouw-domein.nl`. Je zou de Respellion inlogscherm moeten zien. |
### Handmatige Force-Rebuild
Wanneer de repo is geüpdatet en je wil de nieuwe versie afdwingen via command-line:
```bash
docker compose -f docker-compose.prod.yml up -d --build --no-cache
```
---
## 5. Veelvoorkomende valkuilen & Fixes
| Symptom | Oorzaak | Oplossing |
|---------|---------|-----------|
| **SSL Error / ERR_CONNECTION_REFUSED** | DNS is nog niet actief of poort 443 staat dicht in je firewall (Hetzner). | Check Hetzner Cloud Console en open TCP poort 80 & 443 in de Inbound Firewall. |
| **Anthropic API Werkt Niet** | Foute key of Nginx proxy mist configuratie. | Open de app, ga naar Admin -> Settings en vul je Anthropic Key in. Controleer ook of `nginx.conf` correct wordt ingeladen in Nginx. |
| **Witte pagina na deploy** | Oude Docker image wordt gebruikt. | Doe een force-rebuild (`--build --no-cache`) in Portainer of via de console. |
---
## 6. Checklist voor een nieuwe implementatie
- [ ] Repository up-to-date en gepusht naar `main`.
- [ ] `docker-compose.prod.yml` is gedefinieerd in Portainer.
- [ ] Environment variable `DOMAIN_NAME` is correct ingesteld.
- [ ] DNS A-Record staat op de juiste server.
- [ ] Na eerste inlog: Open "Admin" -> "Settings" en voeg de Anthropic API Key toe, klik op Opslaan.

37
docker-compose.prod.yml Normal file
View File

@@ -0,0 +1,37 @@
services:
frontend:
build: .
restart: always
expose:
- "80"
caddy:
image: caddy:2-alpine
restart: always
ports:
- "80:80"
- "443:443"
environment:
- DOMAIN_NAME=${DOMAIN_NAME}
entrypoint:
- /bin/sh
- -c
- |
# Build Caddyfile on container start
CLEAN_DOMAIN=$(printf '%s' "$DOMAIN_NAME" | tr -d ';')
cat > /etc/caddy/Caddyfile <<EOF
${CLEAN_DOMAIN} {
reverse_proxy /* frontend:80
}
EOF
# Run Caddy with the generated file
caddy run --config /etc/caddy/Caddyfile --adapter caddyfile
volumes:
- caddy_data:/data
- caddy_config:/config
depends_on:
- frontend
volumes:
caddy_data:
caddy_config: