Files
la-voix-du-peuple/EXPLOITATION.md
T
pironantoine 432509b2d3 Add technical and operational guides for the platform
Add ARCHITECTURE.md and EXPLOITATION.md files detailing the system's technical design and operational procedures.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 923ae0e3-a363-4db8-b04a-e8baca2a1330
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 9fd0e274-629e-4dae-8960-adf4c556797f
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/8af7d2ec-2cc3-4ece-8af3-9f071488d072/923ae0e3-a363-4db8-b04a-e8baca2a1330/VnHW0bR
Replit-Helium-Checkpoint-Created: true
2026-04-03 17:09:34 +00:00

13 KiB

Manuel d'Exploitation — La Voix du Peuple

Version : 1.0
Date : Avril 2026
Public : Administrateurs système / DevOps


1. Services et composants

Service Technologie Géré par
Équilibreur frontal HAProxy Infrastructure réseau séparée
Reverse proxy Nginx systemd / DNF
API backend Gunicorn + Flask systemd (voix-du-peuple-api)
Base de données PostgreSQL 15 systemd (postgresql-15)
Frontend Fichiers statiques Nginx (pas de service dédié)

2. Commandes de gestion courantes

2.1 API Flask (Gunicorn)

# Statut
sudo systemctl status voix-du-peuple-api

# Démarrer / arrêter / redémarrer
sudo systemctl start   voix-du-peuple-api
sudo systemctl stop    voix-du-peuple-api
sudo systemctl restart voix-du-peuple-api

# Rechargement gracieux (sans coupure de connexions)
sudo systemctl reload voix-du-peuple-api

# Activer/désactiver le démarrage automatique
sudo systemctl enable  voix-du-peuple-api
sudo systemctl disable voix-du-peuple-api

2.2 Nginx

sudo systemctl status  nginx
sudo systemctl reload  nginx   # Recharge la config sans coupure
sudo systemctl restart nginx   # Redémarrage complet

# Tester la configuration avant reload
sudo nginx -t

2.3 PostgreSQL

sudo systemctl status    postgresql-15
sudo systemctl restart   postgresql-15

# Connexion console
sudo -u postgres psql -d voixdupeuple

# Sauvegarde manuelle
sudo -u postgres pg_dump voixdupeuple > /backup/voixdupeuple_$(date +%Y%m%d_%H%M).sql

2.4 HAProxy (réseau séparé)

HAProxy est géré sur son propre hôte/réseau. Référez-vous à la documentation de votre équipe réseau.

Vérification de la connectivité depuis l'hôte applicatif :

# Vérifier que Nginx répond bien sur le réseau interne
curl -v http://<IP_NGINX>/api/healthz

3. Vérifications de santé

3.1 Endpoint de santé applicatif

# Depuis l'hôte (via Nginx)
curl http://localhost/api/healthz
# Réponse attendue : {"status":"ok"}

# Directement sur Gunicorn (bypass Nginx)
curl http://127.0.0.1:8000/api/healthz

3.2 Vérification complète de la chaîne

# 1. PostgreSQL
sudo -u postgres psql -c "SELECT 1" voixdupeuple

# 2. Gunicorn
curl -s http://127.0.0.1:8000/api/healthz | grep ok

# 3. Nginx
curl -s http://localhost/api/healthz | grep ok

# 4. Stats de la plateforme
curl -s http://localhost/api/ideas/stats
# Réponse : {"total": N, "accepted": N, "rejected": N}

# 5. Depuis HAProxy (test end-to-end TLS)
curl -s https://voix-du-peuple.example.com/api/healthz

3.3 Script de vérification rapide

#!/bin/bash
# /opt/voix-du-peuple/scripts/healthcheck.sh

ERRORS=0

check() {
    echo -n "$1 ... "
    if eval "$2" &>/dev/null; then
        echo "OK"
    else
        echo "ERREUR"
        ERRORS=$((ERRORS+1))
    fi
}

check "PostgreSQL"    "sudo -u postgres psql -c 'SELECT 1' voixdupeuple"
check "Gunicorn"      "curl -sf http://127.0.0.1:8000/api/healthz"
check "Nginx"         "curl -sf http://localhost/api/healthz"
check "Systemd API"   "systemctl is-active --quiet voix-du-peuple-api"

[ $ERRORS -eq 0 ] && echo "Tout OK" || echo "$ERRORS service(s) en erreur"
exit $ERRORS

4. Logs

4.1 Localisation des logs

Source Fichier / Commande
API Flask (applicatif) /var/log/voix-du-peuple/api-error.log
API Flask (accès HTTP) /var/log/voix-du-peuple/api-access.log
Journald (systemd) journalctl -u voix-du-peuple-api
Nginx accès /var/log/nginx/access.log
Nginx erreurs /var/log/nginx/error.log
PostgreSQL /var/lib/pgsql/15/data/log/
HAProxy Voir hôte HAProxy dédié

4.2 Consultation en temps réel

# Logs applicatifs Flask
tail -f /var/log/voix-du-peuple/api-error.log

# Logs systemd en temps réel
journalctl -u voix-du-peuple-api -f

# Logs Nginx
tail -f /var/log/nginx/error.log
tail -f /var/log/nginx/access.log

# Combiner plusieurs sources
journalctl -u voix-du-peuple-api -u nginx -f

4.3 Recherche dans les logs

# Erreurs OpenAI des dernières 24h
grep -i "openai\|erreur\|error" /var/log/voix-du-peuple/api-error.log | tail -50

# Requêtes POST /api/ideas
grep "POST /api/ideas" /var/log/voix-du-peuple/api-access.log | tail -20

# IP ayant déclenché le rate limiting (code 429)
grep " 429 " /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn

4.4 Rotation des logs

Créer /etc/logrotate.d/voix-du-peuple :

/var/log/voix-du-peuple/*.log {
    daily
    rotate 30
    compress
    delaycompress
    missingok
    notifempty
    postrotate
        systemctl reload voix-du-peuple-api > /dev/null 2>&1 || true
    endscript
}

5. Sauvegardes

5.1 Base de données

# Sauvegarde manuelle
sudo -u postgres pg_dump voixdupeuple \
  | gzip > /backup/voixdupeuple_$(date +%Y%m%d_%H%M%S).sql.gz

# Restauration
gunzip -c /backup/voixdupeuple_20260401_120000.sql.gz \
  | sudo -u postgres psql voixdupeuple

5.2 Sauvegarde automatique (cron)

sudo crontab -e -u postgres
# Sauvegarde quotidienne à 3h00
0 3 * * * pg_dump voixdupeuple | gzip > /backup/voixdupeuple_$(date +\%Y\%m\%d).sql.gz

# Nettoyage des sauvegardes de plus de 30 jours
0 4 * * * find /backup -name "voixdupeuple_*.sql.gz" -mtime +30 -delete

6. Mise à jour applicative

6.1 Mise à jour du code

sudo -u voixdupeuple bash << 'EOF'
  cd /opt/voix-du-peuple
  git fetch origin
  git log HEAD..origin/main --oneline   # Voir ce qui va changer
  git pull origin main
EOF

6.2 Mise à jour des dépendances Python

sudo -u voixdupeuple bash << 'EOF'
  cd /opt/voix-du-peuple
  .venv/bin/pip install -r artifacts/flask-api/requirements.txt
EOF
sudo systemctl restart voix-du-peuple-api

6.3 Rebuild du frontend

sudo -u voixdupeuple bash << 'EOF'
  cd /opt/voix-du-peuple
  pnpm install --frozen-lockfile
  cd artifacts/voix-du-peuple
  pnpm exec vite build --config vite.config.selfhost.ts
EOF
sudo systemctl reload nginx

6.4 Procédure de mise à jour complète

sudo -u voixdupeuple bash << 'EOF'
  cd /opt/voix-du-peuple
  git pull origin main
  .venv/bin/pip install -r artifacts/flask-api/requirements.txt
  pnpm install --frozen-lockfile
  cd artifacts/voix-du-peuple
  pnpm exec vite build --config vite.config.selfhost.ts
EOF

sudo systemctl restart voix-du-peuple-api
sudo systemctl reload nginx
# Vérification
sleep 3
curl -s http://localhost/api/healthz

7. HAProxy — intégration réseau séparé

7.1 Vérification de la connectivité inter-réseaux

# Depuis l'hôte HAProxy : vérifier que Nginx est joignable
curl -v http://<IP_NGINX_INTERNE>:80/api/healthz

# Depuis l'hôte applicatif : vérifier que les X-Forwarded-For arrivent bien
curl -s http://localhost/api/ideas/stats
journalctl -u voix-du-peuple-api -n 20 | grep "127.0.0.1\|X-Forwarded"

7.2 IP réelle dans Flask (rate limiting)

Flask utilise l'IP réelle du client via l'en-tête X-Forwarded-For positionné par HAProxy. Nginx doit être configuré pour transmettre cet en-tête et déclarer l'IP de HAProxy comme source de confiance.

Dans /etc/nginx/conf.d/voix-du-peuple.conf :

# Remplacez par l'IP réelle de votre HAProxy
set_real_ip_from  192.168.10.5;
real_ip_header    X-Forwarded-For;
real_ip_recursive on;

location /api/ {
    proxy_pass         http://127.0.0.1:8000;
    proxy_set_header   Host              $host;
    proxy_set_header   X-Real-IP         $remote_addr;
    proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
    proxy_set_header   X-Forwarded-Proto $http_x_forwarded_proto;
    proxy_read_timeout 60s;
}

7.3 Vérification du rate limiting par IP réelle

# Simuler une requête comme si elle venait d'une IP externe via HAProxy
curl -H "X-Forwarded-For: 1.2.3.4" http://127.0.0.1:8000/api/healthz

# Vérifier dans les logs Flask que l'IP 1.2.3.4 est bien loggée
journalctl -u voix-du-peuple-api -n 5

7.4 Check HAProxy depuis la plateforme

# Vérifier les en-têtes qui arrivent réellement sur Gunicorn
python3 -c "
import urllib.request
req = urllib.request.Request('http://127.0.0.1:8000/api/healthz')
r = urllib.request.urlopen(req)
print(dict(r.headers))
"

8. Gestion des incidents

8.1 L'API ne répond plus (5xx)

# 1. Vérifier le service
sudo systemctl status voix-du-peuple-api

# 2. Lire les logs récents
journalctl -u voix-du-peuple-api -n 50 --no-pager

# 3. Redémarrer
sudo systemctl restart voix-du-peuple-api

# 4. Si l'erreur persiste, vérifier PostgreSQL
sudo systemctl status postgresql-15
curl -s http://127.0.0.1:8000/api/healthz

8.2 Erreurs OpenAI (filtrage/synthèse dégradé)

Les erreurs OpenAI n'interrompent pas le service :

  • Si le filtrage échoue → l'idée est rejetée par défaut (fail-safe)
  • Si la synthèse échoue → le texte précédent est conservé
# Vérifier les erreurs OpenAI
grep -i "openai\|openrouter\|api_key" /var/log/voix-du-peuple/api-error.log | tail -20

# Tester la clé API manuellement
source /opt/voix-du-peuple/.env
curl https://api.openai.com/v1/models \
  -H "Authorization: Bearer $OPENAI_API_KEY" | python3 -m json.tool | head -20

8.3 Base de données inaccessible

# Vérifier PostgreSQL
sudo systemctl status postgresql-15
sudo -u postgres psql -c "SELECT version();"

# Connexion directe avec l'utilisateur applicatif
source /opt/voix-du-peuple/.env
psql "$DATABASE_URL" -c "SELECT COUNT(*) FROM ideas;"

# Redémarrer PostgreSQL en dernier recours
sudo systemctl restart postgresql-15
sudo systemctl restart voix-du-peuple-api

8.4 Attaque ou abus (rate limit dépassé)

# Identifier les IPs agressives
grep " 429 " /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -20

# Bloquer une IP via firewalld
sudo firewall-cmd --add-rich-rule='rule family="ipv4" source address="1.2.3.4" reject' --permanent
sudo firewall-cmd --reload

# Ou déléguer le blocage à HAProxy (recommandé — ACL HAProxy)

8.5 Disque plein

# Localiser ce qui prend de la place
df -h
du -sh /var/log/voix-du-peuple/*
du -sh /backup/*

# Nettoyer les anciens logs (si logrotate non configuré)
find /var/log/voix-du-peuple -name "*.log.*" -mtime +14 -delete

# Nettoyer les anciennes sauvegardes
find /backup -name "voixdupeuple_*.sql.gz" -mtime +30 -delete

9. Supervision (recommandations)

9.1 Métriques à surveiller

Métrique Seuil d'alerte Commande
CPU Gunicorn > 80% sustained top -p $(pgrep -d, gunicorn)
Mémoire > 80% free -m
Espace disque > 80% df -h
Temps de réponse /api/ideas > 10s time curl http://localhost/api/ideas
Taux d'erreur 5xx > 1% grep " 5[0-9][0-9] " /var/log/nginx/access.log
PostgreSQL connexions > 80 SELECT count(*) FROM pg_stat_activity;

9.2 Intégration avec des outils de supervision

Prometheus + Alertmanager (optionnel) :

# Installer flask-prometheus-metrics si vous souhaitez exposer des métriques
.venv/bin/pip install prometheus-flask-exporter

Nagios / Zabbix — check HTTP simple :

check_http -H localhost -u /api/healthz -e "200 OK" -s '{"status":"ok"}'

10. Opérations PostgreSQL courantes

-- Nombre d'idées par statut
SELECT accepted, COUNT(*) FROM ideas GROUP BY accepted;

-- Dernières idées soumises
SELECT id, LEFT(content, 60), author, accepted, created_at
FROM ideas ORDER BY created_at DESC LIMIT 10;

-- Idées rejetées avec motif
SELECT LEFT(content, 60), rejection_reason, legal_basis
FROM ideas WHERE accepted = FALSE ORDER BY created_at DESC LIMIT 10;

-- Synthèse actuelle
SELECT idea_count, updated_at, LEFT(text, 200) FROM synthesis;

-- Taille des tables
SELECT relname, pg_size_pretty(pg_total_relation_size(relid))
FROM pg_stat_user_tables ORDER BY pg_total_relation_size(relid) DESC;

-- Connexions actives
SELECT count(*), state FROM pg_stat_activity GROUP BY state;

11. Référence des fichiers de configuration

Fichier Rôle
/opt/voix-du-peuple/.env Variables d'environnement (secrets)
/etc/systemd/system/voix-du-peuple-api.service Service systemd Gunicorn
/etc/nginx/conf.d/voix-du-peuple.conf Configuration Nginx
/var/lib/pgsql/15/data/pg_hba.conf Authentification PostgreSQL
/var/lib/pgsql/15/data/postgresql.conf Configuration PostgreSQL
/etc/logrotate.d/voix-du-peuple Rotation des logs
/opt/voix-du-peuple/artifacts/flask-api/ai_agent.py Configuration agents IA