8. Headless-Modus (Server)

Bemerkung

Dieser Abschnitt beschreibt einen fortgeschrittenen und optionalen Modus von blunderDB, der für Server-Bereitstellungen, den Mehrbenutzerbetrieb und die Automatisierung gedacht ist. Die normale und empfohlene Nutzung von blunderDB bleibt die Desktop-Anwendung, die in den vorherigen Kapiteln beschrieben wird. Wenn Sie blunderDB allein auf Ihrem Computer verwenden, benötigen Sie diesen Modus nicht: Sie können dieses Kapitel überspringen, ohne dabei Analysefunktionen zu verpassen.

8.1. Überblick

Dasselbe Binärprogramm blunderdb kann zusätzlich zur Desktop-Anwendung und den Kommandozeilenbefehlen (siehe Befehlszeilenschnittstelle (CLI)) im Headless-Modus laufen: ohne grafische Oberfläche, vollständig über die Kommandozeile oder über das Netzwerk gesteuert. Dieser Modus umfasst drei Anwendungsfälle:

  • der Daemon serve — stellt die Engine von blunderDB als HTTP-+-JSON-Dienst bereit, um eine gemeinsame Datenbank auf einem Server zu betreiben und mehrbenutzerfähig darauf zuzugreifen;

  • der generische Dispatcher call — ruft jede beliebige Speicheroperation direkt und lokal auf, für Skripting und Tests;

  • der Befehl migrate — überträgt eine SQLite-Einzelbenutzerdatenbank in ein mehrbenutzerfähiges PostgreSQL-Backend.

Diese drei Anwendungsfälle stützen sich auf eine gemeinsame Speicherschicht, die mit zwei Backends umgehen kann: SQLite (das übliche .db-Dateiformat der Desktop-Anwendung) und PostgreSQL (für mehrbenutzerfähige Server-Bereitstellungen).

8.2. Der Daemon serve

blunderdb serve startet die Engine als HTTP-Dienst, der mit JSON antwortet. Damit lässt sich eine Stellungsdatenbank auf einer Maschine hosten und von mehreren Clients aus darauf zugreifen.

# Servir une base SQLite locale sur le port 8080
blunderdb serve --db ma_base.db --addr :8080

# Servir un backend PostgreSQL
blunderdb serve --backend postgres \
    --dsn "postgres://user:pass@host:5432/blunderdb?sslmode=disable" \
    --addr :8080

Warnung

Der Daemon führt keinerlei Authentifizierung durch. Er vertraut dem Anfrage-Header X-Tenant-ID und muss hinter einem Reverse-Proxy (nginx, Caddy …) laufen, der für die Authentifizierung zuständig ist. Setzen Sie ihn niemals direkt dem öffentlichen Internet aus.

Optionen:

Option

Standard

Bedeutung

--db <Pfad>

SQLite-Datei (Kurzform für --backend sqlite --dsn <chemin>)

--backend <type>

sqlite

Speicher-Backend: sqlite oder postgres

--dsn <Zeichenkette>

$BLUNDERDB_DSN

Verbindungszeichenkette des Backends

--addr <Host:Port>

:8080

Lausch-Adresse

--log-level <Stufe>

info

Protokollierungsstufe: debug|info|warn|error

--metrics

true

stellt /metrics bereit (Prometheus-Format)

--cors-allow-origin <Ursprung>

aktiviert CORS für diesen Ursprung (standardmäßig deaktiviert)

--rate-limit-rps <n>

0

Anfragenlimit pro Sekunde und pro Tenant (0 = deaktiviert)

--rate-limit-burst <n>

2×rps

Größe des Token-Buckets für Anfragenspitzen

--rls

false

PostgreSQL: aktiviert Row-Level Security pro Tenant (Verteidigung in der Tiefe, optional)

Die meisten Optionen können auch über Umgebungsvariablen bereitgestellt werden (BLUNDERDB_BACKEND, BLUNDERDB_DSN, BLUNDERDB_ADDR, BLUNDERDB_LOG_LEVEL, BLUNDERDB_RLS).

8.2.1. Endpunkte

Der Dienst stellt stets vorhandene Betriebs-Endpunkte bereit:

  • GET /healthz — Lebendigkeit (der Prozess läuft);

  • GET /readyz — Bereitschaft (der Speicher antwortet);

  • GET /metrics — Prometheus-Metriken (falls --metrics aktiv ist).

Die fachliche Oberfläche folgt dem Schema POST /v1/<Familie>.<Methode> (zum Beispiel /v1/positions.save, /v1/matches.get). Die Familien umfassen Stellungen, Analysen, Matches, Kommentare, Sammlungen, Turniere, Anki-Karten, Filter, Sitzungen, Suche, Metadaten, Statistiken und Import. Die Listing-Endpunkte liefern einen NDJSON-Strom (ein JSON-Objekt pro Zeile). Der Server wird bei SIGINT / SIGTERM sauber beendet.

Zwei Methoden der positions-Familie dekodieren eine Stellung, ohne sie zu speichern: positions.fromXGID rekonstruiert eine Stellung aus einer XGID-Zeichenkette und positions.fromXGP aus einer Einzelstellungsdatei .xgp.

8.3. PostgreSQL-Backend und Mehrbenutzerbetrieb

Für eine gemeinsame Bereitstellung kann blunderDB die Daten in PostgreSQL statt in einer SQLite-Datei speichern. Das Backend wird über --backend postgres und die Verbindungszeichenkette --dsn ausgewählt. Das Schema wird beim Start automatisch erstellt und migriert.

Die Daten sind nach Tenant (Mandant) abgeschottet: jede Anfrage trägt eine Scope-Kennung (Header X-Tenant-ID, standardmäßig default), was es mehreren Benutzern ermöglicht, dieselbe Instanz zu teilen, ohne die Daten der anderen zu sehen. Die Option --rls aktiviert zusätzlich die Row-Level Security von PostgreSQL: Es werden Isolationsrichtlinien pro Tenant installiert und app.tenant_id wird pro Verbindung gesetzt. Dies ist eine optionale Verteidigung in der Tiefe, die standardmäßig deaktiviert ist.

8.4. Eine SQLite-Datenbank nach PostgreSQL migrieren

blunderdb migrate kopiert eine SQLite-Einzelbenutzerdatenbank in ein PostgreSQL-Backend, unter einem gewählten Tenant-Scope — das ist der Weg, um eine Desktop-Bibliothek in eine Server-Bereitstellung „hochzuladen“.

blunderdb migrate \
    --from sqlite:///chemin/vers/base.db \
    --to   "postgres://user:pass@host:5432/db?sslmode=disable" \
    --tenant-id mon-tenant

# Prévisualiser sans rien écrire
blunderdb migrate --from sqlite:///chemin/vers/base.db \
    --tenant-id mon-tenant --dry-run

Die Migration kopiert die Stellungen, ihre Analysen und Kommentare, die Matches (Partien + Züge), die Turniere (mit ihren Match-Verknüpfungen) und die Sammlungen (mit ihrer Zusammensetzung), wobei die Primär- und Fremdschlüssel neu zugewiesen werden, das Ganze in einer einzigen Transaktion auf der Zielseite: Der Vorgang ist atomar (ein Fehlschlag lässt das Ziel unverändert, es genügt, ihn erneut zu starten). Der Fortschritt und die abschließende Bilanz werden als NDJSON auf der Standardausgabe ausgegeben.

Option

Standard

Bedeutung

--from <uri>

SQLite-Quelldatenbank (sqlite:///chemin oder ein einfacher Pfad)

--to <dsn>

Ziel-PostgreSQL-DSN (postgres://…)

--tenant-id <scope>

Ziel-Tenant-Scope (erforderlich außer bei --dry-run)

--dry-run

zählt, was kopiert würde, ohne etwas zu schreiben

--on-conflict <Richtlinie>

""

"" bricht ab, wenn der Tenant bereits Daten hat; skip führt zusammen (Deduplizierung der Stellungen über den Zobrist-Hash)

Bemerkung

Noch nicht migriert werden die Anwendungszustände: Anki-Decks/-Karten, Filterbibliothek, Such- und Befehlsverlauf sowie Sitzungsmetadaten. Vorrang hat die Migration der Stellungsbibliothek und des Match-Verlaufs.

8.5. Der generische Dispatcher call

Ergänzend zu den herkömmlichen Unterbefehlen (Befehlszeilenschnittstelle (CLI)) stellt blunderdb call alle Speicheroperationen direkt und lokal bereit. Es verwendet dieselben Handler wie der Daemon serve: das Verhalten ist also identisch zu POST /v1/<famille>.<méthode>. Das ist nützlich für Skripting und Integrationstests.

# Lister toutes les méthodes disponibles
blunderdb call --list

# Lectures
blunderdb call metadata.counts --db ma_base.db
blunderdb call positions.list  --db ma_base.db --json '{"limit":10}'
blunderdb call matches.get     --db ma_base.db --json '{"id":1}'

# Écritures
blunderdb call positions.save  --db ma_base.db --json '{"position":{...}}'
blunderdb call matches.delete  --db ma_base.db --json '{"id":42}'

Optionen:

Option

Standard

Bedeutung

--db <Pfad>

SQLite-Datei (Kurzform für --backend sqlite --dsn <chemin>)

--backend <type>

sqlite

sqlite oder postgres

--dsn <Zeichenkette>

$BLUNDERDB_DSN

Verbindungszeichenkette des Backends

--scope <Zeichenkette>

default

Tenant-Scope (gesendet als X-Tenant-ID)

--json <Zeichenkette>

{}

Anfragerumpf im JSON-Format

--json-file <Pfad>

liest den Anfragerumpf aus einer Datei

--list

zeigt alle Methoden <famille>.<méthode> an und beendet sich

Die JSON-Antwort (oder der NDJSON-Strom bei den *.list-Endpunkten) wird auf die Standardausgabe geschrieben. Im Fehlerfall endet der Prozess mit einem von null verschiedenen Code, und der Umschlag {"error":{…}} wird auf die Standardausgabe geschrieben, damit er analysierbar bleibt (zum Beispiel mit jq).