Changelog

Unreleased

Ajouté

  • ./docker.sh eval-run : lance l'eval runner golden dataset depuis le conteneur Docker contre le webhook par défaut de la DB de production — nouvelle option --db <chemin> dans eval-runner.mjs (résolution du webhook défaut + envoi de l'apiKey en Authorization: Bearer), eval-runner.mjs ajouté à l'image Docker
  • Section « Évaluation qualité » dans le README : usage d'eval-runner (local + Docker, barème /6, vérification de source, pièges du mock) et d'eval-logs (votes, --export-candidates), workflow type
  • eval-runner.mjs : champ expected_source dans le golden dataset (wiki/bricozor/none) — le runner croise la source réelle du webhook (event meta) avec la source attendue et signale les écarts dans la console ([source:wiki→none]) et dans le CSV
  • Golden dataset : entrées génériques commande_suivi_generique_01 et sav_generique_01 pour les questions sans fournisseur nommé ; patterns des entrées Emuca/Holzmann resserrés sur le nom du fournisseur pour éviter les faux matches
  • ./app.sh deploy prod : déploiement en production via SSH — git pull + build Docker sans downtime (build pendant que l'ancien container tourne, swap rapide) ; paramétrable via DEPLOY_SSH_HOST et DEPLOY_APP_PATH dans .env
  • eval-logs.mjs intègre les votes : les conversations avec un pouce bas sont évaluées par le juge LLM même sans pattern dans le golden dataset ([VOTE-]), et une colonne vote (upvoted/downvoted/none) apparaît dans le CSV d'export
  • eval-logs.mjs --export-candidates <output.yaml> : détecte les questions récurrentes non couvertes par le golden dataset, les groupe par similarité Jaccard et génère un YAML annoté par LLM prêt à intégrer dans evals/golden_dataset.yaml
  • Footer de version dans la sidebar : « Développé avec ❤️ par le Studio Web Bricozor » + lien cliquable vers /changelog
  • Page /changelog (app/pages/changelog.vue) : affiche CHANGELOG.md rendu en markdown, sans sidebar (layout: false)
  • GET /api/changelog : route serveur qui lit et retourne CHANGELOG.md (async, 404 si absent)
  • CHANGELOG.md inclus dans l'image Docker de production (.dockerignore + Dockerfile)
  • GET /api/blob/[...pathname] — route de serving des fichiers uploadés en mode self-hosted (sans authentification, pathname non-devinable par construction)
  • Helper resolveBlobUrl (server/utils/blob.ts) : retourne l'URL fournie par le driver blob si disponible, sinon construit une URL absolue ${NUXT_PUBLIC_BASE_URL}/api/blob/<pathname>
  • Env var NUXT_PUBLIC_BASE_URL (défaut http://localhost:3000) — URL publique de Zorg, utilisée pour construire les URLs absolues des pièces jointes accessibles depuis n8n

Ajouté — Webhooks multi (assistants n8n)

  • Gestion de plusieurs assistants n8n (« webhooks ») nommés depuis l'UI admin (/settings), chaque chat associé à un webhook avec résolution dynamique (webhook du chat → défaut → 503 « Aucun assistant configuré »)
  • Table webhooks + colonne chats.webhookId (nullable, onDelete: set null) + relations + migration Drizzle — server/db/schema.ts
  • server/utils/webhooks.ts : resolveChatWebhook (résout le webhook d'un chat, fallback sur le défaut, sinon null) + applyDefaultFlag (bascule le défaut), avec tests unitaires
  • server/plugins/01.bootstrapWebhooks.ts : seed idempotent du 1er webhook défaut depuis les env vars au premier boot
  • POST /api/chats/[id] : résolution du webhook depuis la DB (au lieu des env vars)
  • API admin CRUD : GET/POST /api/admin/webhooks + PATCH/DELETE /api/admin/webhooks/[id] — unicité du défaut via transaction Drizzle ; refus de supprimer/retirer le défaut ou le dernier assistant
  • Section « Assistants » dans /settings + ModalWebhookEdit.vue (ajout/édition, toggle « par défaut »)

Ajouté — LLM settings UI

  • Provider LLM (modèle de génération des titres de conversation) configurable depuis l'UI admin au lieu des env vars
  • Table settings (clé/valeur) en DB — server/db/schema.ts
  • server/utils/llmSettings.ts : resolveLlmSettings (pure, avec tests), getLlmSettings / saveLlmSettings (DB avec fallback sur les env vars)
  • getTitleModel (server/utils/titleModel.ts) : lit la config LLM depuis la DB, avec fallback sur les env vars NUXT_TITLE_LLM_*
  • GET /api/admin/settings/llm + PUT /api/admin/settings/llm (admin) — lecture/mise à jour de la config ; apiKey absente du body = conservée (inchangée)
  • Section LLM dans /settings (provider google ou openai-compatible, modèle, clé API, base URL)

Modifié

  • Env vars NUXT_N8N_WEBHOOK_URL / NUXT_N8N_WEBHOOK_API_KEY : deviennent bootstrap-only — lues uniquement au 1er démarrage pour seed le webhook défaut, puis gérées depuis /settings. Le chemin chat ne lit plus que la DB.

Corrigé

  • 500 « crypto.randomUUID is not a function » côté client quand Zorg est servi en HTTP (contexte navigateur non sécurisé) : nouveau helper randomId (shared/utils/id.ts) qui retombe sur crypto.getRandomValues quand crypto.randomUUID est absent ; utilisé dans app/pages/index.vue et useFileUpload.ts
  • Les mutations de la page /settings (sauvegarde config LLM, toggle admin, création/édition de webhooks) échouaient en 403 « CSRF Token Mismatch » — le header CSRF (useCsrf) est désormais envoyé sur tous les appels, comme dans le reste de l'application
  • Génération du titre de conversation rendue non-bloquante — le chat continue normalement si le LLM est injoignable (host.docker.internal ou clé API absente)
  • Chemin DB par défaut de eval-logs.mjs corrigé (.data/db/sqlite.db au lieu de .data/hub/db.sqlite) — ./docker.sh eval-logs fonctionne sans --db
  • 99 erreurs lint stylistic corrigées dans scripts/eval-logs.mjs, scripts/eval-runner.mjs et app/pages/settings.vue — pipeline CI débloqué
  • En mode self-hosted (driver fs), le driver blob ne retournait pas d'URL — les pièces jointes étaient silencieusement perdues dans les messages et inaccessibles depuis n8n

Sécurité

  • Rejet des tentatives de path traversal (../, ..\\) sur la route /api/blob — hardening contre les encodages multi-couches (URL encoding, double encoding)
  • L'apiKey des webhooks n'est jamais retourné par l'API admin (omis des réponses GET/POST/PATCH ; un PATCH sans apiKey conserve l'ancienne valeur)

0.1.0 — Non publié

Ajouté

  • Application de chat Nuxt 4 connectée à un workflow n8n (streaming NDJSON)
  • Auth Google OAuth restreinte au domaine @bricozor.com
  • GET /api/chats — liste des conversations
  • POST /api/chats — création d'une conversation
  • GET /api/chats/[id] — détail d'une conversation avec ses messages
  • POST /api/chats/[id] — envoi d'un message, réponse en streaming depuis n8n
  • DELETE /api/chats/[id] — suppression d'une conversation et de ses messages
  • DELETE /api/chats/[id]/messages — suppression de tous les messages d'une conversation
  • PATCH /api/chats/[id]/title — renommage manuel d'une conversation
  • GET /api/chats/[id]/votes — récupère les votes sur les messages
  • POST /api/chats/[id]/votes — vote positif/négatif sur un message
  • PUT /api/upload/[chatId] — upload de fichiers (images, PDF, CSV ; max 10 Mo)
  • DELETE /api/upload/[...pathname] — suppression d'un fichier uploadé
  • Génération automatique du titre de conversation via LLM configurable (NUXT_TITLE_LLM_PROVIDER, modèle par défaut : Gemini 2.5 Flash Lite)
  • Base de données SQLite via Drizzle ORM, migrations automatiques au démarrage
  • Parser de stream n8n (server/utils/n8nStream.ts) avec tests unitaires (Vitest)
  • Script scripts/mock-n8n.mjs — faux webhook n8n local pour développer sans dépendre du vrai workflow
  • Support PWA (manifest, icônes)
  • Dockerfile et script docker.sh (build, start, restart, stop, logs, ssh)
  • Workflow CI GitHub Actions (lint, typecheck, tests)