# Serienbrief Django-Webanwendung zur Erzeugung von Serienbriefen aus DOCX-Vorlagen und CSV-Empfängerlisten. Läuft als Compose-Stack, HTTP-only — TLS terminiert ein **vorgelagerter Nginx-Reverse-Proxy** außerhalb dieses Stacks. ## Architektur ``` LAN-Client ──HTTPS──▶ Externer Nginx-Proxy ──HTTP──▶ App-Stack (dieses Repo) (TLS-Terminierung) │ ├─ nginx (intern, :8080) ├─ web (Gunicorn / Django) ├─ worker (Celery) ├─ beat (Celery Scheduler) ├─ db (PostgreSQL 16) ├─ redis (Celery Broker) └─ backup (nightly pg_dump) ``` Der externe Proxy muss diese Header setzen: - `X-Forwarded-Proto: https` - `X-Forwarded-For: ` - `Host: ` ## Verzeichnisstruktur ``` serienbrief/ ├── docker-compose.yml # Prod-Stack ├── docker-compose.override.yml # Dev-Overrides (Auto-Merge) ├── .env.example # Konfig-Template ├── .devcontainer/ # VS Code Dev-Container ├── .vscode/ # Launch, Tasks, Settings ├── nginx/ # App-interner Nginx (HTTP) ├── app/ # Django-Projekt │ ├── Dockerfile # Multi-Stage: builder/dev/runtime │ ├── requirements.txt │ ├── requirements-dev.txt │ ├── pyproject.toml # Ruff & Pytest │ ├── manage.py │ ├── config/ # Settings, URLs, WSGI │ └── mailmerge/ # Django-App: Models, Views, Tasks ├── postgres/init/ # SQL-Init-Scripts (optional) ├── secrets/ # Passwort-Files (chmod 600) └── backups/ # DB- & Media-Dumps ``` ## Erstinbetriebnahme (Prod) ```bash cp .env.example .env nano .env # Werte setzen mkdir -p secrets openssl rand -base64 32 > secrets/postgres_password.txt chmod 600 secrets/postgres_password.txt docker compose -f docker-compose.yml build docker compose -f docker-compose.yml up -d docker compose exec web python manage.py createsuperuser ``` Der externe Proxy zeigt dann z.B. so auf den Stack: ```nginx location / { proxy_pass http://app-host:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto https; } ``` ## Entwicklung in VS Code ### Variante A — Dev-Container (empfohlen) 1. VS Code öffnet das Projekt 2. Extension **Dev Containers** installiert 3. Command Palette → `Dev Containers: Reopen in Container` 4. VS Code startet Compose mit Override (Build-Target `dev`, Code-Mount, Hot-Reload) 5. Terminal im Container: `python manage.py migrate` 6. Browser: `http://localhost:8000` ### Variante B — Lokal mit Compose im Hintergrund ```bash docker compose up -d # Override wird automatisch geladen docker compose logs -f web ``` Code-Änderungen sind sofort wirksam (Bind-Mount + `runserver` mit Auto-Reload). ### Debugging Im Container läuft `debugpy` auf Port **5678**. In VS Code: - Run & Debug → **Django: attach (debugpy in Container)** - Breakpoints überall im Code setzen Alternativ Task **django: start debugpy** ausführen und attachen. ### Häufige VS-Code-Tasks `Strg+Shift+P` → `Tasks: Run Task`: | Task | Wirkung | |---|---| | compose: up | Stack starten | | compose: logs web | Live-Logs | | django: makemigrations | Migrations erzeugen | | django: migrate | Migrations anwenden | | django: shell | Django-Shell | | django: createsuperuser | Admin-User anlegen | | tests: pytest | Tests laufen lassen | | lint: ruff | Linting | ## Härtungs-Hinweise - Externe Proxy-Konfiguration (TLS, HSTS, CSP, Rate-Limit) **muss** vorhanden sein - `APP_BIND_IP=127.0.0.1` in `.env` außer der externe Proxy läuft auf anderem Host - `DJANGO_ALLOWED_HOSTS` und `CSRF_TRUSTED_ORIGINS` strikt setzen - `SECURE_PROXY_SSL_HEADER` ist gesetzt — damit erkennt Django korrekt, dass der Client über HTTPS kam - Postgres und Redis sind **nicht** nach außen exponiert (nur im backend-Netz) - Container laufen non-root, `read_only`, mit `no-new-privileges` - Backups regelmäßig auf zweites System spiegeln (Borg/Restic)