Konfiguration
Die Anwendung wird über Umgebungsvariablen konfiguriert. Erstellen Sie eine .env-Datei im selben Verzeichnis wie die docker-compose.yml.
Pflichtangaben
| Variable | Beschreibung |
|---|---|
DJANGO_SECRET_KEY | Geheimer Schlüssel für Django (zufällig generieren) |
DJANGO_DB_HOST | Hostname der Datenbank (z.B. resource-planning-db) |
DJANGO_DB_NAME | Name der Datenbank |
DJANGO_DB_USER | Datenbank-Benutzer |
DJANGO_DB_PASSWORD | Datenbank-Passwort |
Anwendung
| Variable | Standard | Beschreibung |
|---|---|---|
DJANGO_DEBUG | False | Debug-Modus aktivieren (True/False) |
DJANGO_ALLOWED_HOSTS | localhost | Erlaubte Hostnamen (Leerzeichen-getrennt) |
DJANGO_USE_X_FORWARDED_HOST | False | X-Forwarded-Host Header verwenden (bei Reverse Proxy) |
DJANGO_NUM_PROXIES | 1 | Wie viele Proxys vor der Anwendung stehen. Entscheidet, welcher Eintrag aus X-Forwarded-For als Client gilt — und damit, gegen wen ein Rate Limit zählt. Ein nginx davor: 1; nginx hinter Cloudflare: 2; direkt erreichbar: 0. |
WEB_CONCURRENCY | 1 | Anzahl Uvicorn Worker-Prozesse |
E-Mail
Für Passwort-Reset und E-Mail-Änderungen muss ein SMTP-Server konfiguriert werden.
| Variable | Standard | Beschreibung |
|---|---|---|
DJANGO_EMAIL_HOST | localhost | SMTP-Server |
DJANGO_EMAIL_PORT | 25 | SMTP-Port |
DJANGO_EMAIL_HOST_USER | SMTP-Benutzer | |
DJANGO_EMAIL_HOST_PASSWORD | SMTP-Passwort | |
DJANGO_EMAIL_USE_TLS | False | TLS verwenden |
DJANGO_EMAIL_USE_SSL | False | SSL verwenden |
DJANGO_DEBUG_EMAIL | False | E-Mails auf die Konsole ausgeben statt senden |
Frontend-URLs
Diese URLs werden in E-Mail-Vorlagen verwendet (z.B. Passwort-Reset-Links).
| Variable | Beschreibung |
|---|---|
DJANGO_CONFIG_FRONTEND_URL_LOGIN | URL der Login-Seite |
DJANGO_CONFIG_FRONTEND_URL_PASSWORD_RESET | URL der Passwort-Reset-Seite |
DJANGO_CONFIG_FRONTEND_URL_EMAIL_CHANGE | URL der E-Mail-Änderungsseite |
JWT-Authentifizierung
| Variable | Standard | Beschreibung |
|---|---|---|
DJANGO_ACCESS_TOKEN_LIFETIME | 300 | Access-Token Gültigkeit in Sekunden (5 Min) |
DJANGO_REFRESH_TOKEN_LIFETIME | 1209600 | Refresh-Token Gültigkeit in Sekunden (14 Tage) |
DJANGO_ROTATE_REFRESH_TOKENS | True | Refresh-Token bei Erneuerung rotieren |
DJANGO_BLACKLIST_AFTER_ROTATION | True | Alten Refresh-Token nach Rotation sperren |
DJANGO_REFRESH_TOKEN_COOKIE_SECURE | False | Secure-Flag für Refresh-Token-Cookie (bei HTTPS: True) |
DJANGO_REFRESH_TOKEN_COOKIE_SAMESITE | Lax | SameSite-Attribut (Strict oder Lax) |
CSRF
| Variable | Standard | Beschreibung |
|---|---|---|
DJANGO_CSRF_COOKIE_SECURE | False | Secure-Flag für CSRF-Cookie (bei HTTPS: True) |
DJANGO_CSRF_COOKIE_SAMESITE | Lax | SameSite-Attribut |
DJANGO_CSRF_COOKIE_HTTPONLY | False | HttpOnly-Flag |
DJANGO_CSRF_TRUSTED_ORIGINS | http://localhost:8000 | Vertrauenswürdige Origins (Leerzeichen-getrennt) |
HTTPS
| Variable | Standard | Beschreibung |
|---|---|---|
DJANGO_SECURE_PROXY_SSL_HEADER | https | Der Wert von X-Forwarded-Proto, der eine verschlüsselte Anfrage bedeutet. |
DJANGO_SESSION_COOKIE_SECURE | False | Session-Cookie nur über HTTPS senden. Django-Admin und DRF-Session-Authentifizierung nutzen dieses Cookie — bei TLS einschalten. |
DJANGO_SECURE_HSTS_SECONDS | 0 | Dauer des Strict-Transport-Security-Headers in Sekunden; 0 schaltet ihn ab. Beispiel: 31536000 (ein Jahr). |
DJANGO_SECURE_PROXY_SSL_HEADER nicht auf http setzenDer Wert ist derjenige, der eine verschlüsselte Anfrage kennzeichnet. Steht dort http, gilt jede unverschlüsselte Anfrage als sicher und jede HTTPS-Anfrage als unsicher — genau verkehrt herum. Der Standard ist https; ändern Sie ihn nur, wenn Ihr Proxy nachweislich einen anderen Wert sendet.
Browser merken sich den Header für seine volle Dauer. Setzen Sie DJANGO_SECURE_HSTS_SECONDS erst, wenn die Anwendung dauerhaft über HTTPS erreichbar ist und bleiben soll.
Rate Limits
Die Anmeldung und das Zurücksetzen des Passworts sind mengenmäßig begrenzt. Ein leerer Wert schaltet das jeweilige Limit ab.
| Variable | Standard | Beschreibung |
|---|---|---|
DJANGO_THROTTLE_LOGIN | 120/min | Anmeldeversuche je Client-IP. Muss eine ganze Schule verkraften, die sich gleichzeitig anmeldet — nicht eine einzelne Person. |
DJANGO_THROTTLE_LOGIN_ACCOUNT | 10/min | Anmeldeversuche je Benutzername, unabhängig von der Adresse. Das Limit gegen das Durchprobieren eines Passworts. |
DJANGO_THROTTLE_PASSWORD_RESET | 20/hour | Anforderungen einer Passwort-Reset-Mail je Client-IP. |
Ein langes Fenster bei DJANGO_THROTTLE_LOGIN_ACCOUNT erlaubt es, ein Konto absichtlich auszusperren, indem sein Budget aufgebraucht wird. Der Standard von einer Minute ist bewusst kurz gewählt.
Die Limits werden im lokalen Cache gezählt. Mit mehreren Worker-Prozessen (WEB_CONCURRENCY über 1) zählt jeder für sich, das tatsächliche Limit ist also entsprechend höher. Für exakte Limits konfigurieren Sie einen gemeinsamen Cache.
DJANGO_NUM_PROXIES muss stimmenDie IP-basierten Limits zählen gegen die Adresse, die aus X-Forwarded-For ermittelt wird. Ist die Zahl der Proxys falsch, zählt entweder alles gegen den Proxy — dann sperrt ein Nutzer alle anderen aus — oder gegen eine vom Client frei wählbare Adresse, dann wirkt das Limit gar nicht.
CORS
| Variable | Standard | Beschreibung |
|---|---|---|
DJANGO_CORS_ALLOWED_ORIGINS | http://localhost:4000 | Erlaubte Origins (Leerzeichen-getrennt) |
DJANGO_CORS_ALLOW_CREDENTIALS | True | Credentials (Cookies) erlauben |
Internationalisierung
| Variable | Standard | Beschreibung |
|---|---|---|
DJANGO_LANGUAGE_CODE | de-de | Standardsprache |
DJANGO_TIME_ZONE | UTC | Zeitzone |
Timeouts
| Variable | Standard | Beschreibung |
|---|---|---|
DJANGO_CONFIG_USERTOKEN_EXPIRATION | 15 | Gültigkeit Benutzer-Token für Passwort-Reset/E-Mail-Änderung (Minuten) |
Schuljahreskalender
| Variable | Standard | Beschreibung |
|---|---|---|
DJANGO_SCHOOL_FINAL_GRADE | 3 | Höchstes Ausbildungsjahr der Schule. Wer darüber hinaus vorrücken würde, hat die Ausbildung beendet. |
DJANGO_ACADEMIC_YEAR_SEED | — | Nur für die einmalige Umstellung auf den Schuljahreskalender. JSON-Liste der historischen Schuljahre, siehe unten. |
DJANGO_ACADEMIC_YEAR_SEED
Bei der Migration auf den datumsbasierten Schuljahreskalender werden die zurückliegenden Schuljahre aus dem alten Protokoll der Schuljahreswechsel rekonstruiert. Dieses Protokoll kennt aber nur den Tag, an dem der Wechsel ausgelöst wurde — nicht den tatsächlichen Stichtag.
Setzen Sie diese Variable vor der Migration, um die Stichtage exakt vorzugeben:
DJANGO_ACADEMIC_YEAR_SEED='[{"label": "2023/24", "start_date": "2023-08-01"}, {"label": "2024/25", "start_date": "2024-08-01"}, {"label": "2025/26", "start_date": "2025-08-01"}]'
Ist die Variable nicht gesetzt, werden die Stichtage geschätzt. Das kann dazu führen, dass alte Stundeneinträge um einige Tage neben der tatsächlichen Schuljahresgrenze liegen. Nach der Migration wird die Variable nicht mehr verwendet und kann entfernt werden.
Ist der Inhalt kein gültiges JSON in der oben gezeigten Form, bricht die Migration mit einer Fehlermeldung ab. Sie fällt bewusst nicht stillschweigend auf geschätzte Daten zurück.
Beispiel .env
# Sicherheit
DJANGO_SECRET_KEY=ihr-geheimer-schluessel-hier
DJANGO_DEBUG=False
DJANGO_ALLOWED_HOSTS=api.example.com
# Datenbank
DJANGO_DB_HOST=resource-planning-db
DJANGO_DB_NAME=resource_planning
DJANGO_DB_USER=rp_user
DJANGO_DB_PASSWORD=sicheres-passwort
# E-Mail
DJANGO_EMAIL_HOST=smtp.example.com
DJANGO_EMAIL_PORT=587
DJANGO_EMAIL_HOST_USER=noreply@example.com
DJANGO_EMAIL_HOST_PASSWORD=email-passwort
DJANGO_EMAIL_USE_TLS=True
# Frontend-URLs
DJANGO_CONFIG_FRONTEND_URL_LOGIN=https://app.example.com/login
DJANGO_CONFIG_FRONTEND_URL_PASSWORD_RESET=https://app.example.com/reset-password
DJANGO_CONFIG_FRONTEND_URL_EMAIL_CHANGE=https://app.example.com/account/change-email
# Schuljahreskalender
DJANGO_SCHOOL_FINAL_GRADE=3
# HTTPS-Einstellungen
DJANGO_REFRESH_TOKEN_COOKIE_SECURE=True
DJANGO_CSRF_COOKIE_SECURE=True
DJANGO_SESSION_COOKIE_SECURE=True
DJANGO_SECURE_PROXY_SSL_HEADER=https
DJANGO_USE_X_FORWARDED_HOST=True
DJANGO_NUM_PROXIES=1
DJANGO_CSRF_TRUSTED_ORIGINS=https://api.example.com
DJANGO_CORS_ALLOWED_ORIGINS=https://app.example.com