Files
la-voix-du-peuple/docs/DAT.md
T
pironantoine 213a67e612 Create documentation for project architecture, deployment, and usage
Add DAT, DEX, GITEA_TUTO, and WIKI markdown files to the docs directory, and update agent_assets_metadata.toml to include these new documents.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 923ae0e3-a363-4db8-b04a-e8baca2a1330
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 4bb1a658-d577-451e-965c-fa15e2c21ca9
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/8af7d2ec-2cc3-4ece-8af3-9f071488d072/923ae0e3-a363-4db8-b04a-e8baca2a1330/RusmVRz
Replit-Helium-Checkpoint-Created: true
2026-04-04 06:56:49 +00:00

218 lines
8.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Document d'Architecture Technique — La Voix du Peuple
**Version** : 1.0
**Date** : Avril 2026
**Statut** : En production (Replit), prêt pour auto-hébergement
---
## 1. Présentation générale
**La Voix du Peuple** est une plateforme civique permettant à des citoyens de soumettre des propositions politiques. Ces contributions sont :
1. Filtrées par un agent IA selon le droit international des droits humains
2. Synthétisées automatiquement en un résumé clair par thème
3. Affichées en temps réel à destination d'élus ou de décideurs
---
## 2. Architecture globale
```
┌─────────────────────────────────────────────────────────┐
│ Navigateur client │
│ React + Vite (TypeScript) │
└────────────────────────┬────────────────────────────────┘
│ HTTP/REST (JSON)
┌─────────────────────────────────────────────────────────┐
│ Reverse Proxy — Nginx / HAProxy │
│ (rate limiting, TLS, X-Forwarded-For) │
└────────────────────────┬────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ Backend API — Flask (Python) │
│ Port 8080 │
│ │
│ ┌──────────────┐ ┌─────────────┐ ┌──────────────┐ │
│ │ app.py │ │ ai_agent.py │ │ database.py │ │
│ │ (routes) │──▶│ (filtre + │ │ (PostgreSQL) │ │
│ │ │ │ synthèse) │ │ │ │
│ └──────────────┘ └──────┬──────┘ └──────┬───────┘ │
└─────────────────────────────┼───────────────┼──────────┘
│ │
▼ ▼
┌──────────────────┐ ┌──────────────┐
│ Mistral AI API │ │ PostgreSQL │
│ api.mistral.ai │ │ (base DB) │
└──────────────────┘ └──────────────┘
```
---
## 3. Composants
### 3.1 Frontend — React + Vite
| Élément | Détail |
|---------|--------|
| Framework | React 18 + TypeScript |
| Build | Vite 7 |
| Styles | Tailwind CSS + shadcn/ui |
| Routing | Wouter |
| État serveur | TanStack Query |
| Client API | `@workspace/api-client-react` (généré depuis OpenAPI) |
| Police | Bahnschrift (titres), Inter (corps) |
**Pages** :
- `/` — Page principale (formulaire + fil + synthèse)
- `/about` — À propos et fondements juridiques
- `/transparence` — Fonctionnement technique et données collectées
**Variables d'environnement** :
- `BASE_URL` — Préfixe de chemin (injecté par Vite)
- `PORT` — Port du serveur de développement (assigné par Replit)
---
### 3.2 Backend — Flask
| Élément | Détail |
|---------|--------|
| Framework | Flask 3 (Python 3.11+) |
| Serveur WSGI | Gunicorn (production) |
| CORS | flask-cors |
| Rate limiting | flask-limiter (5 req/min par IP sur POST /api/ideas) |
| ORM | psycopg2-binary (requêtes SQL directes) |
**Endpoints** :
| Méthode | Route | Description |
|---------|-------|-------------|
| `GET` | `/api/ideas` | Liste des contributions acceptées |
| `POST` | `/api/ideas` | Soumet une nouvelle contribution |
| `GET` | `/api/ideas/stats` | Statistiques (acceptées/refusées) |
| `GET` | `/api/synthesis` | Texte de synthèse actuel |
| `GET` | `/health` | Health check |
**Réponses** : JSON camelCase, statuts HTTP standard.
---
### 3.3 Agent IA
Deux appels distincts à l'API Mistral (compatible OpenAI SDK) :
#### Filtre de modération
- **Modèle** : `mistral-small-latest` (configurable via `FILTER_MODEL`)
- **Entrée** : Texte brut de la contribution
- **Sortie** : JSON `{"accepted": bool, "reason"?: string, "legal_basis"?: string}`
- **Référentiel** : DUDH, PIDCP, CEDH, Charte UE, CERD, Statut de Rome
- **Max tokens** : 300
#### Synthèse collective
- **Modèle** : `mistral-large-latest` (configurable via `SYNTHESIS_MODEL`)
- **Entrée** : Liste de toutes les contributions acceptées
- **Sortie** : Texte libre structuré par thèmes, destiné aux élus
- **Max tokens** : 1200
- **Déclencheur** : À chaque nouvelle contribution acceptée (asynchrone)
**Priorité de configuration du client IA** :
1. `MISTRAL_API_KEY``https://api.mistral.ai/v1`
2. `OPENAI_API_KEY` → API OpenAI standard
3. `AI_INTEGRATIONS_OPENAI_*` → Proxy Replit (intégration native)
---
### 3.4 Base de données — PostgreSQL
**Table `ideas`** :
| Colonne | Type | Description |
|---------|------|-------------|
| `id` | SERIAL PK | Identifiant |
| `content` | TEXT | Texte de la contribution |
| `author` | VARCHAR(100) | Pseudonyme (nullable) |
| `accepted` | BOOLEAN | Résultat du filtre |
| `rejection_reason` | TEXT | Motif de refus (nullable) |
| `legal_basis` | TEXT | Base légale du refus (nullable) |
| `created_at` | TIMESTAMPTZ | Horodatage de soumission |
**Table `synthesis`** :
| Colonne | Type | Description |
|---------|------|-------------|
| `id` | SERIAL PK | (toujours 1 — ligne unique) |
| `text` | TEXT | Dernier texte de synthèse |
| `idea_count` | INTEGER | Nombre de contributions intégrées |
| `updated_at` | TIMESTAMPTZ | Dernière mise à jour |
---
## 4. Variables d'environnement
| Variable | Obligatoire | Description |
|----------|-------------|-------------|
| `DATABASE_URL` | ✅ | URL PostgreSQL complète |
| `MISTRAL_API_KEY` | ✅ (ou OpenAI) | Clé API Mistral |
| `SESSION_SECRET` | ✅ | Secret Flask (sessions) |
| `FILTER_MODEL` | — | Modèle de filtrage (défaut : `mistral-small-latest`) |
| `SYNTHESIS_MODEL` | — | Modèle de synthèse (défaut : `mistral-large-latest`) |
| `OPENAI_API_KEY` | — | Alternative à Mistral |
| `MISTRAL_BASE_URL` | — | URL custom Mistral (défaut : `https://api.mistral.ai/v1`) |
---
## 5. Infrastructure de déploiement (auto-hébergement)
```
Internet ──▶ HAProxy (TLS, load balancing)
└──▶ Nginx (reverse proxy, rate limiting IP)
└──▶ Gunicorn × N workers (Flask)
└──▶ Fichiers statiques Vite (build)
PostgreSQL (local ou RDS)
```
**Fichiers fournis** :
- `deploy/nginx.conf` — Configuration Nginx avec HAProxy support
- `deploy/voix-du-peuple-api.service` — Unité systemd pour Gunicorn
- `artifacts/voix-du-peuple/vite.config.selfhost.ts` — Build sans plugins Replit
---
## 6. Sécurité
| Mesure | Implémentation |
|--------|----------------|
| Rate limiting | 5 POST/min par IP (flask-limiter) |
| Sanitisation | bleach sur le contenu avant stockage |
| CORS | Origines configurées explicitement |
| SQL injection | Requêtes paramétrées (psycopg2) |
| Secrets | Variables d'environnement uniquement, jamais dans le code |
| Données personnelles | Aucune IP stockée, pseudonyme facultatif |
---
## 7. Flux de traitement d'une contribution
```
POST /api/ideas
├─ Validation (longueur 101000 chars)
├─ Sanitisation (bleach)
├─ INSERT en base (statut pending)
├─ Appel Mistral (filtre)
│ ├─ accepted=true → UPDATE idea, déclenche synthèse async
│ └─ accepted=false → UPDATE idea avec motif
├─ [async] Appel Mistral (synthèse) sur toutes contributions acceptées
│ └─ UPSERT table synthesis
└─ Réponse JSON 201
```