Files
LehrerApp/docker/README.md
T
2026-08-24 12:14:36 +02:00

160 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# LehrerApp.Api Deployment per Docker
## Starten
```bash
cd docker
JWT_SECRET=<zufälliger-langer-string> docker compose up -d
```
Alle Server-Daten (Ereignis-Logs, Snapshots, Anhänge, Nutzer, Schulstandorte und Wetter-Cache)
liegen im Named Volume `api-data`
bei Neustarts/Updates bleibt es erhalten. Bewusst **kein** Bind-Mount auf einen Host-Pfad: bei
Git-basierten Deployments (z. B. Dokploy) wird das Checkout-Verzeichnis bei jedem Redeploy neu
geklont, ein dort liegendes `./data` wäre dabei jedes Mal leer (siehe TODO.md 10.2.4 für den
Vorfall, der das aufgedeckt hat).
## Nutzer anlegen
Es gibt keine offene Registrierung (`/api/auth/register`). Neue Nutzer werden per CLI im
laufenden Container angelegt:
```bash
docker compose exec api dotnet LehrerApp.Api.dll create-user <benutzername>
```
Fragt interaktiv nach einem Passwort (mind. 12 Zeichen). Alternativ nicht-interaktiv, z. B. aus
einem Skript:
```bash
docker compose exec api dotnet LehrerApp.Api.dll create-user <benutzername> --password "<passwort>"
```
Der Befehl beendet sich danach sofort wieder, ohne den API-Dienst zu starten für den
eigentlichen Serverbetrieb läuft `docker compose up` unverändert weiter.
## Passwort zurücksetzen
Analog zu `create-user`, aber für einen bereits existierenden Nutzer (schlägt fehl, wenn der
Nutzer nicht existiert zum Anlegen `create-user` verwenden):
```bash
docker compose exec api dotnet LehrerApp.Api.dll set-password <benutzername>
docker compose exec api dotnet LehrerApp.Api.dll set-password <benutzername> --password "<passwort>"
```
## Backup
`backup.sh` sichert das Named Volume `api-data` (Ereignis-Logs, Snapshots, Anhänge, Nutzer) über
einen kurzlebigen Alpine-Container als komprimiertes Tar-Archiv unter `docker/backups/` und räumt
Archive älter als 30 Tage automatisch auf:
```bash
./docker/backup.sh
```
Der volle Docker-Volume-Name ist `<compose-projektname>_api-data`. Lokal (`cd docker && docker
compose up`) ist das `docker_api-data` (Default im Skript), bei Dokploy der App-Slug, z. B.
`lehrerapp-syncserver-xyz_api-data` im Zweifel prüfen mit `docker volume ls | grep api-data`
und per Umgebungsvariable übergeben:
```bash
LEHRERAPP_DATA_VOLUME=lehrerapp-syncserver-xyz_api-data ./docker/backup.sh
```
Für regelmäßige Sicherung z. B. per Cron, einmal täglich nachts:
```bash
0 3 * * * LEHRERAPP_DATA_VOLUME=... /pfad/zu/docker/backup.sh >> /pfad/zu/docker/backup.log 2>&1
```
## Rate Limiting
`/api/auth/login` ist auf 5 Versuche pro Minute begrenzt (Brute-Force-Schutz). Alle übrigen
Endpunkte sind zusätzlich global auf 120 Anfragen pro Minute je IP begrenzt. Anhänge sind auf
15 MB Anfragegröße gedeckelt (Kestrel `MaxRequestBodySize`), einzelne Dateien clientseitig
zusätzlich auf 10 MB (`IAttachmentStorage.MaxSizeBytes`).
## Schulstandort und Wetter
Die authentifizierten Endpunkte unter `/api/school` geocodieren eine Schuladresse einmalig über
Nominatim/OpenStreetMap und laden danach ausschließlich mit den ermittelten Koordinaten
MOSMIX-Vorhersagen und CAP-Warnungen des DWD. Ergebnisse werden gecacht; bei einem vorübergehenden
DWD-Ausfall liefert der Server den letzten erfolgreichen Stand. Für eine eigene Installation sollte
`GEOCODING_USER_AGENT` auf eine Kennung mit eigener Kontakt-Webseite gesetzt werden, beispielsweise
`LehrerApp-Server/1.0 (+https://schule.example)`. Der Container benötigt ausgehenden HTTPS-Zugriff
auf `nominatim.openstreetmap.org`, `www.dwd.de` und `opendata.dwd.de`.
## WebUntis
Die WebUntis-Zugangsdaten werden ausschließlich im API-Container konfiguriert und nie an den
Desktop-Client ausgegeben:
```dotenv
WEBUNTIS_SCHOOL=meine-schule
WEBUNTIS_HOST=meine-schule.webuntis.com
WEBUNTIS_USER=technischer-benutzer
WEBUNTIS_PASSWORD=geheimes-passwort
WEBUNTIS_CLIENT=LehrerApp
WEBUNTIS_SESSION_IDLE_MINUTES=10
```
`WEBUNTIS_HOST` kann entfallen, wenn der Host `<WEBUNTIS_SCHOOL>.webuntis.com` entspricht. Der
technische Benutzer benötigt die jeweiligen WebUntis-Leserechte und darf für die alte JSON-RPC-
Schnittstelle keine aktivierte Zwei-Faktor-Authentifizierung haben.
Der API-Server meldet sich beim ersten Abruf an und verwendet diese Sitzung für weitere Abrufe.
Erst wenn zehn Minuten lang kein WebUntis-Abruf mehr aktiv war, meldet er sich automatisch ab.
`WEBUNTIS_SESSION_IDLE_MINUTES` kann bei Bedarf auf einen Wert zwischen 1 und 30 Minuten geändert
werden.
Alle Endpunkte benötigen dasselbe Bearer-Token wie die Sync-API und führen genau einen Abruf aus;
sie speichern das Ergebnis nicht serverseitig:
| Endpunkt | Zweck |
| --- | --- |
| `GET /api/webuntis/student-report?className=7a` | Schülerreport, optional nach Klasse gefiltert |
| `GET /api/webuntis/schoolyears` | Schuljahre |
| `GET /api/webuntis/classes?schoolyearId=123` | Klassen eines Schuljahres |
| `GET /api/webuntis/teachers` | Lehrkräfte und WebUntis-IDs |
| `GET /api/webuntis/holidays` | Ferienzeiträume |
| `GET /api/webuntis/timegrid` | Stunden-/Zeitraster |
| `GET /api/webuntis/substitutions?startDate=20260824&endDate=20260828` | Vertretungen im Zeitraum |
| `GET /api/webuntis/timetable?elementType=teacher&elementId=42&startDate=20260824&endDate=20260828` | Stundenplan für Klasse, Lehrkraft, Fach, Raum oder Schüler |
| `GET /api/webuntis/students/9001/absences?startDate=20260801&endDate=20270731` | Fehlzeiten eines Schülers; `9001` ist der `externKey` aus dem Schülerreport |
| `GET /api/webuntis/students/17/class-register-entries?startDate=20260801&endDate=20270731` | Klassenbucheinträge eines Schülers; `17` ist dessen `untisId` |
| `GET /api/webuntis/class-register/categories` | Kategorien der Klassenbucheinträge |
| `GET /api/webuntis/class-register/category-groups` | Kategoriegruppen der Klassenbucheinträge |
Datumsparameter verwenden das WebUntis-Format `yyyyMMdd`. Vertretungen sind auf 31 Tage und
Stundenpläne auf 62 Tage pro Anfrage begrenzt, damit ein einzelner Client keine unkontrolliert
großen WebUntis-Abfragen auslösen kann. Fehlzeiten und Klassenbucheinträge dürfen für einen
kompletten Schuljahreszeitraum von bis zu 400 Tagen geladen werden. Diese Klassenbuchfunktionen
sind nur verfügbar, wenn das Modul an der Schule aktiv ist und der technische Benutzer die
benötigten Leserechte besitzt.
## Deployment über Dokploy
Kein manuelles Bauen/Hochladen nötig Dokploy zieht das Repo direkt per Git und baut das Image
selbst aus `docker/Dockerfile.api`.
1. Neue **Compose-Application** anlegen, Quelle „Git" (Custom Git Provider), Repo-URL
`https://git.science-teaching.de/admin/LehrerApp.git`, Branch `main`. Bei privatem Repo einen
SSH-Deploy-Key in Dokploy hinterlegen und im Git-Server eintragen.
2. **Compose Path**: `docker/docker-compose.yml`. Der Build-Context (`context: ..`) und das
Dockerfile sind darin bereits korrekt referenziert Dokploy checkt das ganze Repo aus, das
passt ohne Anpassung.
3. **Umgebungsvariable** `JWT_SECRET` im Dokploy-UI unter „Environment" setzen (nicht als `.env`
einchecken) wird als `${JWT_SECRET}` in die Compose-Datei injiziert, wie beim lokalen Start.
4. **Domain/Port**: Domain auf Container-Port `5000` mappen, HTTPS über Dokploys eingebauten
Reverse-Proxy aktivieren.
5. **Auto-Deploy**: Webhook-URL aus Dokploy als Push-Hook im Git-Server eintragen (falls
unterstützt, z. B. Gitea) dann löst jeder `git push` automatisch ein Rebuild aus.
6. **Nutzer anlegen** nach dem ersten Deploy: über Dokploys Terminal/Exec-Tab am laufenden
Container `dotnet LehrerApp.Api.dll create-user <name>` ausführen (siehe oben).
**Wichtig:** die Daten liegen im Named Volume `api-data`, nicht in einem Bind-Mount Dokploy
klont das Repo bei jedem Redeploy neu, ein Bind-Mount relativ zum Checkout wäre dabei jedes Mal
leer gewesen (das ist real passiert, siehe TODO.md 10.2.4). Named Volumes übersteht Dokploys
`docker compose up --build --remove-orphans` dagegen unverändert.