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.
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 :
| Mode | Usage typique | Sortie |
|---|---|---|
| Serveur Node | Staging sur Koyeb (Docker) | Serveur standalone (server.js), APIs dynamiques, chat en flux |
| Export statique | Production sur Cloudflare Pages | Ré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.
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.
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.Le contact avec l'équipe passe par le chat (Wagmi), pas un formulaire séparé : la stack matérialise ce choix produit.
| Sujet | Outils |
|---|---|
| Lint / format | Biome (pas d'ESLint/Prettier dans ce dépôt) |
| Types | tsc --noEmit |
| Unité + intégration | Vitest + Testing Library + happy-dom |
| E2E | Playwright |
| Budget perf | Lighthouse CI (lhci, assertions dans lighthouserc.js) |
| Hooks Git | simple-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.
Le contenu du site ne sert pas qu'aux humains. Le générateur SFT ingère :
wagmi-skills.md et ai.txt,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.
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.