Skip to content

Latest commit

 

History

History
153 lines (114 loc) · 5.8 KB

File metadata and controls

153 lines (114 loc) · 5.8 KB

Deployment

CodeCraft läuft als drei Container: codecraft-web liefert den Editor als statische Dateien aus, codecraft-server das Backend — beide hinter Traefik. Dazu optional codecraft-luanti, der Spielserver, siehe unten.

Warum zwei Container unter einer Domain

Der Editor spricht das Backend über relative Pfade an (/api/v1/…, siehe apps/web/src/backend.ts). Damit ist es dieselbe Herkunft, und CORS entfällt vollständig — kein Preflight, keine Access-Control-*-Header, keine zweite Domain, die man im Frontend konfigurieren müsste.

Traefik entscheidet am Pfad: alles unter /api geht an das Backend, der Rest an nginx. Die beiden Router tragen deshalb ausgeschriebene Prioritäten (100 bzw. 1) in docker-compose.yml.

Der andere Weg wäre gewesen, das Backend die statischen Dateien mitliefern zu lassen — ein Container statt zwei. Das hätte MIME-Typen, Cache-Header und Bereichsanfragen in apps/server bedeutet, also rund sechzig Zeilen Code, die nginx besser kann und die niemand testet.

Voraussetzungen auf dem Server

docker network create proxy      # falls noch nicht vorhanden

Traefik muss die entryPoints web und websecure sowie den certresolver myresolver kennen — dieselbe Konfiguration wie bei kidslab.de und ZeFix.org.

Einrichten

git clone https://github.com/KidsLabDe/CodeCraft.git
cd CodeCraft

cp .env.example .env
$EDITOR .env                     # DOMAIN eintragen, ggf. LUANTI_PORT anpassen

mkdir -p daten welt
sudo chown 10001:10001 daten     # uid des Nutzers im Server-Container
sudo chown 30000:30000 welt      # uid des Nutzers im Luanti-Container

docker compose build
docker compose up -d

docker compose build führt Typprüfung und Tests mit aus. Ein Abbild, das nicht typprüft, entsteht gar nicht erst — sonst fiele der Fehler erst zur Laufzeit auf dem Server auf.

Die Daten

Alles liegt in einer Datei: daten/backend.json. Darin stehen die registrierten Spielserver samt Token, die Turtles, die Sitzungen der Kinder mit ihren Zauberwörtern und alle gespeicherten Programme.

Bewusst ein gemapptes Verzeichnis und kein Docker-Volume: die Datei soll sich lesen, mit cp sichern und im Zweifel von Hand reparieren lassen.

# Sichern
cp daten/backend.json sicherung/backend-$(date +%F).json

# Hineinschauen
python3 -m json.tool daten/backend.json | less

Das Verzeichnis muss 10001:10001 gehören (der Nutzer codecraft im Container, siehe Dockerfile). Gehört es root, startet das Backend, kann aber nichts schreiben — der Zustand wäre nach jedem Neustart weg.

daten/ steht in .gitignore. Die Zauberwörter der Kinder gehören nicht ins Repository.

Absenderadressen und die Drossel

Das Zauberwort ist Anmeldung und Geheimnis zugleich (siehe „Grenzen" in protocol.md). Was es tragfähig macht, ist die Drossel in apps/server/src/drossel.ts — und die braucht die echte Adresse des Kindes.

Hinter Traefik sieht das Backend sonst nur die Adresse des Proxys, und alle Kinder einer Schule teilten sich einen Zähler: ein einziges Kind, das sich fünfmal vertippt, sperrte die ganze Klasse aus. Deshalb steht in docker-compose.yml:

- CODECRAFT_TRUST_PROXY=1

Diese Einstellung gehört nur dorthin, wo wirklich ein Proxy davorsteht. Ohne Proxy darf jeder X-Forwarded-For frei erfinden und die Sperre damit vollständig umgehen. Der Backend-Container hat deshalb bewusst kein ports: — er ist ausschließlich über das proxy-Netz erreichbar.

Der gemeinsame Eimer der Drossel greift unabhängig davon und hält auch bei gefälschtem Header.

Aktualisieren

git pull
docker compose build
docker compose up -d

Die Datendatei bleibt unberührt. Speicher.laden füllt Felder auf, die eine ältere Fassung noch nicht geschrieben hat — ein Zustand aus einer früheren Version läuft also weiter.

Der Luanti-Server

Der Dienst codecraft-luanti baut aus docker/luanti/Dockerfile ein eigenständiges Spielserver-Image: das offizielle Basisspiel minetest_game (gepinnt auf Tag 5.8.0) plus der codecraft-Mod aus mods/luanti/codecraft. Beides kommt bei jedem Build frisch aus dem Quelltext, nur die Welt liegt außerhalb, im gemappten welt/ (siehe „Einrichten" oben — muss 30000:30000 gehören, dem Nutzer minetest im offiziellen Luanti-Image).

Luanti spricht UDP, kein HTTP — Traefik kann hier nicht vermitteln, der Port geht über docker-compose.yml direkt nach außen, Standardwert 36767 (siehe LUANTI_PORT in .env.example). Zum Backend spricht die Mod intern über den Dienstnamen server (docker/luanti/minetest.conf), ohne Umweg über Traefik — das Backend hat ja ohnehin kein ports:.

Das ist der bequeme Weg für einen einzelnen Server wie diesen. Der Spielserver kann davon unabhängig auch woanders laufen (z. B. in einer Schule): Er registriert sich beim Backend über POST /api/v1/servers/register und pollt danach, muss die Domain also nur über HTTPS erreichen können, läuft aber sonst außerhalb dieses Composes. Konfiguration dafür beschreibt mods/luanti/codecraft/README.md.

Nachsehen, ob es läuft

docker compose ps                       # alle drei Dienste, server healthy
docker compose logs -f server
docker compose logs -f luanti           # "Beim Backend angemeldet als ..."

curl -s https://$DOMAIN/ | head -5      # Editor
curl -s -X POST https://$DOMAIN/api/v1/pair \
  -H 'Content-Type: application/json' -d '{"code":"AAAAA"}'
# -> 404 "Code unbekannt oder abgelaufen": die API antwortet, Traefik trennt
#    die Pfade richtig. Kommt stattdessen HTML, greift die Prioritätsregel nicht.

Mit Luanti.app verbinden: Server, $DOMAIN, Port aus LUANTI_PORT (Standard 36767).

Der Healthcheck des Backends fragt genau diesen Endpunkt ab und wertet 404 und 429 als gesund — beides heißt, dass der Server antwortet und die Drossel lebt.