Files
minecraft-atm10-stats/docs/OPERATIONS.md
T
lucashahmannandClaude Opus 5.5 e66f89f49e feat: ATM10-Statistikportal
Statistikportal für den All-the-Mods-10-Server (Minecraft 1.21.1, NeoForge 21.1):

- mod/: serverseitige NeoForge-Mod atmstats – Ereignisse, Live-Zähler, Servergesundheit,
  FTB-Dateien; Outbox auf der Festplatte, signierter gzip-Versand per HTTPS mit Backoff
- backend/: Fastify + PostgreSQL – idempotenter Ingest, Projektionen, Parser für SNBT,
  FTB Quests/Teams, world/stats, world/advancements und latest.log; Ranglisten, Snapshots,
  Retention, öffentliche API, SSE-Live-Updates, Admin mit argon2/CSRF, SFTP-Worker
- web/: Next.js-Dashboard – Übersicht, Ranglisten, Spielerprofile, ATM10, Server,
  Datenquellen, Verwaltung
- shared/: Protokoll v1 und API-Typen; tools/: Simulator, Log-Anonymisierer, RCON-Client
- deploy/: Docker Compose hinter Traefik, Backup; docs/: Architektur, Datenquellen, Betrieb

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 09:33:25 +02:00

9.2 KiB

Betrieb

Für den Betreiber des Portals: Installation auf dem DMZ-Server hinter OPNsense und Traefik, Einspielen der Mod bei Mine-Hoster, laufender Betrieb und Fehlerbehebung.

1. Voraussetzungen

  • Server in der DMZ mit Docker und Docker Compose v2
  • Traefik v3 mit Docker-Provider, einem HTTPS-Entrypoint und Cert-Resolver; Traefik hängt an einem externen Docker-Netz (z. B. traefik)
  • DNS-Eintrag für die Portal-Domain, der auf die öffentliche IP der OPNsense zeigt
  • Zugang zum Mine-Hoster-Gamepanel (SFTP-Daten unter Einstellungen → SFTP-Details)

Traefik v2: Die Middleware heißt dort ipwhitelist statt ipallowlist. In deploy/docker-compose.yml die Zeile traefik.http.middlewares.atm-ingest-allow.ipallowlist.sourcerange entsprechend umbenennen.

2. Portal installieren

git clone <repo> atm10-stats && cd atm10-stats
cp deploy/.env.example deploy/.env
openssl rand -hex 32        # Wert für INGEST_HMAC_SECRET
openssl rand -base64 24     # Wert für POSTGRES_PASSWORD
nano deploy/.env            # DOMAIN, TRAEFIK_*, INGEST_ALLOWED_IPS, Secrets, optional SFTP_*
docker compose -f deploy/docker-compose.yml --env-file deploy/.env up -d --build
docker compose -f deploy/docker-compose.yml --env-file deploy/.env ps     # api, web, db: healthy

Admin-Konto anlegen (Passwort mindestens 12 Zeichen, wird interaktiv abgefragt) und Ingest-Token für die Mod erzeugen:

docker compose -f deploy/docker-compose.yml --env-file deploy/.env exec api node dist/cli.js admin:create lucas
docker compose -f deploy/docker-compose.yml --env-file deploy/.env exec api node dist/cli.js token:create

Das Token wird nur einmal angezeigt. Später lässt es sich unter /admin erneuern.

Routing

Pfad Ziel Hinweis
https://DOMAIN/api/ingest* api:3001 nur von INGEST_ALLOWED_IPS (Middleware atm-ingest-allow)
https://DOMAIN/api/* api:3001 öffentliche Lese-API, /api/live (SSE), /api/admin/*
https://DOMAIN/* web:3000 Dashboard

/api/live ist ein Server-Sent-Events-Stream. Auf diese Route keine Kompressions- oder Buffering-Middleware legen. Die API sendet dafür X-Accel-Buffering: no und alle 25 s einen Heartbeat.

OPNsense

  1. Eingehend: HTTPS (443) zur DMZ/Traefik wie für andere Dienste. Die Einschränkung von /api/ingest auf den Gameserver übernimmt Traefik (INGEST_ALLOWED_IPS). Die IP des Mine-Hoster-Servers ergibt sich aus der Serveradresse im Gamepanel (Hostname per nslookup auflösen). Optional zusätzlich in OPNsense: Alias mit dieser IP und eine eigene Regel nur für den Ingest-Host.
  2. Ausgehend (nur für den SFTP-Abgleich): DMZ-Server → SFTP-Host/Port des Mine-Hoster-Servers (TCP). Ohne diese Regel meldet /sources „Fehler beim letzten Abgleich“.

3. Mod bei Mine-Hoster einspielen

Die Mod ist rein serverseitig: Spieler installieren nichts, Clients ohne die Mod können normal verbinden.

  1. Mod bauen (oder fertiges JAR aus einem Release verwenden):
    ./mod/gradlew -p mod build        # → mod/build/libs/atmstats-1.0.0.jar
    
  2. Im Gamepanel den Server stoppen.
  3. Per SFTP (WinSCP/FileZilla) atmstats-1.0.0.jar nach mods/ hochladen.
  4. Server starten und nach dem Start wieder stoppen. Dabei entsteht config/atmstats-server.toml.
  5. Konfiguration per SFTP bearbeiten:
    [atmstats]
        enabled = true
        endpoint = "https://stats.example.de"
        serverId = "atm10"
        token = "<Token aus token:create>"
        hmacSecret = "<INGEST_HMAC_SECRET aus deploy/.env>"
        statsSnapshotMinutes = 5
        questScanMinutes = 2
        healthIntervalSeconds = 60
        maxOutboxMb = 50
    
  6. Server starten und in logs/latest.log nach atmstats: suchen:
    • atmstats: backend reachable: fertig, Daten laufen.
    • atmstats: backend NOT reachable: …: siehe Abschnitt 6. Die Ereignisse bleiben in atmstats/outbox/ auf dem Gameserver und werden gesendet, sobald die Verbindung klappt.

Ohne endpoint, token oder hmacSecret bleibt die Mod passiv und schreibt nur eine Hinweiszeile ins Log.

ATM10-Updates: Die Mod verwendet nur APIs, die in NeoForge 21.1.x stabil sind, und deklariert NeoForge ab 21.1.0. Bei Pack-Updates innerhalb von Minecraft 1.21.1 bleibt das JAR gültig. Der Mod-Ordner wird bei Modpack-Updates manchmal ersetzt: danach prüfen, ob mods/atmstats-*.jar noch vorhanden ist.

4. SFTP-Abgleich (optional, empfohlen)

In deploy/.env SFTP_HOST, SFTP_PORT, SFTP_USER, SFTP_PASSWORD setzen (Gamepanel → Einstellungen → SFTP-Details). SFTP_ROOT ist das Serververzeichnis, SFTP_WORLD_DIR der Weltordner (Standard world, level-name aus server.properties). Danach docker compose … up -d worker.

Der Worker holt alle 15 Minuten nur geänderte Dateien. Unter /admin lässt sich ein Abgleich sofort anstoßen. Ohne funktionierende Mod liefert SFTP trotzdem Gesamtzähler, Advancements, Quests und (aus dem Log) Beitritte; Echtzeit-Daten, Todesursachen und Serverleistung fehlen dann.

Das SFTP-Passwort ist das des Gamepanels und gewährt Schreibzugriff. Der Worker liest ausschließlich.

5. Laufender Betrieb

Updates

git pull
docker compose -f deploy/docker-compose.yml --env-file deploy/.env up -d --build

Datenbankmigrationen laufen beim Start von api und worker automatisch (serialisiert über einen Advisory-Lock).

Token und Secret erneuern

  • Ingest-Token: /admin → Ingest-Token erneuern. Das alte Token wird sofort ungültig. Das neue in config/atmstats-server.toml eintragen und den Gameserver neu starten. Bis dahin puffert die Mod und meldet authentication failed im Log; es geht nichts verloren, solange das Outbox-Limit nicht erreicht ist.
  • HMAC-Secret: in deploy/.env und in der Mod-Konfiguration gleichzeitig ändern, dann api und Gameserver neu starten.

Backups

Der Container backup erstellt täglich pg_dump-Dateien im Volume backups und löscht sie nach BACKUP_KEEP_DAYS (Standard 14). Sofortiges Backup:

docker compose -f deploy/docker-compose.yml --env-file deploy/.env exec backup sh /backup.sh once

Wiederherstellung (ersetzt alle Daten):

C="docker compose -f deploy/docker-compose.yml --env-file deploy/.env"
$C stop api worker web
$C exec backup ls -t /backups                                   # gewünschte Datei wählen
$C exec backup dropdb atm
$C exec backup createdb atm
$C exec backup pg_restore --dbname=atm --no-owner /backups/<datei>.dump
$C start api worker web

Das Volume backups liegt auf demselben Host. Für echte Ausfallsicherheit die Dateien zusätzlich extern sichern (z. B. per Borg/Restic vom Docker-Volume-Pfad).

Verhalten bei Neustarts

Situation Verhalten
Gameserver stoppt normal Mod schreibt player_leave und server_stop in die Outbox und sendet sie beim nächsten Start; nach 10 min ohne Health-Meldung schließt der Worker offene Sitzungen
Gameserver stürzt ab beim nächsten Start schließt server_start offene Sitzungen zum letzten Lebenszeichen (kein Aufblähen der Spielzeit)
Backend nicht erreichbar Mod puffert auf der Festplatte (max. maxOutboxMb, 7 Tage), sendet mit Backoff 1 s … 5 min weiter; bei Platzmangel werden zuerst alte Statistik-Snapshots verworfen
Doppelte Lieferung jede Meldung hat eine eindeutige ID, Duplikate werden ignoriert
API/Worker-Neustart zustandslos; der Worker setzt den Log-Abgleich am gespeicherten Byte-Offset fort

6. Fehlerbilder

Meldung Ursache Lösung
Mod: backend NOT reachable: …ConnectException / HttpTimeoutException Mine-Hoster blockiert ausgehende Verbindungen, DNS falsch oder OPNsense/Traefik lässt nicht durch curl -v https://DOMAIN/api/health von außen testen; Traefik-Log prüfen (403 = IP nicht in INGEST_ALLOWED_IPS); falls der Hoster ausgehend blockiert: enabled = false und nur SFTP nutzen
Mod: clock skew: server clock differs … Uhr des Gameservers oder des DMZ-Servers weicht > 5 min ab NTP auf dem DMZ-Server prüfen; beim Hoster melden, falls der Gameserver falsch geht
Mod: authentication failed (bad_token) Token falsch oder widerrufen neues Token erzeugen und eintragen
Mod: authentication failed (bad_signature) hmacSecret ≠ INGEST_HMAC_SECRET Werte angleichen
Mod: batch rejected by backend … moved to outbox/rejected Daten passten nicht zum Protokoll (z. B. veraltete Mod-Version) Datei in atmstats/outbox/rejected/ samt .reason.txt prüfen, Mod und Backend auf gleichen Stand bringen
Mod: outbox limit reached, dropped … Backend länger nicht erreichbar Verbindung reparieren; ggf. maxOutboxMb erhöhen
/sources: SFTP „Fehler beim letzten Abgleich“ Zugangsdaten, Port oder OPNsense-Regel Fehlertext unter /admin → Letzte Fehler
Live-Ticker bleibt leer, Seite sonst ok Proxy puffert SSE keine Kompressions-/Buffering-Middleware auf /api/live
Login unter /admin klappt nicht ohne HTTPS Cookie ist Secure Portal über HTTPS aufrufen (lokal: NODE_ENV=development)

Spieler, die nicht im Portal erscheinen möchten, lassen sich unter /admin ausblenden: Sie verschwinden aus allen öffentlichen Ansichten, Ranglisten und Live-Meldungen. Die Daten bleiben gespeichert, sodass sich das Ausblenden rückgängig machen lässt.