Maintenance

Backup et recovery

Backup et recovery

Dernière mise à jour : 2026-05-10

⚠️ À COMPLÉTER

Aucune stratégie de backup applicative n'est implémentée dans le code (pas de script scripts/backup.sh, pas de cron dans src/cron/, pas de doc dans ARCHITECTURE.md / CONTRIBUTING.md).

La protection des données dépend actuellement entièrement de la stratégie Unraid native (RAID + parity + snapshots ZFS si activé sur le pool appdata).

À clarifier / décider :

Données à protéger

Quoi Volume Critique ?
SQLite (database.db) /mnt/user/appdata/boardgame-referee/data/database.db ~10-50 MB Élevé : users, jeux, questions, feedbacks
PDFs uploadés /mnt/user/appdata/boardgame-referee/pdfs/*.pdf 100 MB - quelques GB Élevé : irrécupérables si perdus (à moins d'avoir l'original)
PNG rendus /mnt/user/appdata/boardgame-referee/pdfs/images/ Quelques GB Faible : régénérables via pdftoppm
Caches JSON /mnt/user/appdata/boardgame-referee/data/{ocr,contexts,conflicts}-*-<slug>.json Quelques MB Moyen : régénérables via Claude (mais cher en quota)
Data sources cartes /mnt/user/appdata/boardgame-referee/data/{magic,lorcana,terraforming-mars,ark-nova}-cards/ Quelques GB Moyen : re-téléchargeables sauf TM/Ark Nova qui sont locaux statiques
Card image cache /mnt/user/appdata/boardgame-referee/data/card-images-cache/ Quelques GB Faible : régénérable sharp depuis CDN
Qdrant collections /mnt/user/appdata/boardgame-referee/qdrant/storage/ Quelques GB Élevé : ré-ingestion = qq heures + quota Claude
Clé SSH oracle /mnt/user/appdata/boardgame-referee/ssh/id_ed25519 <1 KB Critique : sans elle, plus d'oracle SSH
NPM config /mnt/user/appdata/nginx-proxy-manager/ Quelques MB Élevé : recréer manuellement long

Recommandations (à valider)

Niveau minimum

  1. SQLite : sqlite3 .backup quotidien vers un autre disque ou cloud
  2. PDFs : rsync vers un autre disque ou cloud (ils sont irrécupérables)
  3. Clé SSH oracle : copie sur un support sécurisé (Bitwarden chiffré ou USB)

Niveau confortable

  1. Qdrant snapshots : pris au moment des grosses ré-ingestions (cf. deploiement/rollback.md pour les commandes)
  2. Caches JSON : optionnel, ils valent juste ce qu'ils ont coûté en quota Claude
  3. NPM config : backup database.sqlite NPM quotidien

Snapshots Qdrant

Manuel pour l'instant :

# Snapshot d'une collection
curl -X POST http://192.168.10.100:6333/collections/<collection>/snapshots
# Renvoie un nom de snapshot

# Liste les snapshots
curl http://192.168.10.100:6333/collections/<collection>/snapshots

# Télécharger un snapshot pour archive
curl -O http://192.168.10.100:6333/collections/<collection>/snapshots/<snapshot-name>

Restore SQLite

# Stop le container
docker compose -f /mnt/user/appdata/boardgame-referee/docker-compose.yml stop app

# Restore depuis backup
cp /backup/path/database.db.bak /mnt/user/appdata/boardgame-referee/data/database.db
# Ou si WAL : restore aussi .db-wal et .db-shm

# Restart
docker compose -f /mnt/user/appdata/boardgame-referee/docker-compose.yml up -d app

Restore Qdrant

# Re-create la collection depuis le snapshot
curl -X PUT http://192.168.10.100:6333/collections/<collection>/snapshots/recover \
  -H "Content-Type: application/json" \
  -d '{"location": "<snapshot-url>"}'

Test de restore

⚠️ Une procédure de restore non testée n'existe pas. Quand tu as une stratégie de backup en place, force-toi à tester un restore au moins une fois par trimestre :

  1. Sur un container test sur Unraid avec un volume vide
  2. Restore SQLite + Qdrant
  3. Vérifier qu'on peut login + voir les jeux + reposer une question

Questions à toi (Thymon)

Mise à jour des dépendances

Mise à jour des dépendances

Dernière mise à jour : 2026-05-10

Politique : manuel

Pas de Renovate / Dependabot configurés. Mises à jour manuelles, en bloc, quand tu en as envie.

Pourquoi pas Renovate ?

Si tu changes d'avis : Renovate self-hosted via Docker, conf simple renovate.json à la racine.

Workflow de batch update

Backend

# 1. Voir les outdated
npm outdated

# 2. Patch updates (safe — bug fixes)
npm update

# 3. Minor / major sélectifs
npm install hono@latest
npm install drizzle-orm@latest
npm install zod@latest
npm install @types/node@latest

# 4. Vérifier
npm run build
npm test

# 5. Tester en dev
npm run dev
# Aller sur /play, poser quelques questions, vérifier RAG OK

# 6. Si tout OK : commit + push → CI build + push image → pull en prod
git add package.json package-lock.json
git commit -m "chore(deps): bump backend deps"
git push

Frontend

cd frontend
npm outdated
npm update

# Major upgrades
npm install vue@latest vue-router@latest pinia@latest
npm install tailwindcss@latest @tailwindcss/vite@latest
npm install marked@latest

npm run build
npm test
npm run dev
# Tester l'UI

cd ..
git add frontend/package.json frontend/package-lock.json
git commit -m "chore(deps): bump frontend deps"
git push

Cas spéciaux

@flesh-and-blood/cards + @flesh-and-blood/types

Ces deux packages contiennent les données cartes FAB. Mettre à jour = nouveau set FAB. Workflow détaillé : tcg-integrations/mettre-a-jour-cartes.md.

npm update @flesh-and-blood/cards @flesh-and-blood/types
# Build + push obligatoire pour que le container voie les nouvelles cartes

mana-font (frontend)

Webfont Andrew Gioia pour symboles MTG. Update rare (pas de nouveau symbole MTG depuis longtemps). Si nouvelle release :

cd frontend
npm install mana-font@latest
# Vérifier visuellement qu'aucun symbole existant ne casse

pdfjs-dist

Couche d'extraction PDF. Updates régulières mais souvent breaking. Tester sur quelques PDFs de jeux différents avant de push.

better-sqlite3

Driver SQLite synchrone. Update prudemment — il y a souvent des prebuilds Node-version-specific. Si update casse en CI :

Audit de sécurité

npm audit
cd frontend && npm audit

Souvent du bruit (vulnérabilités CVSS bas dans des transitive deps). Filtrer :

npm audit --audit-level=high

Update Node 22 → 23 / 24

Pas urgent. Mais quand tu décides :

  1. Modifier engines.node dans package.json
  2. Modifier le base image dans Dockerfile (FROM node:24-bookworm-slim)
  3. Modifier le base image dans .gitea/workflows/build.yml (container test)
  4. npm ci localement avec Node 24, vérifier que tout compile / test

Pas de major version pinning

package.json utilise ^ partout (= compatible minor). Pas de pinning strict. Si tu veux pin une version pour reproduire un bug :

npm install hono@4.7.6 --save-exact

--save-exact retire le ^.

Logs

Logs

Dernière mise à jour : 2026-05-10

Logger central

Tous les logs serveur passent par src/lib/logger.ts. Conventions :

Locations

Rotation

Géré par entrypoint.sh au boot :

Pas de rotation runtime (only at boot). Si tu fais beaucoup de DEBUG en prod, pense à restart le container ou logrotate manuellement.

Filtrer

# Live tail
docker exec boardgame-referee tail -f /app/data/logs/server.log

# Live tail avec scope filter (jq pour le JSON)
docker exec boardgame-referee tail -f /app/data/logs/server.log | jq 'select(.scope == "rag")'

# Grep classique
docker exec boardgame-referee grep "claude-quota" /app/data/logs/server.log | tail -50

# Toutes les erreurs des 24 dernières heures
docker exec boardgame-referee grep -E '"level":"error"' /app/data/logs/server.log | tail -100

Niveaux par défaut

Environnement LOG_LEVEL
Dev debug
Prod info

Pour passer en debug en prod (temporaire) :

# Modifier .env Unraid : LOG_LEVEL=debug
# Restart container :
docker compose -f /mnt/user/appdata/boardgame-referee/docker-compose.yml restart app

⚠️ N'oublie pas de remettre en info après — debug est verbeux et remplit vite les 50 Mo.

Logs notables à surveiller

Pattern Sens
[claude-quota] ClaudeQuotaError Pause auto enclenchée, vérifier resetAt
[ssh] command rejected Wrapper oracle a bloqué une commande (debug /tmp/oracle-debug.log si nécessaire)
[ingest] stage error Stage d'ingestion en échec
[meta-sync] purge Purge hebdo des chunks méta périmés
[forum-sync] Cron forums BGG (3h du matin par défaut)
[hyde] timeout HyDE Haiku a dépassé 25s, fallback question brute
[contextual-cache] flushed Cache JSON sauvé sur disque (avant quota error ou checkpoint)
[rag] retrieve Diagnostics retrieval (timings, scores) en debug

Pas de log aggregation externe

Pas de Loki / Grafana / ELK / Sentry actuellement. Tout reste dans le fichier Unraid + Docker logs.

Si un jour tu veux ajouter Sentry ou un log shipper :

Debug d'une question

Pour relire les diagnostics d'une question donnée (lecture seule) :

docker exec boardgame-referee npm run debug:question -- --id <questionId>

Affiche : question, réponse, chunks retrouvés, HyDE, timings — équivalent CLI du panneau /admin/feedback/<id>.

Monitoring

Monitoring

Dernière mise à jour : 2026-05-10

État actuel : minimal

L'app expose un seul endpoint health, qui sert au healthcheck Docker. Pas de Prometheus / Grafana / OpenTelemetry / Sentry actuellement.

GET /api/health

{
  "status": "ok",
  "timestamp": "2026-05-10T12:34:56.789Z"
}

Public (pas d'auth). Utilisé par le healthcheck Docker (docker-compose.yml) :

healthcheck:
  test: ["CMD", "wget", "-qO-", "http://localhost:3000/api/health"]
  interval: 30s
  timeout: 5s
  retries: 3
  start_period: 10s

Si 3 échecs consécutifs → container marqué unhealthy. Pas de restart auto en place — c'est manuel.

GET /api/admin/health (admin only)

Plus riche, utilisé par la page /admin :

{
  "qdrant": { "ok": true, "collections": 14, "stats": {...} },
  "tei": { "ok": true, "model": "BAAI/bge-m3" },
  "reranker": { "ok": true },
  "claude_ssh": { "ok": true, "latency_ms": 5234 },
  "smtp": { "ok": true, "configured": true },
  "stats": {
    "totalGames": 23,
    "totalQuestions": 542,
    "totalUsers": 3
  }
}

Si tu vois Qdrant/TEI/Reranker ok: false, le RAG est cassé. Investigation :

Ajouter un monitoring externe

Suggestions (non implémentées) :

Uptime Kuma

Prometheus + Grafana

Sentry

Alerting

Aucun alerting actuellement. Si l'app meurt à 3h du matin, tu le vois quand tu poses ta première question le lendemain.

Pour ajouter du basique :

Métriques applicatives à logger

Si tu veux instrumenter sans monitoring formel :

Ensuite tu peux faire grep + jq sur /app/data/logs/server.log pour des stats ad hoc.

Troubleshooting

Troubleshooting

Dernière mise à jour : 2026-05-10

Pièges connus déjà rencontrés, à actualiser au fil des incidents.

Build / CI

Le job test échoue avec une erreur better-sqlite3

vue-tsc --noEmit passe en local mais foire en CI

Backend runtime

Boolean("false") === true — env var qui ne fait pas ce qu'on attend

process.env.X retourne undefined en prod

Une regex \b ne match pas un nom finissant par é

Claude répond "je ne vois aucune image"

ClaudeQuotaError non détectée → "answer empty" générique

Retrieval / RAG

Citation [BASE p.4 : "..."] ouvre la mauvaise page

Image PNG envoyée à Claude mais c'est la page 4 du mauvais livret

Claude cite un livret en [EXT p.4] au lieu de [Heavy Rain, p.4]

Reranker retourne ~0 sur du contenu technique abstrait → enterre le bon chunk

Frontend

display: flex dans une CSS scoped écrase le hidden lg:flex Tailwind

Card autocomplete @ ne matche pas un nom japonais / accentué

CardZoomModal ne s'ouvre pas après reload

SSE casse à 60s pile et UI affiche network error

Infra

TEI répond 000 (curl no response)

Container oracle SSH timeout

Wrapper oracle rejette toutes les commandes

Logs spam "EADDRINUSE :::3000"

BDD

Migration Drizzle qui ne s'applique pas

database.db is locked