Aller au contenu

Architecture Overview

Version française
Back to index


General presentation

Racines is a PWA (Progressive Web App) companion for learning African languages. The application is organized around three roles (Player, Speaker, Admin) with dedicated routes and APIs.


Architecture diagram

Internet
    │
    ▼
┌─────────────────────────────────────┐
│             Traefik                 │  ← Reverse proxy + TLS
│         (port 80/443)               │
└──────────────────┬──────────────────┘
                   │
                   ▼
┌─────────────────────────────────────┐
│          Next.js App                │  ← Port 3000
│    (App Router, Server Components)  │
│                                     │
│  ┌──────────┐  ┌────────────────┐  │
│  │  Pages   │  │  API Routes    │  │
│  │  /       │  │  /api/*        │  │
│  │  /[lang] │  │                │  │
│  │  /admin  │  │                │  │
│  │  /speaker│  │                │  │
│  └──────────┘  └───────┬────────┘  │
└───────────────────────┬┴────────────┘
                        │
           ┌────────────┴────────────┐
           ▼                         ▼
┌─────────────────┐       ┌─────────────────┐
│   PostgreSQL    │       │     MinIO        │
│   (Port 5432)   │       │  (Port 9000)     │
│                 │       │  Bucket: audios  │
│  languages      │       │  (public-read)   │
│  cards          │       └─────────────────┘
│  items          │               │
│  speakers       │               │ Public URL
│  admins         │               ▼
│  statistics     │     ┌─────────────────┐
│  contact_msgs   │     │  CDN / Client   │
│  quiz_sessions  │     │  (AUDIO_CDN_URL) │
└─────────────────┘     └─────────────────┘

Technology stack

Layer Technology Role
Framework Next.js 16 (App Router) SSR, API Routes, routing
UI React 19 + TypeScript Components, state
Styling Tailwind CSS 4 Utility styles
Database PostgreSQL 15 Application data
ORM / DB Client pg (node-postgres) Raw SQL queries with pool
Audio storage MinIO (S3-compatible) MP3 audio files
Audio processing FFmpeg (fluent-ffmpeg) Conversion, compression, normalization
Admin auth PBKDF2 + JWT (jose) Secure authentication
PWA Service Worker + IndexedDB v3 Offline mode
Charts Recharts Admin graphs
Excel import xlsx Parsing .xlsx files
Email Nodemailer Contact form
Reverse proxy Traefik TLS, routing, CORS

Main data flows

1. Read flow (player)

Player (browser)
    │
    ├─ SSR (Server Component) ──► PostgreSQL → cards + items
    │
    ├─ Client hydration
    │
    └─ Audio playback ──────────► MinIO CDN (AUDIO_CDN_URL) direct
                                  or fallback /api/audio/serve/[...path]

2. Audio upload flow (speaker)

Speaker (browser)
    │
    └─ POST /api/audio/upload
            │
            ├─ Speaker code verification (speakers table)
            ├─ FFmpeg processing (mono, 96kbps MP3)
            ├─ Upload to MinIO (bucket: audios)
            └─ UPDATE items SET audio_url = ... (PostgreSQL)

3. Offline flow (player)

Player installs the app
    │
    └─ Service Worker INSTALL
            │
            └─ PRECACHE_URLS (static assets)

Player downloads a language
    │
    └─ CACHE_PAGES message → Service Worker
            │
            ├─ Step 1: Ping /api/health/live
            ├─ Step 2: GET /api/languages/[id]/content → IndexedDB
            ├─ Step 3: Fetch audios → cache racines-audio-cache-v3
            ├─ Step 4: Orphan cleanup
            └─ Step 5: Cache HTML pages

Structural architecture decisions

No TTS (Text-to-Speech)

The application never uses synthetic voices. If an item has no native recording (audio_url IS NULL), the audio button is simply absent. Quality takes precedence over completeness.

Offline-first

Content is designed to work without a connection after download. The Service Worker is the cornerstone of this architecture.

Direct audio CDN

In production, audio is served directly from MinIO via AUDIO_CDN_URL — the Next.js server is not in the read path. The fallback proxy (/api/audio/serve/) is only used in development.

PostgreSQL with raw SQL

No ORM. Queries are written in native SQL via the pg pool. This provides full control over performance and complex aggregations.

Conditional Service Worker

The SW is disabled on /admin and /speaker to avoid cache conflicts in these stateful interfaces.


Source directory structure

src/
├── app/                        # Next.js App Router
│   ├── page.tsx                # Home page (/)
│   ├── layout.tsx              # Root layout (AUDIO_CDN_URL injection)
│   ├── [language]/             # Player pages
│   │   ├── page.tsx            # Language hub
│   │   ├── words/              # Word Challenge
│   │   ├── phrases/            # Phrase Challenge
│   │   └── revision/           # MCQ Revision
│   ├── admin/                  # Admin interface (JWT-protected)
│   ├── speaker/                # Speaker interface
│   ├── contact/                # Contact form
│   └── api/                    # API Routes
├── components/                 # React components
│   ├── admin/                  # Admin components
│   ├── revision/               # MCQ components
│   └── [shared]/               # Nav, Audio, Modals...
├── contexts/                   # React contexts
│   ├── LanguageContext.tsx      # Language unlocking, admin flag
│   └── SpeakerContext.tsx       # Speaker auth
├── hooks/                      # Custom hooks
│   └── useAuth.tsx             # Admin auth hook
├── lib/                        # Utilities and services
│   ├── postgres.ts             # PostgreSQL pool
│   ├── auth.ts                 # PBKDF2, JWT
│   ├── auth-middleware.ts      # Protection middleware
│   ├── audio-url.ts            # Audio URL generation
│   ├── minio-client.ts         # MinIO client
│   ├── offline-download.ts     # Offline download orchestration
│   ├── offline-data.ts         # Reading data from IndexedDB
│   ├── network-state.ts        # Network circuit breaker
│   ├── statistics.ts           # Event tracking
│   └── quizUtils.ts            # MCQ logic
├── types/                      # TypeScript types
│   └── index.ts                # All business types
└── data/
    └── quiz-distractors.ts     # MCQ distractor data

Next steps