8. Modo headless (servidor)

Nota

Esta sección describe un modo avanzado y opcional de blunderDB, destinado a los despliegues en servidor, al uso multiusuario y a la automatización. El uso normal y recomendado de blunderDB sigue siendo la aplicación de escritorio descrita en los capítulos anteriores. Si utiliza blunderDB en solitario, en su propio ordenador, no necesita este modo: puede ignorar este capítulo sin perder ninguna de las funcionalidades de análisis.

8.1. Visión general

El mismo binario blunderdb puede, además de la aplicación de escritorio y de los comandos en línea (véase Interfaz de línea de comandos (CLI)), funcionar en modo headless: sin interfaz gráfica, controlado por completo desde la línea de comandos o a través de la red. Este modo agrupa tres usos:

  • el demonio serve — expone el motor de blunderDB como un servicio HTTP + JSON, para ejecutar una base compartida en un servidor y acceder a ella entre varios usuarios;

  • el despachador genérico call — invoca cualquier operación de almacenamiento directamente, en local, para el scripting y las pruebas;

  • el comando migrate — transfiere una base SQLite de un solo usuario a un backend PostgreSQL multiusuario.

Estos tres usos se apoyan en una capa de almacenamiento común capaz de comunicarse con dos backends: SQLite (el formato de archivo .db habitual de la aplicación de escritorio) y PostgreSQL (para los despliegues en servidor multiusuario).

8.2. El demonio serve

blunderdb serve lanza el motor como un servicio HTTP que responde en JSON. Permite alojar una base de posiciones en una máquina y acceder a ella desde varios clientes.

# 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

Advertencia

El demonio no realiza ninguna autenticación. Confía en la cabecera de petición X-Tenant-ID y debe ejecutarse detrás de un reverse-proxy (nginx, Caddy…) encargado de la autenticación. Nunca lo exponga directamente en la Internet pública.

Opciones:

Opción

Por defecto

Significado

--db <chemin>

archivo SQLite (atajo de --backend sqlite --dsn <chemin>)

--backend <type>

sqlite

backend de almacenamiento: sqlite o postgres

--dsn <chaîne>

$BLUNDERDB_DSN

cadena de conexión del backend

--addr <hôte:port>

:8080

dirección de escucha

--log-level <niveau>

info

nivel de registro: debug|info|warn|error

--metrics

true

expone /metrics (formato Prometheus)

--cors-allow-origin <origine>

activa CORS para este origen (desactivado por defecto)

--rate-limit-rps <n>

0

límite de peticiones por segundo y por tenant (0 = desactivado)

--rate-limit-burst <n>

2×rps

tamaño del cubo de tokens para los picos de peticiones

--rls

false

PostgreSQL: activa la Row-Level Security por tenant (defensa en profundidad, opcional)

La mayoría de las opciones también pueden indicarse mediante variables de entorno (BLUNDERDB_BACKEND, BLUNDERDB_DSN, BLUNDERDB_ADDR, BLUNDERDB_LOG_LEVEL, BLUNDERDB_RLS).

8.2.1. Puntos de acceso

El servicio expone puntos de acceso de explotación, siempre presentes:

  • GET /healthz — vivacidad (el proceso está en ejecución);

  • GET /readyz — disponibilidad (el almacenamiento responde);

  • GET /metrics — métricas Prometheus (si --metrics está activo).

La superficie funcional sigue el esquema POST /v1/<famille>.<méthode> (por ejemplo /v1/positions.save, /v1/matches.get). Las familias abarcan las posiciones, análisis, partidas, comentarios, colecciones, torneos, tarjetas Anki, filtros, sesiones, búsqueda, metadatos, estadísticas e importación. Los endpoints de listado devuelven un flujo NDJSON (un objeto JSON por línea). El servidor se detiene limpiamente con SIGINT / SIGTERM.

Dos métodos de la familia positions decodifican una posición sin guardarla: positions.fromXGID reconstruye una posición a partir de una cadena XGID, y positions.fromXGP a partir de un archivo de posición única .xgp.

8.3. Backend PostgreSQL y multiusuario

Para un despliegue compartido, blunderDB puede almacenar los datos en PostgreSQL en lugar de en un archivo SQLite. El backend se selecciona mediante --backend postgres y la cadena de conexión --dsn. El esquema se crea y se migra automáticamente al arrancar.

Los datos están compartimentados por tenant (inquilino): cada petición lleva un identificador de scope (cabecera X-Tenant-ID, por defecto default), lo que permite que varios usuarios compartan la misma instancia sin ver los datos de los demás. La opción --rls activa además la Row-Level Security de PostgreSQL: se instalan políticas de aislamiento por tenant y app.tenant_id se fija por conexión. Es una defensa en profundidad opcional, desactivada por defecto.

8.4. Migrar una base SQLite a PostgreSQL

blunderdb migrate copia una base SQLite de un solo usuario a un backend PostgreSQL, bajo un scope de tenant elegido: es la vía para « subir » una biblioteca de escritorio a un despliegue en servidor.

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 migración copia las posiciones, sus análisis y comentarios, las partidas (juegos + jugadas), los torneos (con sus vínculos de partida) y las colecciones (con su composición), reasignando las claves primarias y foráneas, todo ello en una única transacción del lado de destino: la operación es atómica (un fallo deja el destino intacto, basta con relanzarla). El progreso y el balance final se emiten en NDJSON por la salida estándar.

Opción

Por defecto

Significado

--from <uri>

base SQLite de origen (sqlite:///chemin o una simple ruta)

--to <dsn>

DSN PostgreSQL de destino (postgres://…)

--tenant-id <scope>

scope de tenant de destino (obligatorio salvo en --dry-run)

--dry-run

cuenta lo que se copiaría sin escribir nada

--on-conflict <politique>

""

"" se interrumpe si el tenant ya tiene datos; skip fusiona (deduplicación de las posiciones por hash Zobrist)

Nota

No se migran (todavía) los estados de la aplicación: mazos/tarjetas Anki, biblioteca de filtros, historial de búsqueda y de comandos, y metadatos de sesión. La prioridad es la migración de la biblioteca de posiciones y del historial de partidas.

8.5. El despachador genérico call

Como complemento de los subcomandos históricos (Interfaz de línea de comandos (CLI)), blunderdb call expone todas las operaciones de almacenamiento directamente, en local. Pasa por los mismos gestores que el demonio serve: el comportamiento es, por tanto, idéntico al de POST /v1/<famille>.<méthode>. Resulta útil para el scripting y las pruebas de integración.

# 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}'

Opciones:

Opción

Por defecto

Significado

--db <chemin>

archivo SQLite (atajo de --backend sqlite --dsn <chemin>)

--backend <type>

sqlite

sqlite o postgres

--dsn <chaîne>

$BLUNDERDB_DSN

cadena de conexión del backend

--scope <chaîne>

default

scope de tenant (enviado como X-Tenant-ID)

--json <chaîne>

{}

cuerpo de la petición en formato JSON

--json-file <chemin>

lee el cuerpo de la petición desde un archivo

--list

muestra todos los métodos <famille>.<méthode> y sale

La respuesta JSON (o el flujo NDJSON para los endpoints *.list) se escribe en la salida estándar. En caso de error, el proceso termina con un código distinto de cero y la envoltura {"error":{…}} se imprime en la salida estándar para seguir siendo analizable (por ejemplo con jq).