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 |
|---|---|---|
|
– |
SQLite-Datei (Kurzform für |
|
|
Speicher-Backend: |
|
|
Verbindungszeichenkette des Backends |
|
|
Lausch-Adresse |
|
|
Protokollierungsstufe: |
|
|
stellt |
|
– |
aktiviert CORS für diesen Ursprung (standardmäßig deaktiviert) |
|
|
Anfragenlimit pro Sekunde und pro Tenant (0 = deaktiviert) |
|
|
Größe des Token-Buckets für Anfragenspitzen |
|
|
PostgreSQL: aktiviert Row-Level Security pro Tenant (Verteidigung in der Tiefe, optional) |
|
– |
optionale Two-Sided-Bearoff-Datenbank ( |
Die meisten Optionen können auch über Umgebungsvariablen bereitgestellt werden (BLUNDERDB_BACKEND, BLUNDERDB_DSN, BLUNDERDB_ADDR, BLUNDERDB_LOG_LEVEL, BLUNDERDB_RLS, BLUNDERDB_TS_PATH).
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--metricsaktiv ist).
La surface métier suit le schéma POST /v1/<famille>.<méthode> (par exemple
/v1/positions.save, /v1/matches.get). Les familles couvrent les
positions, analyses, matchs, commentaires, collections, tournois, cartes Anki,
filtres, sessions, historique (recherche et commandes), recherche,
métadonnées, statistiques, import et export, ainsi que le cycle de vie des
tenants (tenant.purge, réservé au backend PostgreSQL). Les endpoints de
listing renvoient un flux NDJSON (un objet JSON par ligne). Le serveur
s’arrête proprement sur SIGINT / SIGTERM.
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.
Die Familie anki gewinnt sechs Methoden hinzu, die den Planer für verteilte Wiederholung (FSRS) erweitern: anki.reviewLog (ein Protokoll jeder Wiederholung — Bewertung und FSRS-Ergebnis — als Grundlage für Retentionsstatistiken und eine originalgetreue Historie), anki.forecast (eine Vorausschau auf die Anzahl der in den kommenden Tagen fälligen Karten, überfällige Karten eingeschlossen), anki.suspendCard / anki.buryCard / anki.removeCard (eine Karte vorübergehend oder endgültig aus der Wiederholungswarteschlange entfernen) sowie anki.optimizeParams (verschiebt die Zielretention eines Decks in Richtung der auf seinen Wiederholungen beobachteten Erfolgsquote).
8.2.2. Bereitstellung mit Docker
Das Repository stellt eine Dockerfile.serve bereit, die ein minimales Container-Image des Daemons erstellt: Nur das serve-Binary wird kompiliert (reines Go, ohne grafische Oberfläche und ohne CGO, also statisch gelinkt) und anschließend in ein distroless-Image gelegt.
# Construire l'image (depuis la racine du dépôt)
docker build -f Dockerfile.serve -t blunderdb-serve .
# Lancer le démon (le backend par défaut de l'image est postgres)
docker run --rm -p 8080:8080 \
-e BLUNDERDB_DSN="postgres://user:pass@hôte:5432/blunderdb?sslmode=disable" \
blunderdb-serve
Das Image lauscht auf Port 8080 und wird über Umgebungsvariablen konfiguriert (BLUNDERDB_BACKEND, BLUNDERDB_DSN, BLUNDERDB_ADDR, BLUNDERDB_RLS).
Warnung
Wie der Daemon selbst führt der Container keinerlei Authentifizierung durch: Er muss hinter einem Reverse-Proxy platziert werden, der für die Authentifizierung zuständig ist, und darf niemals direkt dem öffentlichen Internet ausgesetzt werden.
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.
Wird ein Tenant außer Betrieb genommen, löscht POST /v1/tenant.purge unwiderruflich alle seine Daten (Stellungen, Matches, Sammlungen, Verlauf usw.) für den aktuellen Tenant (den über X-Tenant-ID übermittelten), sowie seinen Sitzungszustand (letzte Suche, letzte Stellung, geöffnete Tabs — die wenigen mit diesem Scope präfixierten metadata-Zeilen): Der Vorgang läuft in einer einzigen Transaktion ab, ist idempotent (kein Fehler beim Purgen eines bereits leeren Tenants oder beim erneuten Aufruf) und wirkt sich weder auf andere Tenants noch auf die globale Schema-Versionszeile aus. Er ist nur mit dem PostgreSQL-Backend verfügbar — bei einem SQLite-Backend, das keinen Begriff von Tenant kennt, liefert er einen invalid-Fehler.
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
La migration copie les positions, leurs analyses et commentaires, les matchs
(parties + coups), les tournois (avec leurs liens de match) et les collections
(avec leur composition), en réattribuant les clés primaires et étrangères, le
tout dans une seule transaction côté destination : l’opération est atomique
(un échec laisse la destination intacte, il suffit de relancer). La progression
et le bilan final sont émis en NDJSON sur la sortie standard. Si la base source
est assez ancienne pour nécessiter sa propre mise à niveau de schéma sur place,
celle-ci s’exécute d’abord et émet ses propres événements
"schema-migration" (phase/effectué/total) avant que la copie ligne à ligne
ne commence.
Option |
Standard |
Bedeutung |
|---|---|---|
|
– |
SQLite-Quelldatenbank ( |
|
– |
Ziel-PostgreSQL-DSN ( |
|
– |
Ziel-Tenant-Scope (erforderlich außer bei |
|
– |
zählt, was kopiert würde, ohne etwas zu schreiben |
|
|
|
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 |
|---|---|---|
|
– |
SQLite-Datei (Kurzform für |
|
|
|
|
|
Verbindungszeichenkette des Backends |
|
|
Tenant-Scope (gesendet als |
|
|
Anfragerumpf im JSON-Format |
|
– |
liest den Anfragerumpf aus einer Datei |
|
– |
zeigt alle Methoden |
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).