Aller au contenu

Project Charter and Architecture Decisions

Version française
Back to index


This document formalizes the structural architecture decisions of the Racines project. These rules must not be circumvented without prior discussion with the team.


Environment variables — critical rules

AUDIO_CDN_URL (mandatory at runtime)

  • Full URL of the MinIO audio CDN — container environment variable (no more --build-arg)
  • Read by RootLayout (Server Component, force-dynamic) on every SSR request
  • Injected into window.__RACINES_CDN__ as the first <script> in <head>, before any client chunk
  • The Docker entrypoint (docker/entrypoint.sh) fails if this variable is empty at container startup
  • Never leave it empty: getAudioUrl() would return '', causing spurious requests to the current page
  • Example: AUDIO_CDN_URL=https://racines-s3.id2real.net/audios

AUDIO_PROXY_DISABLED (runtime, default: false)

  • Disables the /api/audio/serve/ fallback proxy when AUDIO_CDN_URL is set
  • true in production, false in local development without an external CDN
  • If true and AUDIO_CDN_URL is empty → empty audio URL → silent bug. Both variables must be consistent.

AUDIO_CDN_DOMAIN (runtime, auto-derived)

  • Hostname extracted from AUDIO_CDN_URL, used in the CSP (src/proxy.ts)
  • If absent, docker/entrypoint.sh derives it automatically
  • Can be provided explicitly to override the derivation

.app_env file format (Docker Compose)

  • All values must be in double quotes: VAR="value"
  • Mandatory for values containing special characters (hash #, spaces, =, etc.)

Offline architecture — intentional behaviors

Network circuit breaker (src/lib/network-state.ts)

  • navigator.onLine alone is unreliable (WiFi connected but internet down → true)
  • States: CLOSEDOPENHALF_OPENCLOSED
  • Switches to OPEN after 3 failures in a 60s window
  • Recovery: window.online event (immediate probe) + periodic probe HEAD /api/health/live with exponential backoff 30s→300s
  • HALF_OPEN: the probe succeeded but recovery is only confirmed by the first successful real request
  • recordFetchFailure() only for network errors and codes 502/503/504. 4xx and 500 do not mean the server is unreachable

Audio cache (racines-audio-cache-v3)

  • Responses are stored without the Vary header (the CDN sends Vary: Accept-Encoding which breaks cache.match())
  • Use cache.match(url, { ignoreVary: true }) for reads

IndexedDB — current version: 3

  • items store: by_card_id index added in v3 (migration in openDB())
  • Any addition of a store or index must increment the version and handle onupgradeneeded

Audio — priority rule

The application never uses TTS (Text-to-Speech). If no native audio exists, the button is absent.

Audio playback priority order

  1. IndexedDB: locally downloaded audio (offline-first)
  2. MinIO CDN (AUDIO_CDN_URL): audio served directly from the CDN
  3. Next.js proxy (/api/audio/serve/): fallback in development or if CDN is unavailable
  4. ~~TTS~~: never used

Condition for displaying the audio button

The 🔊 button is displayed only if item.audio_url is non-null in the database. A TTS recording is not an acceptable alternative.


UI patterns — user feedback

States and notifications must always be visible without scrolling.

Sticky banner — persistent state

  • Usage: offline mode, update available
  • Existing implementation: src/components/ServiceWorkerRegistration.tsx
  • Fixed position below the header, permanently visible
  • Do not use for ephemeral messages

Toast — ephemeral confirmation

  • Usage: success of a user action (e.g. contact form submitted)
  • position: fixed, corner or bottom of screen, disappears after ~15s
  • Must include a manual close button (×)
  • Existing implementation: src/app/contact/page.tsx
  • Usage: destructive confirmation, blocking error
  • Avoid for informational messages — prefer toast or banner

PWA / Application icon

Rules

  • The file public/apple-touch-icon.png (180×180 px) must always be present
  • Do not add a manual <link rel="apple-touch-icon"> tag — Next.js generates it from src/app/apple-icon.png
  • NEXT_PUBLIC_PWA_ENABLED=true must be set in production
  • Any icon modifications must be verified by installing the application on iOS and Android

Service Worker — rules

  • The SW is disabled on /admin and /speaker (avoids cache conflicts in these stateful interfaces)
  • Admin pages are never cached by the SW
  • After each deployment, the SW automatically detects the new version and displays an update banner

Code conventions

TypeScript

  • No any without a commented justification
  • Types centralized in src/types/index.ts
  • Server Components by default — 'use client' only when necessary

SQL

  • No string concatenation in queries — always use parameters ($1, $2…)
  • Shared pool via src/lib/postgres.ts — never a direct new connection

Logs

  • Never log sensitive user data (emails, names, message content)
  • Server errors are logged with stack traces but without personal data

This document is authoritative

In case of doubt between this document and a code comment, this document takes precedence. If a rule is circumvented for a valid reason, document it in the code and in this file.


Back to index