Retour au Blog
tech/

Deal ex Machina : la stack technique

Carte concise, orientée ingénieur, des technologies qui font tourner le site Deal ex Machina : framework, données, IA, contenu, garde-fous qualité, et écarts staging / production.

Cet article donne la vue stack : ce que nous exécutons, comment les briques se branchent, et où regarder dans le dépôt. Pour les en-têtes de sécurité, le RGPD, la loi européenne sur l'IA, les objectifs Lighthouse et la feuille de route, voir Le site web est la démo — un récit CTO long format ; celui-ci correspond au schéma que l'on trace au tableau avant d'ouvrir le code.


1. Un codebase, deux formes de déploiement

L'application repose sur Next.js 16 (App Router), Node.js 20 LTS et TypeScript 5.9 en mode strict. La même source part en deux modes :

ModeUsage typiqueSortie
Serveur NodeStaging sur Koyeb (Docker)Serveur standalone (server.js), APIs dynamiques, chat en flux
Export statiqueProduction sur Cloudflare PagesRépertoire out/ avec NEXT_OUTPUT=export / CLOUDFLARE_PAGES=1 ; pages statiques et comportement client où pertinent

Ce découpage est volontaire : validation full-stack dans un conteneur, actifs adaptés au bord pour le site public. Les variables d'environnement et le mode de sortie sont documentés dans le dépôt ; le Dockerfile ne copie que .next/standalone, .next/static et public.


2. Architecture en couches (modèle mental)

flowchart TB
  subgraph client [Navigateur]
    R[React 19 + Tailwind + Radix]
    AUI[Assistant UI + AI SDK React]
  end
  subgraph edge [Hébergement]
    CF[Cloudflare Pages]
    KY[Koyeb + Docker]
  end
  subgraph app [Next.js App Router]
    API[Route Handlers /api/*]
    PAGES[Composants serveur + i18n]
  end
  subgraph data [Plan données]
    PG[(PostgreSQL)]
    DR[Drizzle ORM]
    SB[Supabase Auth]
  end
  subgraph ai [Plan IA]
    LLM[API LLM compatible OpenAI]
    RAG[RAG local BM25]
    SFT[Dataset SFT JSONL]
  end
  R --> PAGES
  AUI --> API
  PAGES --> edge
  API --> edge
  API --> DR
  DR --> PG
  API --> SB
  API --> LLM
  API --> RAG
  SFT -.->|"entraîne / ancre Wagmi"| LLM

Client : React 19, Tailwind CSS, primitives Radix, lucide-react, Zustand lorsque l'état local le justifie. next-intl pilote FR/EN depuis src/i18n/messages/ et le routage [locale].

Application : Les route handlers sous src/app/api/ implémentent le chat, le healthcheck, le statut LLM, le callback d'authentification, etc. La validation est Zod de bout en bout (environnement, corps de requête, schéma du contenu).

Données : PostgreSQL via Drizzle (drizzle/, scripts db:*). Supabase fournit l'authentification compatible SSR (OTP e-mail, etc.) et les cookies de session.

Contenu : Blog et textes longs en Markdown dans content/blog/, compilés au build avec Content Collections et remark-gfm — pas de parseur CMS côté client.

IA : Vercel AI SDK (ai, @ai-sdk/*) et Assistant UI pour le chat Wagmi. Le backend parle une API compatible OpenAI vers Ollama (double service CPU/GPU sur Koyeb ou Ollama local selon l'environnement). Un RAG léger type BM25 (local-rag.ts) ancre le petit modèle sur wagmi-skills.md et public/ai.txt. Un pipeline de supervised fine-tuning (scripts/generate-wagmi-sft-dataset.ts) produit datasets/wagmi-sft/*.jsonl ; les compteurs et étiquettes figurent dans metadata.json après régénération.


3. Surface d'API notable

Points d'intégration les plus utiles pour un lecteur technique :

  • POST /api/chat — Assistant en flux ; limites de débit, modération, modèles différenciés (anonyme vs authentifié), persistance optionnelle soumise au consentement.
  • GET /api/llm/status — Disponibilité du fournisseur pour l'UI et l'exploitation.
  • GET /api/health — Vivacité ; en production la réponse reste minimale.
  • Routes Supabase — Callback et session selon la configuration de l'app.

Le contact avec l'équipe passe par le chat (Wagmi), pas un formulaire séparé : la stack matérialise ce choix produit.


4. Qualité, tests et CI

SujetOutils
Lint / formatBiome (pas d'ESLint/Prettier dans ce dépôt)
Typestsc --noEmit
Unité + intégrationVitest + Testing Library + happy-dom
E2EPlaywright
Budget perfLighthouse CI (lhci, assertions dans lighthouserc.js)
Hooks Gitsimple-git-hooks + lint-staged (+ actionlint sur les workflows)

GitHub Actions enchaînent le déploiement staging (Docker Hub puis Koyeb sur dev), Lighthouse sur les PR, et un déploiement production manuel vers Cloudflare Pages. Les secrets restent dans la CI et les variables de plateforme — jamais dans le bundle.


5. Lien avec les données d'entraînement Wagmi

Le contenu du site ne sert pas qu'aux humains. Le générateur SFT ingère :

  • Les articles de blog (celui-ci une fois fusionné),
  • wagmi-skills.md et ai.txt,
  • Des notes Obsidian optionnelles lorsque OBSIDIAN_VAULT_PATH est défini et que les notes déclarent wagmi_sft: true dans le frontmatter (voir docs/OBSIDIAN_WAGMI_SFT.md).

Les lignes sont du JSONL au format chat, adapté à des outils comme Unsloth ; une régénération met à jour train.jsonl, eval.jsonl et metadata.json. Si vous souhaitez des fiches Obsidian ou du Q&R dérivé de cet article après relecture, il suffira d'ajouter les notes au coffre et de relancer le pipeline.


6. Références (entrées officielles)


Résumé : Node 20, TypeScript strict, Next.js 16 App Router, React 19, Tailwind + Radix, Drizzle + PostgreSQL, Supabase pour l'auth, Content Collections pour le blog, Vercel AI SDK + Assistant UI pour Wagmi, RAG BM25 pour le petit modèle, export SFT JSONL depuis le dépôt + Obsidian optionnel, Biome + Vitest + Playwright + Lighthouse CI, Docker/Koyeb en staging et Cloudflare Pages en production statique.