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
This commit is contained in:
pironantoine
2026-04-04 06:56:49 +00:00
parent e2a0b6401d
commit 213a67e612
5 changed files with 727 additions and 0 deletions
+24
View File
@@ -6,3 +6,27 @@ id = "x31gBYdQKnMGZriV7IJ1v"
uri = "file://DEPLOIEMENT.md" uri = "file://DEPLOIEMENT.md"
type = "text" type = "text"
title = "Guide d'auto-hébergement RockyLinux" title = "Guide d'auto-hébergement RockyLinux"
[[outputs]]
id = "QwHkF1SFkRpZFuyRMNI9h"
uri = "file://docs/GITEA_TUTO.md"
type = "text"
title = "Tutoriel — Récupérer le projet sur Gitea"
[[outputs]]
id = "wdBpdE1lSme8lM2xYd3oJ"
uri = "file://docs/DAT.md"
type = "text"
title = "DAT — Document d'Architecture Technique"
[[outputs]]
id = "NXFvDFOIzX862xNq15Mak"
uri = "file://docs/DEX.md"
type = "text"
title = "DEX — Document d'Exploitation"
[[outputs]]
id = "kJNXgVnYp_LQmPcWr6Osb"
uri = "file://docs/WIKI.md"
type = "text"
title = "Wiki — La Voix du Peuple"
+217
View File
@@ -0,0 +1,217 @@
# 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
```
+244
View File
@@ -0,0 +1,244 @@
# Document d'Exploitation — La Voix du Peuple
**Version** : 1.0
**Date** : Avril 2026
---
## 1. Démarrage et arrêt des services
### Sur Replit
Les services sont gérés par les **Workflows** Replit. Ils démarrent automatiquement.
| Workflow | Commande | Port |
|----------|----------|------|
| API Server | `PORT=8080 sh artifacts/flask-api/start.sh` | 8080 |
| Frontend | `pnpm --filter @workspace/voix-du-peuple run dev` | auto |
Pour redémarrer manuellement : onglet **Workflows** → bouton restart.
---
### En auto-hébergement (RockyLinux / Debian)
```bash
# Démarrer le backend
systemctl start voix-du-peuple-api
# Arrêter le backend
systemctl stop voix-du-peuple-api
# Redémarrer après mise à jour
systemctl restart voix-du-peuple-api
# Voir le statut
systemctl status voix-du-peuple-api
# Activer au démarrage
systemctl enable voix-du-peuple-api
```
```bash
# Redémarrer Nginx après modification de la config
nginx -t && systemctl reload nginx
```
---
## 2. Variables d'environnement
Fichier de référence : `.env.example` à la racine du projet.
En production, créer `/etc/voix-du-peuple/.env` :
```bash
# Base de données
DATABASE_URL=postgresql://user:password@localhost:5432/voix_du_peuple
# IA — choisir l'un des deux
MISTRAL_API_KEY=sk-...
# OPENAI_API_KEY=sk-...
# Modèles (optionnel — valeurs par défaut ci-dessous)
FILTER_MODEL=mistral-small-latest
SYNTHESIS_MODEL=mistral-large-latest
# Sécurité Flask
SESSION_SECRET=une-longue-chaine-aleatoire-securisee
```
> **Important** : Après toute modification du fichier `.env`, redémarrer le service API.
---
## 3. Mise à jour du code
```bash
# Depuis votre serveur, après un git pull
cd /opt/voix-du-peuple
git pull gitea main
# Mettre à jour les dépendances Python si requirements.txt a changé
pip install -r artifacts/flask-api/requirements.txt
# Reconstruire le frontend si le code frontend a changé
pnpm install
pnpm --filter @workspace/voix-du-peuple run build --config vite.config.selfhost.ts
# Redémarrer l'API
systemctl restart voix-du-peuple-api
```
---
## 4. Consultation des logs
### Sur Replit
Voir les logs dans l'onglet **Workflows** → cliquer sur le workflow concerné.
### En auto-hébergement
```bash
# Logs du service systemd (temps réel)
journalctl -u voix-du-peuple-api -f
# 100 dernières lignes
journalctl -u voix-du-peuple-api -n 100
# Logs d'une journée spécifique
journalctl -u voix-du-peuple-api --since "2026-04-01" --until "2026-04-02"
# Logs Nginx
tail -f /var/log/nginx/access.log
tail -f /var/log/nginx/error.log
```
---
## 5. Base de données
### Connexion directe
```bash
psql "$DATABASE_URL"
# ou
psql -U voix_user -d voix_du_peuple -h localhost
```
### Requêtes utiles
```sql
-- Nombre de contributions par statut
SELECT accepted, COUNT(*) FROM ideas GROUP BY accepted;
-- Dernières contributions
SELECT id, author, left(content, 60), accepted, created_at
FROM ideas ORDER BY created_at DESC LIMIT 10;
-- Contributions refusées avec motif
SELECT id, left(content, 60), rejection_reason, created_at
FROM ideas WHERE accepted = false ORDER BY created_at DESC;
-- État de la synthèse
SELECT idea_count, left(text, 200), updated_at FROM synthesis;
```
### Sauvegarde
```bash
# Dump complet
pg_dump "$DATABASE_URL" > backup_$(date +%Y%m%d_%H%M).sql
# Restauration
psql "$DATABASE_URL" < backup_20260401_0800.sql
```
---
## 6. Vérification du bon fonctionnement
```bash
# Health check API
curl http://localhost:8080/health
# Test de soumission
curl -X POST http://localhost:8080/api/ideas \
-H "Content-Type: application/json" \
-d '{"content": "Test de fonctionnement de la plateforme.", "author": "Admin"}'
# Lecture de la synthèse
curl http://localhost:8080/api/synthesis | python3 -m json.tool
# Statistiques
curl http://localhost:8080/api/ideas/stats
```
Réponse attendue du health check :
```json
{"status": "ok"}
```
---
## 7. Changement de modèle IA
Pour basculer vers un modèle différent sans toucher au code :
```bash
# Exemple : passer à mistral-medium
# Éditer le .env
FILTER_MODEL=mistral-medium-latest
SYNTHESIS_MODEL=mistral-medium-latest
# Redémarrer
systemctl restart voix-du-peuple-api
```
Modèles Mistral disponibles (avril 2026) :
- `mistral-small-latest` — Rapide, économique (filtre)
- `mistral-medium-latest` — Équilibré
- `mistral-large-latest` — Meilleure qualité (synthèse)
---
## 8. Purge des données
```sql
-- Supprimer toutes les contributions (irréversible)
TRUNCATE ideas;
TRUNCATE synthesis;
-- Supprimer uniquement les contributions refusées
DELETE FROM ideas WHERE accepted = false;
-- Forcer une re-synthèse (supprimer le cache)
DELETE FROM synthesis;
```
Après une purge, la synthèse se régénère automatiquement à la prochaine contribution acceptée.
---
## 9. Dépannage courant
| Symptôme | Cause probable | Solution |
|----------|---------------|----------|
| `Service temporairement indisponible` sur soumission | Clé Mistral invalide ou quota dépassé | Vérifier `MISTRAL_API_KEY`, consulter console.mistral.ai |
| Synthèse non mise à jour | Erreur silencieuse en arrière-plan | Consulter les logs, vérifier la connexion à l'API IA |
| Erreur 500 sur toutes les routes | `DATABASE_URL` incorrect ou PostgreSQL arrêté | `systemctl status postgresql`, vérifier le `.env` |
| Frontend vide (page blanche) | Build manquant ou `BASE_URL` incorrect | Relancer le build Vite, vérifier la config Nginx |
| Rate limit atteint (429) | Trop de soumissions depuis la même IP | Normal — attendre 1 minute |
| Modèle IA introuvable (404) | Nom de modèle incorrect dans `FILTER_MODEL` | Corriger le nom, redémarrer le service |
---
## 10. Contacts et ressources
- Documentation Mistral : https://docs.mistral.ai
- PostgreSQL : https://www.postgresql.org/docs/
- Flask : https://flask.palletsprojects.com
- Guide de déploiement complet : `DEPLOIEMENT.md`
- Architecture : `docs/DAT.md`
+156
View File
@@ -0,0 +1,156 @@
# Récupérer le projet sur Gitea
Ce tutoriel explique comment pousser le code de **La Voix du Peuple** depuis Replit vers votre instance Gitea, et comment vous synchroniser ensuite à votre rythme.
---
## Prérequis
- Une instance Gitea accessible (ex. `https://git.mondomaine.fr`)
- Un compte avec les droits de création de dépôt
- Git installé localement (si vous travaillez aussi depuis votre poste)
---
## Étape 1 — Créer le dépôt sur Gitea
1. Connectez-vous à votre Gitea
2. Cliquez sur **+ New Repository**
3. Nom suggéré : `voix-du-peuple`
4. Laissez-le **vide** (pas de README, pas de .gitignore)
5. Notez l'URL du dépôt — elle ressemble à :
- SSH : `git@git.mondomaine.fr:vous/voix-du-peuple.git`
- HTTPS : `https://git.mondomaine.fr/vous/voix-du-peuple.git`
---
## Étape 2 — Ajouter Gitea comme remote depuis Replit
Dans le shell Replit (onglet **Shell**) :
```bash
# Vérifier les remotes actuels
git remote -v
# Ajouter votre Gitea comme remote (choisissez SSH ou HTTPS)
git remote add gitea git@git.mondomaine.fr:vous/voix-du-peuple.git
# ou en HTTPS :
git remote add gitea https://git.mondomaine.fr/vous/voix-du-peuple.git
# Vérifier
git remote -v
```
---
## Étape 3 — Configurer l'authentification
### Option A — SSH (recommandé)
Générez une clé SSH dans le shell Replit si vous n'en avez pas :
```bash
ssh-keygen -t ed25519 -C "replit-voix-du-peuple"
cat ~/.ssh/id_ed25519.pub
```
Copiez la clé publique affichée et ajoutez-la dans Gitea :
**Paramètres → Clés SSH → Ajouter une clé**
Testez la connexion :
```bash
ssh -T git@git.mondomaine.fr
```
### Option B — HTTPS avec token
Dans Gitea : **Paramètres → Applications → Générer un token**
Utilisez l'URL avec token :
```bash
git remote set-url gitea https://VOTRE_TOKEN@git.mondomaine.fr/vous/voix-du-peuple.git
```
---
## Étape 4 — Pousser le code
```bash
# Premier push (établit le tracking)
git push -u gitea main
# Pour les push suivants
git push gitea main
```
---
## Étape 5 — Récupérer les mises à jour depuis Replit
Chaque fois que vous voulez synchroniser votre Gitea avec l'état actuel du projet Replit :
```bash
# Depuis le shell Replit
git push gitea main
```
Si vous avez également modifié des fichiers directement sur Gitea :
```bash
git pull gitea main --rebase
git push gitea main
```
---
## Étape 6 — Cloner depuis Gitea sur votre serveur de production
Sur votre serveur RockyLinux / Debian :
```bash
git clone git@git.mondomaine.fr:vous/voix-du-peuple.git
cd voix-du-peuple
# Copier et adapter les variables d'environnement
cp .env.example .env
nano .env # Renseignez DATABASE_URL, MISTRAL_API_KEY, SESSION_SECRET
# Suivre ensuite le guide DEPLOIEMENT.md
```
---
## Résumé des commandes utiles
```bash
# Voir l'état du dépôt
git status
git log --oneline -10
# Synchroniser vers Gitea
git push gitea main
# Récupérer depuis Gitea
git pull gitea main
# Voir les remotes configurés
git remote -v
```
---
## Structure des branches
| Branche | Usage |
|---------|-------|
| `main` | Code de production, stable |
| `replit-agent` | Branche de travail de l'agent Replit (interne) |
> Il est conseillé de ne travailler que sur `main` et de ne jamais pousser `replit-agent` vers votre Gitea.
```bash
# Pousser uniquement main
git push gitea main
```
+86
View File
@@ -0,0 +1,86 @@
# La Voix du Peuple — Page wiki
**Type** : Plateforme civique numérique
**Stack** : Python / Flask · React / Vite · PostgreSQL · Mistral AI
**Hébergement** : Replit (dev) / Auto-hébergeable (RockyLinux, Debian)
**Dépôt** : `voix-du-peuple` (Gitea)
**Statut** : Actif — avril 2026
---
## Qu'est-ce que c'est ?
Un outil permettant à des citoyens de soumettre des propositions politiques en texte libre. Ces propositions sont :
- **filtrées** automatiquement par une IA selon les textes internationaux des droits humains (DUDH, PIDCP, CEDH…)
- **synthétisées** en un résumé structuré par thèmes, destiné à des élus ou décideurs
- **affichées** en temps réel sur la page principale
L'objectif est de fournir aux représentants politiques un document clair et utilisable issu des préoccupations citoyennes, sans intermédiaire.
---
## Fonctionnement en 5 points
1. Un citoyen saisit une proposition sur la page principale
2. L'IA (Mistral Small) vérifie que le contenu respecte les droits fondamentaux
3. Si la contribution est acceptée, elle s'ajoute à la base et déclenche une re-synthèse
4. L'IA (Mistral Large) relit toutes les contributions et produit un résumé thématique
5. Le résumé s'affiche en temps réel à droite de l'écran
---
## Pages de l'application
| URL | Contenu |
|-----|---------|
| `/` | Formulaire de soumission + contributions récentes + synthèse |
| `/about` | Description du projet et fondements juridiques |
| `/transparence` | Fonctionnement de l'IA, données collectées, limites |
---
## Stack technique
| Couche | Technologie |
|--------|-------------|
| Frontend | React 18 + TypeScript + Vite 7 + Tailwind CSS |
| Backend | Python 3.11 + Flask 3 + Gunicorn |
| Base de données | PostgreSQL |
| IA | Mistral AI (API OpenAI-compatible) |
| Modération | `mistral-small-latest` |
| Synthèse | `mistral-large-latest` |
| Police | Bahnschrift (titres), Inter (corps) |
---
## Variables d'environnement clés
```
DATABASE_URL — URL PostgreSQL
MISTRAL_API_KEY — Clé API Mistral
SESSION_SECRET — Secret Flask
FILTER_MODEL — Modèle de modération (optionnel)
SYNTHESIS_MODEL — Modèle de synthèse (optionnel)
```
---
## Données collectées
- Texte de la contribution
- Pseudonyme (facultatif, choisi par l'utilisateur)
- Horodatage
- Résultat du filtre IA (accepté / refusé + motif)
**Non collecté** : adresse IP, compte utilisateur, cookies de suivi.
---
## Liens
- Tutoriel Gitea → [`docs/GITEA_TUTO.md`](./GITEA_TUTO.md)
- Architecture technique → [`docs/DAT.md`](./DAT.md)
- Exploitation → [`docs/DEX.md`](./DEX.md)
- Guide déploiement → [`DEPLOIEMENT.md`](../DEPLOIEMENT.md)
- Fonctionnement technique (in-app) → `/transparence`