Aller au contenu

Charte du projet et décisions d'architecture

English version
Retour au sommaire


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/ quand AUDIO_CDN_URL est défini
  • true en production, false en développement local sans CDN externe
  • Si true et AUDIO_CDN_URL vide → 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.sh le 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.onLine seul est peu fiable (WiFi connecté mais internet coupé → true)
  • États : CLOSEDOPENHALF_OPENCLOSED
  • Bascule en OPEN après 3 échecs dans une fenêtre de 60s
  • Récupération : événement window.online (probe immédiate) + probe périodique HEAD /api/health/live avec 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éussie
  • recordFetchFailure() 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 envoie Vary: Accept-Encoding qui casse le cache.match())
  • Utiliser cache.match(url, { ignoreVary: true }) pour les lectures

IndexedDB — version courante : 3

  • Store items : index by_card_id ajouté en v3 (migration dans openDB())
  • 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

  1. IndexedDB : audio téléchargé localement (offline-first)
  2. CDN MinIO (AUDIO_CDN_URL) : audio servi directement depuis le CDN
  3. Proxy Next.js (/api/audio/serve/) : fallback en développement ou si CDN indisponible
  4. ~~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
  • 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 depuis src/app/apple-icon.png
  • NEXT_PUBLIC_PWA_ENABLED=true doit ê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 /admin et /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'any sans 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.


Retour au sommaire