432509b2d3
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
497 lines
13 KiB
Markdown
497 lines
13 KiB
Markdown
# 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)
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
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 :
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
#!/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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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)
|
|
|
|
```bash
|
|
sudo crontab -e -u postgres
|
|
```
|
|
|
|
```cron
|
|
# 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
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
# 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` :
|
|
|
|
```nginx
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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)
|
|
|
|
```bash
|
|
# 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é
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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é)
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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) :
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```sql
|
|
-- 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 |
|