Charte du projet et décisions d'architecture
Ce document formalise les décisions d'architecture structurantes du projet Racines. Ces règles ne doivent pas être contournées sans discussion préalable avec l'équipe.
Variables d'environnement — règles critiques
AUDIO_CDN_URL (obligatoire au runtime)
- URL complète du CDN audio MinIO — variable d'environnement du container (plus de
--build-arg) - Lue par
RootLayout(Server Component,force-dynamic) à chaque requête SSR - Injectée dans
window.__RACINES_CDN__comme premier<script>de<head>, avant tout chunk client - L'entrypoint Docker (
docker/entrypoint.sh) échoue si cette variable est vide au démarrage du container - Ne jamais la laisser vide :
getAudioUrl()retournerait'', ce qui provoque des requêtes parasites vers la page courante - Exemple :
AUDIO_CDN_URL=https://racines-s3.id2real.net/audios
AUDIO_PROXY_DISABLED (runtime, défaut: false)
- Désactive le fallback proxy
/api/audio/serve/quandAUDIO_CDN_URLest défini trueen production,falseen développement local sans CDN externe- Si
trueetAUDIO_CDN_URLvide → URL audio vide → bug silencieux. Les deux variables doivent être cohérentes.
AUDIO_CDN_DOMAIN (runtime, dérivé automatiquement)
- Hostname extrait de
AUDIO_CDN_URL, utilisé dans la CSP (src/proxy.ts) - Si absent,
docker/entrypoint.shle dérive automatiquement - Peut être fourni explicitement pour surcharger la dérivation
Format du fichier .app_env (Docker Compose)
- Toutes les valeurs doivent être entre double quotes :
VAR="valeur" - Obligatoire pour les valeurs contenant des caractères spéciaux (hash
#, espaces,=, etc.)
Architecture offline — comportements intentionnels
Circuit breaker réseau (src/lib/network-state.ts)
navigator.onLineseul est peu fiable (WiFi connecté mais internet coupé →true)- États :
CLOSED→OPEN→HALF_OPEN→CLOSED - Bascule en
OPENaprès 3 échecs dans une fenêtre de 60s - Récupération : événement
window.online(probe immédiate) + probe périodiqueHEAD /api/health/liveavec backoff exponentiel 30s→300s HALF_OPEN: la probe a réussi mais la récupération n'est confirmée que par la première vraie requête réussierecordFetchFailure()uniquement pour erreurs réseau et codes 502/503/504. Les 4xx et 500 ne signifient pas que le serveur est injoignable
Cache audio (racines-audio-cache-v3)
- Les réponses sont stockées sans le header
Vary(le CDN envoieVary: Accept-Encodingqui casse lecache.match()) - Utiliser
cache.match(url, { ignoreVary: true })pour les lectures
IndexedDB — version courante : 3
- Store
items: indexby_card_idajouté en v3 (migration dansopenDB()) - Tout ajout de store ou d'index doit incrémenter la version et gérer le
onupgradeneeded
Audio — règle de priorité
L'application n'utilise jamais de TTS (Text-to-Speech). Si un audio natif n'existe pas, le bouton est absent.
Ordre de priorité de lecture audio
- IndexedDB : audio téléchargé localement (offline-first)
- CDN MinIO (
AUDIO_CDN_URL) : audio servi directement depuis le CDN - Proxy Next.js (
/api/audio/serve/) : fallback en développement ou si CDN indisponible - ~~TTS~~ : jamais utilisé
Condition pour afficher le bouton audio
Le bouton 🔊 s'affiche uniquement si item.audio_url est non nul en base de données. Un enregistrement TTS n'est pas une alternative acceptable.
Patterns UI — feedback utilisateur
Les états et notifications doivent toujours être visibles sans scroll.
Sticky banner — état persistant
- Usage : mode offline, mise à jour disponible
- Implémentation existante :
src/components/ServiceWorkerRegistration.tsx - Position fixe sous le header, visible en permanence
- Ne pas utiliser pour des messages éphémères
Toast — confirmation éphémère
- Usage : succès d'une action utilisateur (ex : formulaire de contact soumis)
position: fixed, coin ou bas d'écran, disparaît après ~15s- Doit inclure un bouton de fermeture manuel (×)
- Implémentation existante :
src/app/contact/page.tsx
Modal / Dialog — action critique
- Usage : confirmation destructive, erreur bloquante
- À éviter pour les messages informatifs — préférer toast ou banner
PWA / Icône de l'application
Règles
- Le fichier
public/apple-touch-icon.png(180×180 px) doit toujours être présent - Ne pas ajouter de balise
<link rel="apple-touch-icon">manuelle — Next.js la génère depuissrc/app/apple-icon.png NEXT_PUBLIC_PWA_ENABLED=truedoit être défini en production- Toute modification des icônes doit être vérifiée en installant l'application sur iOS et Android
Service Worker — règles
- Le SW est désactivé sur
/adminet/speaker(évite les conflits de cache dans ces interfaces à état) - Les pages admin ne sont jamais mises en cache par le SW
- Après chaque déploiement, le SW détecte automatiquement la nouvelle version et affiche une bannière de mise à jour
Conventions de code
TypeScript
- Pas d'
anysans justification commentée - Types centralisés dans
src/types/index.ts - Server Components par défaut —
'use client'seulement si nécessaire
SQL
- Pas de concaténation de chaînes dans les requêtes — toujours des paramètres (
$1,$2…) - Pool partagé via
src/lib/postgres.ts— jamais de nouvelle connexion directe
Logs
- Jamais de données utilisateur sensibles dans les logs (emails, noms, contenu des messages)
- Les erreurs serveur sont loggées avec stack trace mais sans données personnelles
Ce document fait autorité
En cas de doute entre ce document et un commentaire dans le code, ce document a autorité. Si une règle est contournée pour une raison valide, documentez-le dans le code et dans ce fichier.