From b120e5df45431ecc73a59585f54ba658f1031b24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=B6rdle=20Bot?= Date: Mon, 1 Dec 2025 15:19:19 +0100 Subject: [PATCH] chore: update .gitignore to include .env.example and enhance deployment documentation with Caddy reverse proxy setup --- .env.example | 106 ++++++++++++++++ .gitignore | 1 + CADDY_SETUP.md | 259 +++++++++++++++++++++++++++++++++++++++ Caddyfile | 127 +++++++++++++++++++ DEPLOYMENT.md | 32 +++++ Dockerfile.caddy | 10 ++ docker-compose.caddy.yml | 61 +++++++++ 7 files changed, 596 insertions(+) create mode 100644 .env.example create mode 100644 CADDY_SETUP.md create mode 100644 Caddyfile create mode 100644 Dockerfile.caddy create mode 100644 docker-compose.caddy.yml diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..d813037 --- /dev/null +++ b/.env.example @@ -0,0 +1,106 @@ +# ============================================ +# Hördle Environment Variables +# ============================================ +# Kopiere diese Datei zu .env und passe die Werte an deine Umgebung an: +# cp .env.example .env +# +# WICHTIG: Die .env-Datei sollte niemals in Git committed werden! + +# ============================================ +# Build-Time Variables (NEXT_PUBLIC_*) +# ============================================ +# Diese Variablen werden beim Build-Zeitpunkt in die Next.js-App eingebettet. +# Nach dem Build können sie nicht mehr geändert werden (ohne Rebuild). + +# App-Name (wird in Browser-Tab, PWA, etc. verwendet) +NEXT_PUBLIC_APP_NAME=Hördle + +# App-Beschreibung (für SEO, PWA, etc.) +NEXT_PUBLIC_APP_DESCRIPTION=Daily music guessing game - Guess the song from short audio clips + +# Hauptdomain (ohne https://) +NEXT_PUBLIC_DOMAIN=hoerdle.de + +# Twitter/X Handle (für Meta-Tags) +NEXT_PUBLIC_TWITTER_HANDLE=@hoerdle + +# Plausible Analytics - Domain +NEXT_PUBLIC_PLAUSIBLE_DOMAIN=hoerdle.de + +# Plausible Analytics - Script-URL (selbst gehostet oder extern) +NEXT_PUBLIC_PLAUSIBLE_SCRIPT_SRC=https://plausible.example.com/js/script.js + +# Theme-Farbe (für Browser-UI, PWA, etc.) +NEXT_PUBLIC_THEME_COLOR=#000000 + +# Hintergrundfarbe (für PWA, etc.) +NEXT_PUBLIC_BACKGROUND_COLOR=#ffffff + +# Credits im Footer aktivieren (true/false) +NEXT_PUBLIC_CREDITS_ENABLED=true + +# Credits-Text (vor dem Link) +NEXT_PUBLIC_CREDITS_TEXT=Vibe coded with ☕ and 🍺 by + +# Credits-Link-Text +NEXT_PUBLIC_CREDITS_LINK_TEXT=@yourhandle@server.social + +# Credits-Link-URL +NEXT_PUBLIC_CREDITS_LINK_URL=https://server.social/@yourhandle + +# ============================================ +# Runtime Variables +# ============================================ +# Diese Variablen können zur Laufzeit geändert werden (benötigen keinen Rebuild). + +# Datenbank-URL (SQLite für lokale/kleine Deployments) +# Format: file:/path/to/database.db +DATABASE_URL=file:/app/data/prod.db + +# Admin-Passwort (bcrypt Hash) +# Generiere einen Hash mit: node scripts/hash-password.js dein_passwort +# In docker-compose.yml müssen $ als $$ escaped werden! +ADMIN_PASSWORD=$2b$10$SHOt9G1qUNIvHoWre7499.eEtp5PtOII0daOQGNV.dhDEuPmOUdsq + +# Zeitzone (für tägliche Puzzle-Rotation) +TZ=Europe/Berlin + +# ============================================ +# Optional: Gotify Integration +# ============================================ +# Für Benachrichtigungen (z.B. Fehler-Alerts) + +# Gotify Server URL +GOTIFY_URL=https://gotify.example.com + +# Gotify App Token +GOTIFY_APP_TOKEN=your_gotify_app_token_here + +# ============================================ +# Optional: OpenRouter Integration +# ============================================ +# Für AI-Features (falls vorhanden) + +# OpenRouter API Key +OPENROUTER_API_KEY=your_openrouter_api_key_here + +# ============================================ +# Caddy Reverse Proxy (Optional - Production) +# ============================================ +# Nur benötigt, wenn Caddy für SSL/TLS verwendet wird. + +# GoDaddy API Key (für DNS-01 Challenge bei Wildcard-Zertifikaten) +# Siehe CADDY_SETUP.md für Anleitung zur Erstellung +GODADDY_API_KEY=your_godaddy_api_key_here + +# GoDaddy API Secret +GODADDY_API_SECRET=your_godaddy_api_secret_here + +# Email für Let's Encrypt Benachrichtigungen (optional) +CADDY_EMAIL=admin@hoerdle.de + +# ============================================ +# Build-Time Overrides +# ============================================ +# Optional: Spezifische Version beim Build setzen +# APP_VERSION=v1.0.0 diff --git a/.gitignore b/.gitignore index 33fb187..9684d66 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,7 @@ yarn-error.log* # env files (can opt-in for committing if needed) .env* +!.env.example # vercel .vercel diff --git a/CADDY_SETUP.md b/CADDY_SETUP.md new file mode 100644 index 0000000..119668c --- /dev/null +++ b/CADDY_SETUP.md @@ -0,0 +1,259 @@ +# Caddy-Setup für Hördle + +Diese Anleitung erklärt, wie du Caddy als Reverse-Proxy mit automatischen Let's Encrypt Wildcard-Zertifikaten für die Domains `hoerdle.de` und `hördle.de` (xn--hrdle-jua.de) einrichtest. + +## Übersicht + +Caddy übernimmt folgende Aufgaben: +- Automatische SSL/TLS-Zertifikate via Let's Encrypt +- Wildcard-Zertifikate für beide Domains (inkl. Subdomains) +- Reverse Proxy zu deinem Hördle-Container +- HTTP zu HTTPS Redirect +- Optimierte Einstellungen für Audio-Streaming und Uploads + +## Voraussetzungen + +1. Docker und Docker Compose installiert +2. Zugriff auf deine GoDaddy Domain-Verwaltung +3. Ports 80 und 443 müssen frei sein (Caddy übernimmt diese) + +## Schritt 1: GoDaddy DNS-API-Zugangsdaten erstellen + +Für Wildcard-Zertifikate benötigt Caddy DNS-01 Challenge, was API-Zugriff auf dein GoDaddy-Konto erfordert. + +### GoDaddy API-Keys erstellen + +1. Gehe zu [GoDaddy Developer Portal](https://developer.godaddy.com/) +2. Melde dich mit deinem GoDaddy-Konto an +3. Klicke auf **"Keys"** in der Navigation +4. Klicke auf **"Create New API Key"** +5. Fülle das Formular aus: + - **Key Name**: z.B. "Hördle Caddy DNS" + - **Environment**: Production (für echte Domains) +6. Klicke auf **"Create"** +7. **Wichtig**: Kopiere dir den **API Key** und das **API Secret** - das Secret wird nur einmal angezeigt! + +### Alternative: Manuelle DNS-TXT-Records (ohne API) + +Wenn du keine API-Keys verwenden möchtest, kannst du die DNS-TXT-Records manuell setzen. **Hinweis**: Dies ist nur für die initiale Zertifikatsanfrage möglich, nicht für automatische Erneuerungen. + +Siehe Abschnitt "Manuelle DNS-Konfiguration (ohne API)" weiter unten. + +## Schritt 2: Environment-Variablen konfigurieren + +Erstelle eine `.env`-Datei im Projektverzeichnis (oder erweitere die bestehende): + +```bash +# GoDaddy API-Credentials für DNS-01 Challenge +GODADDY_API_KEY=your_api_key_here +GODADDY_API_SECRET=your_api_secret_here + +# Optional: Email für Let's Encrypt Benachrichtigungen +CADDY_EMAIL=markus@hoerdle.de +``` + +**Wichtig**: Die `.env`-Datei sollte nicht in Git committed werden (sollte bereits in `.gitignore` sein). + +## Schritt 3: Docker-Netzwerk erstellen + +Caddy und Hördle müssen im gleichen Docker-Netzwerk kommunizieren: + +```bash +# Prüfe, ob das Netzwerk bereits existiert +docker network ls | grep hoerdle + +# Falls nicht vorhanden, erstelle es +docker network create hoerdle_default +``` + +## Schritt 4: Caddy starten + +### Option A: Mit docker-compose (Empfohlen) + +```bash +# Starte Hördle + Caddy zusammen +docker compose -f docker-compose.yml -f docker-compose.caddy.yml --profile production up -d + +# Nur Caddy starten (wenn Hördle bereits läuft) +docker compose -f docker-compose.caddy.yml --profile production up -d +``` + +### Option B: Nur Caddy starten (Hördle läuft bereits) + +```bash +docker compose -f docker-compose.caddy.yml --profile production up -d +``` + +## Schritt 5: DNS-Konfiguration in GoDaddy + +### Automatisch (mit API-Keys) + +Wenn du API-Keys konfiguriert hast, wird Caddy automatisch die benötigten DNS-TXT-Records erstellen. Keine manuellen DNS-Änderungen nötig! + +### Manuell (ohne API-Keys) + +Wenn du die API-Keys nicht verwenden möchtest, musst du die DNS-TXT-Records manuell setzen: + +#### Für hoerdle.de: + +1. Gehe zu deinem [GoDaddy DNS-Verwaltung](https://dcc.godaddy.com/manage/YOUR_DOMAIN/dns) +2. Für jedes Wildcard-Zertifikat benötigst du einen TXT-Record: + - **Typ**: TXT + - **Name**: `_acme-challenge` + - **Wert**: (wird von Let's Encrypt generiert - siehe Caddy-Logs) + - **TTL**: 600 (10 Minuten) + +**Wichtig**: Für Wildcard-Zertifikate brauchst du: +- Einen TXT-Record für `_acme-challenge.hoerdle.de` (Domain selbst) +- Einen TXT-Record für `_acme-challenge.*.hoerdle.de` (Wildcard) + +#### Für hördle.de (xn--hrdle-jua.de): + +Das gleiche Vorgehen für die Punycode-Domain: +- `_acme-challenge.xn--hrdle-jua.de` +- `_acme-challenge.*.xn--hrdle-jua.de` + +**Hinweis**: Die manuelle Methode funktioniert nur für die initiale Zertifikatsanfrage. Für automatische Erneuerungen benötigst du die API-Keys. + +## Schritt 6: Prüfen, ob alles funktioniert + +### Caddy-Logs ansehen + +```bash +docker logs -f hoerdle-caddy +``` + +Du solltest sehen: +- Caddy startet erfolgreich +- Let's Encrypt-Zertifikate werden angefordert +- Zertifikate sind gültig + +### Zertifikate prüfen + +```bash +# Prüfe Zertifikate im Browser +# Öffne: https://hoerdle.de +# Öffne: https://hördle.de +``` + +Oder via Command-Line: + +```bash +# Prüfe Zertifikat für hoerdle.de +openssl s_client -connect hoerdle.de:443 -servername hoerdle.de < /dev/null 2>/dev/null | openssl x509 -noout -text | grep "Subject:" + +# Prüfe Zertifikat für hördle.de +openssl s_client -connect xn--hrdle-jua.de:443 -servername xn--hrdle-jua.de < /dev/null 2>/dev/null | openssl x509 -noout -text | grep "Subject:" +``` + +## Troubleshooting + +### Caddy startet nicht + +**Problem**: Container stoppt sofort nach Start. + +**Lösung**: +1. Prüfe Caddy-Logs: `docker logs hoerdle-caddy` +2. Prüfe Caddyfile-Syntax: `docker run --rm -v $(pwd)/Caddyfile:/etc/caddy/Caddyfile:ro caddy:2-alpine caddy validate --config /etc/caddy/Caddyfile` +3. Prüfe, ob Ports 80/443 frei sind: `sudo netstat -tlnp | grep -E ':80|:443'` + +### Zertifikate werden nicht erstellt + +**Problem**: Let's Encrypt-Zertifikate werden nicht angefordert. + +**Lösung**: +1. Prüfe GoDaddy API-Credentials in `.env` +2. Prüfe Caddy-Logs für DNS-Challenge-Fehler +3. Stelle sicher, dass die Domains korrekt auf deinen Server zeigen (A-Records) +4. Bei manueller DNS-Konfiguration: Prüfe, ob TXT-Records korrekt gesetzt sind + +### DNS-Challenge schlägt fehl + +**Problem**: DNS-01 Challenge kann DNS-Records nicht erstellen. + +**Lösung**: +1. Prüfe GoDaddy API-Permissions +2. Stelle sicher, dass API-Keys Production-Keys sind (nicht Development) +3. Prüfe Domain-Ownership in GoDaddy +4. Warte einige Minuten - DNS-Propagierung kann dauern + +### Audio-Dateien funktionieren nicht + +**Problem**: MP3-Dateien werden nicht korrekt gestreamt. + +**Lösung**: +1. Prüfe Caddy-Logs: `docker logs hoerdle-caddy | grep -i range` +2. Prüfe, ob Range-Header weitergegeben werden (Browser DevTools → Network) +3. Stelle sicher, dass der `/uploads/` Handle korrekt konfiguriert ist + +### Container können nicht kommunizieren + +**Problem**: Caddy kann den hoerdle-Container nicht erreichen. + +**Lösung**: +1. Prüfe, ob beide Container im gleichen Netzwerk sind: + ```bash + docker network inspect hoerdle_default + ``` +2. Prüfe, ob hoerdle-Container läuft: `docker ps | grep hoerdle` +3. Teste Verbindung von Caddy zu Hördle: + ```bash + docker exec hoerdle-caddy wget -O- http://hoerdle:3010/api/health + ``` + +## Deployment-Workflow + +### Caddy nur in Produktion aktivieren + +Die `docker-compose.caddy.yml` verwendet das `production`-Profile. Um Caddy zu aktivieren: + +```bash +# Mit Production-Profile +docker compose -f docker-compose.yml -f docker-compose.caddy.yml --profile production up -d + +# Ohne Caddy (nur Hördle) +docker compose -f docker-compose.yml up -d +``` + +### Caddy aktualisieren + +```bash +# Pull neues Caddy-Image +docker compose -f docker-compose.caddy.yml pull + +# Restart Caddy-Container +docker compose -f docker-compose.caddy.yml --profile production restart caddy +``` + +### Caddy-Konfiguration ändern + +Nach Änderungen am Caddyfile: + +```bash +# Caddyfile validieren +docker run --rm -v $(pwd)/Caddyfile:/etc/caddy/Caddyfile:ro caddy:2-alpine caddy validate --config /etc/caddy/Caddyfile + +# Caddy neu laden (ohne Downtime) +docker compose -f docker-compose.caddy.yml --profile production exec caddy caddy reload --config /etc/caddy/Caddyfile +``` + +## Sicherheit + +### API-Keys schützen + +- **Niemals** API-Keys in Git committen +- Verwende `.env`-Dateien (sollten in `.gitignore` sein) +- Setze minimale Berechtigungen für API-Keys in GoDaddy +- Rotiere API-Keys regelmäßig + +### Firewall + +Stelle sicher, dass nur Ports 80 und 443 öffentlich erreichbar sind. Port 3010 (Hördle) sollte nicht öffentlich erreichbar sein. + +## Weitere Ressourcen + +- [Caddy Dokumentation](https://caddyserver.com/docs/) +- [Caddy DNS-Provider](https://caddyserver.com/docs/modules/tls.dns) +- [GoDaddy API Dokumentation](https://developer.godaddy.com/doc/endpoint/domains) +- [Let's Encrypt Wildcard-Zertifikate](https://letsencrypt.org/docs/challenge-types/#dns-01-challenge) + diff --git a/Caddyfile b/Caddyfile new file mode 100644 index 0000000..319451f --- /dev/null +++ b/Caddyfile @@ -0,0 +1,127 @@ +# Caddy-Konfiguration für Hördle +# Wildcard-Zertifikate für hoerdle.de und hördle.de (xn--hrdle-jua.de) + +# DNS-01 Challenge für Wildcard-Zertifikate (GoDaddy) +# Die DNS-TXT-Records müssen manuell in GoDaddy gesetzt werden (siehe CADDY_SETUP.md) + +# Domain 1: hoerdle.de (ASCII) +*.hoerdle.de, hoerdle.de { + # TLS mit DNS-01 Challenge für Wildcard + tls { + dns godaddy {env.GODADDY_API_KEY} {env.GODADDY_API_SECRET} + } + + # Upload-Limit: 50MB (wie in nginx.conf.example) + request_body { + max_size 50MB + } + + # Reverse Proxy zu hoerdle Container + reverse_proxy hoerdle:3010 { + # HTTP/1.1 für WebSocket Support + transport http { + versions 1.1 + } + + # Headers für Audio-Streaming (Range Requests) + header_up Range {http.request.header.Range} + header_up If-Range {http.request.header.If-Range} + + # Kein Caching bei Range Requests + header_down Cache-Control "no-cache" { + when {http.request.header.Range} + } + + # Timeouts für große Dateien (300s wie in nginx) + timeout 300s + + # WebSocket Support + header_up Connection {>Connection} + header_up Upgrade {>Upgrade} + } + + # Spezielle Behandlung für /uploads/ Route (Audio-Dateien) + handle /uploads/* { + reverse_proxy hoerdle:3010 { + # Range Requests für Audio-Streaming + header_up Range {http.request.header.Range} + header_up If-Range {http.request.header.If-Range} + + # Buffering deaktivieren für große Dateien + # (Caddy hat kein explizites proxy_buffering, aber Timeouts setzen) + timeout 300s + + # Kein Caching bei Range Requests + header_down Cache-Control "no-cache" { + when {http.request.header.Range} + } + } + } + + # HTTP zu HTTPS Redirect + @http { + protocol http + } + redir @http https://{host}{uri} permanent +} + +# Domain 2: hördle.de (Punycode: xn--hrdle-jua.de) +*.xn--hrdle-jua.de, xn--hrdle-jua.de { + # TLS mit DNS-01 Challenge für Wildcard + tls { + dns godaddy {env.GODADDY_API_KEY} {env.GODADDY_API_SECRET} + } + + # Upload-Limit: 50MB + request_body { + max_size 50MB + } + + # Reverse Proxy zu hoerdle Container + reverse_proxy hoerdle:3010 { + # HTTP/1.1 für WebSocket Support + transport http { + versions 1.1 + } + + # Headers für Audio-Streaming (Range Requests) + header_up Range {http.request.header.Range} + header_up If-Range {http.request.header.If-Range} + + # Kein Caching bei Range Requests + header_down Cache-Control "no-cache" { + when {http.request.header.Range} + } + + # Timeouts für große Dateien (300s) + timeout 300s + + # WebSocket Support + header_up Connection {>Connection} + header_up Upgrade {>Upgrade} + } + + # Spezielle Behandlung für /uploads/ Route (Audio-Dateien) + handle /uploads/* { + reverse_proxy hoerdle:3010 { + # Range Requests für Audio-Streaming + header_up Range {http.request.header.Range} + header_up If-Range {http.request.header.If-Range} + + # Timeouts für große Dateien + timeout 300s + + # Kein Caching bei Range Requests + header_down Cache-Control "no-cache" { + when {http.request.header.Range} + } + } + } + + # HTTP zu HTTPS Redirect + @http { + protocol http + } + redir @http https://{host}{uri} permanent +} + diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index a5ed499..3e088e0 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -82,3 +82,35 @@ docker ps ``` Look for the "healthy" status in the STATUS column. + +## Caddy Reverse Proxy (Optional - Production) + +For production deployments with automatic SSL/TLS certificates, Caddy can be used as a reverse proxy. Caddy provides: + +- Automatic Let's Encrypt certificates (including wildcard certificates) +- HTTP to HTTPS redirect +- Optimized settings for audio streaming and file uploads +- Support for both `hoerdle.de` and `hördle.de` (Punycode: `xn--hrdle-jua.de`) + +### Quick Start + +1. **Follow the setup guide**: See `CADDY_SETUP.md` for detailed instructions +2. **Configure environment variables**: Add GoDaddy API credentials to your `.env` file +3. **Start with Caddy**: + ```bash + docker compose -f docker-compose.yml -f docker-compose.caddy.yml --profile production up -d + ``` + +### Without Caddy + +If you don't want to use Caddy, you can deploy normally: + +```bash +docker compose -f docker-compose.yml up -d +``` + +The application will still be accessible on port 3010, but you'll need to configure SSL/TLS separately (e.g., with nginx). + +### Caddy Troubleshooting + +See `CADDY_SETUP.md` for detailed troubleshooting information. diff --git a/Dockerfile.caddy b/Dockerfile.caddy new file mode 100644 index 0000000..f95c173 --- /dev/null +++ b/Dockerfile.caddy @@ -0,0 +1,10 @@ +# Dockerfile für Caddy mit GoDaddy DNS-Provider Plugin +FROM caddy:2-builder AS builder + +RUN xcaddy build \ + --with github.com/caddy-dns/godaddy + +FROM caddy:2 + +COPY --from=builder /usr/bin/caddy /usr/bin/caddy + diff --git a/docker-compose.caddy.yml b/docker-compose.caddy.yml new file mode 100644 index 0000000..3cc52fd --- /dev/null +++ b/docker-compose.caddy.yml @@ -0,0 +1,61 @@ +# Docker Compose Konfiguration für Caddy Reverse Proxy +# Optional: Nur in Produktionsumgebung verwenden +# +# Starten: docker compose -f docker-compose.yml -f docker-compose.caddy.yml up -d +# Stoppen: docker compose -f docker-compose.yml -f docker-compose.caddy.yml down + +version: '3.8' + +services: + caddy: + # Verwende Custom-Image mit GoDaddy DNS-Plugin + build: + context: . + dockerfile: Dockerfile.caddy + # Alternativ: Verwende Standard-Caddy und manuelle DNS-Konfiguration + # image: caddy:2-alpine + container_name: hoerdle-caddy + restart: unless-stopped + ports: + # Standard HTTP/HTTPS Ports + - "80:80" + - "443:443" + - "443:443/udp" # Für HTTP/3 (QUIC) + environment: + # GoDaddy API-Credentials für DNS-01 Challenge + # Diese müssen in einer .env-Datei gesetzt werden: + # GODADDY_API_KEY=your_api_key + # GODADDY_API_SECRET=your_api_secret + - GODADDY_API_KEY=${GODADDY_API_KEY:-} + - GODADDY_API_SECRET=${GODADDY_API_SECRET:-} + # Optional: Email für Let's Encrypt Benachrichtigungen + - CADDY_EMAIL=${CADDY_EMAIL:-} + volumes: + # Caddyfile-Konfiguration + - ./Caddyfile:/etc/caddy/Caddyfile:ro + # Persistente Zertifikat-Speicherung + - caddy_data:/data + - caddy_config:/config + networks: + - default + # Health Check + healthcheck: + test: ["CMD", "caddy", "version"] + interval: 30s + timeout: 10s + retries: 3 + # Nur starten, wenn ENABLE_CADDY=true gesetzt ist + profiles: + - production + +volumes: + caddy_data: + driver: local + caddy_config: + driver: local + +networks: + default: + # Netzwerk wird automatisch erstellt wenn beide Compose-Dateien zusammen verwendet werden + # Bei separater Verwendung: docker network create hoerdle_default und external: true setzen +