8. Modalità headless (server)
Nota
Questa sezione descrive una modalità avanzata e facoltativa di blunderDB, destinata ai deployment su server, all’uso multiutente e all’automazione. L’uso normale e consigliato di blunderDB resta l’applicazione desktop descritta nei capitoli precedenti. Se usi blunderDB da solo, sul tuo computer, non hai bisogno di questa modalità: puoi ignorare questo capitolo senza perdere nulla delle funzionalità di analisi.
8.1. Panoramica
Lo stesso binario blunderdb può, oltre all’applicazione desktop e ai comandi a riga di comando (vedi Interfaccia a riga di comando (CLI)), funzionare in modalità headless: senza interfaccia grafica, pilotato interamente da riga di comando o tramite rete. Questa modalità raggruppa tre usi:
il demone
serve— espone il motore di blunderDB come servizio HTTP + JSON, per far girare un database condiviso su un server e accedervi in più persone;il dispatcher generico
call— richiama qualsiasi operazione di archiviazione direttamente, in locale, per lo scripting e i test;il comando
migrate— trasferisce un database SQLite monoutente verso un backend PostgreSQL multiutente.
Questi tre usi si basano su un livello di archiviazione comune capace di dialogare con due backend: SQLite (il consueto formato di file .db dell’applicazione desktop) e PostgreSQL (per i deployment server multiutente).
8.2. Il demone serve
blunderdb serve avvia il motore come servizio HTTP che risponde in JSON. Permette di ospitare un database di posizioni su una macchina e di accedervi da più client.
# 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
Avvertimento
Il demone non effettua alcuna autenticazione. Si fida dell’header di richiesta X-Tenant-ID e deve girare dietro un reverse-proxy (nginx, Caddy…) incaricato dell’autenticazione. Non esporlo mai direttamente su Internet pubblico.
Opzioni:
Opzione |
Predefinito |
Significato |
|---|---|---|
|
– |
file SQLite (scorciatoia per |
|
|
backend di archiviazione: |
|
|
stringa di connessione del backend |
|
|
indirizzo di ascolto |
|
|
livello di logging: |
|
|
espone |
|
– |
abilita CORS per questa origine (disabilitato per impostazione predefinita) |
|
|
limite di richieste al secondo per tenant (0 = disabilitato) |
|
|
dimensione del secchio di token per i picchi di richieste |
|
|
PostgreSQL: abilita la Row-Level Security per tenant (difesa in profondità, opzionale) |
|
– |
database di bearoff two-sided ( |
La maggior parte delle opzioni può anche essere fornita tramite variabile d’ambiente (BLUNDERDB_BACKEND, BLUNDERDB_DSN, BLUNDERDB_ADDR, BLUNDERDB_LOG_LEVEL, BLUNDERDB_RLS, BLUNDERDB_TS_PATH).
8.2.1. Punti di accesso
Il servizio espone dei punti di accesso operativi, sempre presenti:
GET /healthz— liveness (il processo è in esecuzione);GET /readyz— readiness (l’archiviazione risponde);GET /metrics— metriche Prometheus (se--metricsè attivo).
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.
Due metodi della famiglia positions decodificano una posizione senza salvarla: positions.fromXGID ricostruisce una posizione da una stringa XGID e positions.fromXGP da un file di posizione singola .xgp.
La famiglia anki guadagna sei metodi che estendono il pianificatore a ripetizione dilazionata (FSRS): anki.reviewLog (registro di ogni revisione — valutazione e risultato FSRS — per le statistiche di ritenzione e uno storico fedele), anki.forecast (proiezione del numero di carte in scadenza nei prossimi giorni, incluse quelle in ritardo), anki.suspendCard / anki.buryCard / anki.removeCard (rimuovere una carta dalla coda di revisione temporaneamente o definitivamente) e anki.optimizeParams (avvicina il tasso di ritenzione obiettivo di un mazzo al tasso di successo osservato sulle sue revisioni).
8.2.2. Deployment con Docker
Il repository fornisce un Dockerfile.serve che costruisce un’immagine container minima del demone: viene compilato solo il binario serve (Go puro, senza interfaccia grafica e senza CGO, quindi collegato staticamente), poi collocato in un’immagine distroless.
# 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
L’immagine ascolta sulla porta 8080 e si configura tramite variabili d’ambiente (BLUNDERDB_BACKEND, BLUNDERDB_DSN, BLUNDERDB_ADDR, BLUNDERDB_RLS).
Avvertimento
Come il demone stesso, il container non effettua alcuna autenticazione : deve essere collocato dietro un reverse-proxy incaricato dell’autenticazione e non essere mai esposto direttamente su Internet pubblico.
8.3. Backend PostgreSQL e multiutente
Per un deployment condiviso, blunderDB può memorizzare i dati in PostgreSQL invece che in un file SQLite. Il backend è selezionato tramite --backend postgres e la stringa di connessione --dsn. Lo schema viene creato e migrato automaticamente all’avvio.
I dati sono compartimentati per tenant (locatario): ogni richiesta porta un identificatore di scope (header X-Tenant-ID, per impostazione predefinita default), il che permette a più utenti di condividere la stessa istanza senza vedere i dati altrui. L’opzione --rls abilita in aggiunta la Row-Level Security di PostgreSQL: vengono installate politiche di isolamento per tenant e app.tenant_id viene impostato per connessione. È una difesa in profondità facoltativa, disabilitata per impostazione predefinita.
Quando un tenant viene dismesso, POST /v1/tenant.purge elimina definitivamente tutti i suoi dati (posizioni, partite, collezioni, cronologia, ecc.) sul tenant corrente (quello indicato da X-Tenant-ID), oltre al suo stato di sessione (ultima ricerca, ultima posizione, schede aperte — le poche righe metadata prefissate con questo scope): l’operazione viene eseguita in un’unica transazione, è idempotente (nessun errore nel purgare un tenant già vuoto o nel ripetere la chiamata) e non ha effetto su nessun altro tenant né sulla riga globale di versione dello schema. È disponibile solo con il backend PostgreSQL — restituisce un errore invalid su un backend SQLite, che non ha alcuna nozione di tenant.
8.4. Migrare un database SQLite verso PostgreSQL
blunderdb migrate copia un database SQLite monoutente verso un backend PostgreSQL, sotto uno scope di tenant scelto — è il percorso per « caricare » una libreria desktop verso un deployment server.
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.
Opzione |
Predefinito |
Significato |
|---|---|---|
|
– |
database SQLite di origine ( |
|
– |
DSN PostgreSQL di destinazione ( |
|
– |
scope di tenant di destinazione (obbligatorio tranne in |
|
– |
conta ciò che verrebbe copiato senza scrivere nulla |
|
|
|
Nota
Non vengono (ancora) migrati gli stati applicativi: deck/carte Anki, libreria di filtri, cronologia di ricerca e di comandi, e metadati di sessione. La priorità è la migrazione della libreria di posizioni e della cronologia dei match.
8.5. Il dispatcher generico call
In aggiunta ai sottocomandi storici (Interfaccia a riga di comando (CLI)), blunderdb call espone tutte le operazioni di archiviazione direttamente, in locale. Passa per gli stessi gestori del demone serve: il comportamento è quindi identico a POST /v1/<famiglia>.<metodo>. È utile per lo scripting e i test di integrazione.
# 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}'
Opzioni:
Opzione |
Predefinito |
Significato |
|---|---|---|
|
– |
file SQLite (scorciatoia per |
|
|
|
|
|
stringa di connessione del backend |
|
|
scope di tenant (inviato come |
|
|
corpo della richiesta in formato JSON |
|
– |
legge il corpo della richiesta da un file |
|
– |
mostra tutti i metodi |
La risposta JSON (o il flusso NDJSON per gli endpoint *.list) viene scritta sullo standard output. In caso di errore, il processo termina con un codice diverso da zero e l’envelope {"error":{…}} viene stampato sullo standard output per restare analizzabile (per esempio con jq).