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
This commit is contained in:
+496
@@ -0,0 +1,496 @@
|
||||
# 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 |
|
||||
Reference in New Issue
Block a user