Files
la-voix-du-peuple/DEPLOIEMENT.md
T
pironantoine c0322d5c8e Add self-hosting capabilities and deployment guide for the application
Implement self-hosting for RockyLinux by adding systemd and Nginx configurations, updating API models to support standard OpenAI keys, and providing a comprehensive deployment guide.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 923ae0e3-a363-4db8-b04a-e8baca2a1330
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: f8aa455f-f180-4964-94dd-11cfb1a42383
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:06:07 +00:00

10 KiB

Guide d'auto-hébergement — La Voix du Peuple

RockyLinux 9 (ou RHEL 9, AlmaLinux 9)


Architecture

Internet
   │
   ▼
[Nginx]  ─── /api/* ──► [Gunicorn + Flask]  ──► [PostgreSQL]
   │                           │
   └─── /* ──► [React SPA]     └──► [OpenAI API]
               (fichiers statiques)
  • Frontend : React + Vite, servi comme fichiers statiques par Nginx
  • Backend : Flask + Gunicorn (4 workers), accessible uniquement via Nginx
  • Base de données : PostgreSQL 15+
  • IA : API OpenAI (clé standard, pas de proxy Replit)

Prérequis

# Mettre à jour le système
sudo dnf update -y

# Outils de base
sudo dnf install -y git curl wget epel-release

1. Installer les dépendances système

Python 3.11

sudo dnf install -y python3.11 python3.11-pip python3.11-devel
python3.11 --version   # doit afficher Python 3.11.x

Node.js 20 (LTS)

curl -fsSL https://rpm.nodesource.com/setup_20.x | sudo bash -
sudo dnf install -y nodejs
node --version   # doit afficher v20.x.x

pnpm

npm install -g pnpm
pnpm --version

PostgreSQL 15

sudo dnf install -y postgresql15-server postgresql15
sudo /usr/pgsql-15/bin/postgresql-15-setup initdb
sudo systemctl enable --now postgresql-15

Nginx

sudo dnf install -y nginx
sudo systemctl enable nginx

2. Configurer PostgreSQL

# Connexion en tant que postgres
sudo -u postgres psql

-- Dans psql :
CREATE USER voixdupeuple WITH PASSWORD 'CHANGEME_MOT_DE_PASSE_FORT';
CREATE DATABASE voixdupeuple OWNER voixdupeuple;
GRANT ALL PRIVILEGES ON DATABASE voixdupeuple TO voixdupeuple;
\q

Éditez /var/lib/pgsql/15/data/pg_hba.conf pour autoriser la connexion locale par mot de passe :

# Remplacez "ident" par "md5" sur la ligne 127.0.0.1/32 :
host    all             all             127.0.0.1/32            md5
sudo systemctl restart postgresql-15

# Test de connexion
psql -U voixdupeuple -h 127.0.0.1 -d voixdupeuple -c "\dt"

3. Créer l'utilisateur système

sudo useradd -r -s /bin/bash -d /opt/voix-du-peuple voixdupeuple
sudo mkdir -p /opt/voix-du-peuple
sudo chown voixdupeuple:voixdupeuple /opt/voix-du-peuple

4. Cloner le dépôt depuis Gitea

sudo -u voixdupeuple bash -c "
  cd /opt
  git clone https://votre-gitea.example.com/utilisateur/voix-du-peuple.git voix-du-peuple
"

5. Configurer les variables d'environnement

sudo -u voixdupeuple bash -c "
  cp /opt/voix-du-peuple/.env.example /opt/voix-du-peuple/.env
"

# Éditer le fichier .env
sudo nano /opt/voix-du-peuple/.env

Remplissez au minimum :

DATABASE_URL=postgresql://voixdupeuple:CHANGEME_MOT_DE_PASSE_FORT@127.0.0.1:5432/voixdupeuple
OPENAI_API_KEY=sk-VOTRE_CLE_OPENAI
SESSION_SECRET=GENEREZ_UNE_VALEUR_ALEATOIRE_ICI
PORT=8000
FLASK_ENV=production

Générez le SESSION_SECRET :

python3.11 -c "import secrets; print(secrets.token_hex(32))"

Sécurisez le fichier :

sudo chmod 600 /opt/voix-du-peuple/.env
sudo chown voixdupeuple:voixdupeuple /opt/voix-du-peuple/.env

6. Installer les dépendances Python

sudo -u voixdupeuple bash -c "
  cd /opt/voix-du-peuple
  python3.11 -m venv .venv
  .venv/bin/pip install --upgrade pip
  .venv/bin/pip install -r artifacts/flask-api/requirements.txt
"

7. Initialiser la base de données

sudo -u voixdupeuple bash -c "
  cd /opt/voix-du-peuple/artifacts/flask-api
  source /opt/voix-du-peuple/.env
  export DATABASE_URL OPENAI_API_KEY SESSION_SECRET FLASK_ENV PORT
  /opt/voix-du-peuple/.venv/bin/python3 -c '
from database import init_db
init_db()
print(\"Base de données initialisée.\")
'
"

8. Construire le frontend React

sudo -u voixdupeuple bash -c "
  cd /opt/voix-du-peuple
  pnpm install --frozen-lockfile

  cd artifacts/voix-du-peuple
  pnpm exec vite build --config vite.config.selfhost.ts
"

Les fichiers statiques sont générés dans artifacts/voix-du-peuple/dist/public/.


9. Configurer le service systemd (Gunicorn)

# Créer le répertoire de logs
sudo mkdir -p /var/log/voix-du-peuple
sudo chown voixdupeuple:voixdupeuple /var/log/voix-du-peuple

# Copier le fichier de service
sudo cp /opt/voix-du-peuple/deploy/voix-du-peuple-api.service \
        /etc/systemd/system/voix-du-peuple-api.service

# Activer et démarrer
sudo systemctl daemon-reload
sudo systemctl enable --now voix-du-peuple-api

# Vérifier
sudo systemctl status voix-du-peuple-api

Test de l'API :

curl http://127.0.0.1:8000/api/healthz
# {"status": "ok"}

10. Configurer Nginx

# Copier la config Nginx
sudo cp /opt/voix-du-peuple/deploy/nginx.conf \
        /etc/nginx/conf.d/voix-du-peuple.conf

# Éditez le fichier pour mettre votre nom de domaine
sudo nano /etc/nginx/conf.d/voix-du-peuple.conf
# Remplacez voix-du-peuple.example.com par votre domaine

# Tester la configuration
sudo nginx -t

# Recharger Nginx
sudo systemctl reload nginx

11. Ouvrir le pare-feu

sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload

SELinux — autoriser Nginx à se connecter à Gunicorn :

sudo setsebool -P httpd_can_network_connect 1

12. (Recommandé) HTTPS avec Let's Encrypt

sudo dnf install -y certbot python3-certbot-nginx

sudo certbot --nginx -d voix-du-peuple.example.com \
  --email admin@example.com --agree-tos --no-eff-email

# Renouvellement automatique (déjà configuré par certbot)
sudo systemctl status certbot-renew.timer

Puis décommentez le bloc HTTPS dans /etc/nginx/conf.d/voix-du-peuple.conf.


Structure du projet

voix-du-peuple/
├── artifacts/
│   ├── flask-api/           # Backend Python Flask
│   │   ├── app.py           # Application principale + routes
│   │   ├── ai_agent.py      # Agent IA (filtrage + synthèse)
│   │   ├── legal_framework.py  # Prompts ancrés dans le droit international
│   │   ├── database.py      # Accès PostgreSQL (psycopg2)
│   │   ├── requirements.txt # Dépendances Python
│   │   └── start.sh         # Démarrage développement
│   └── voix-du-peuple/      # Frontend React + Vite
│       ├── src/
│       │   ├── pages/       # home.tsx, about.tsx
│       │   ├── components/  # Composants UI (shadcn/ui + radix)
│       │   └── App.tsx      # Routing principal
│       ├── vite.config.ts         # Config Replit (développement)
│       └── vite.config.selfhost.ts # Config auto-hébergement (production)
├── lib/
│   ├── api-spec/            # Spécification OpenAPI
│   ├── api-client-react/    # Hooks React Query générés
│   └── db/                  # Schéma Drizzle (référence)
├── deploy/
│   ├── voix-du-peuple-api.service  # Service systemd Gunicorn
│   └── nginx.conf                  # Configuration Nginx
├── .env.example             # Variables d'environnement (modèle)
└── DEPLOIEMENT.md           # Ce fichier

Variables d'environnement complètes

Variable Obligatoire Description
DATABASE_URL Oui URL PostgreSQL complète
OPENAI_API_KEY Oui Clé API OpenAI (sk-...)
OPENAI_BASE_URL Non Proxy OpenAI compatible (Ollama, Azure, etc.)
OPENAI_FILTER_MODEL Non Modèle de filtrage (défaut : gpt-4o-mini)
OPENAI_SYNTHESIS_MODEL Non Modèle de synthèse (défaut : gpt-4o)
SESSION_SECRET Oui Clé secrète Flask (min 32 caractères aléatoires)
PORT Non Port Gunicorn (défaut : 8000)
FLASK_ENV Non production ou development

Modèles IA utilisés

Fonction Modèle par défaut Configurable dans
Filtrage des idées gpt-4o-mini ai_agent.py ligne 56
Synthèse collective gpt-4o ai_agent.py ligne 104

Note : Les modèles gpt-5-mini et gpt-5.2 sont les noms utilisés sur la plateforme Replit. En auto-hébergement avec l'API OpenAI standard, utilisez gpt-4o-mini et gpt-4o.

Pour changer les modèles, éditez artifacts/flask-api/ai_agent.py :

# Filtrage (ligne ~56)
model="gpt-4o-mini",  # ou tout modèle OpenAI compatible

# Synthèse (ligne ~104)
model="gpt-4o",       # ou tout modèle OpenAI compatible

Mise à jour

sudo -u voixdupeuple bash -c "
  cd /opt/voix-du-peuple
  git pull origin main

  # Mise à jour des dépendances Python si nécessaire
  .venv/bin/pip install -r artifacts/flask-api/requirements.txt

  # Mise à jour des dépendances Node et rebuild frontend
  pnpm install --frozen-lockfile
  cd artifacts/voix-du-peuple
  pnpm exec vite build --config vite.config.selfhost.ts
"

# Redémarrer le service
sudo systemctl restart voix-du-peuple-api
sudo systemctl reload nginx

Logs et supervision

# Logs API Flask
sudo journalctl -u voix-du-peuple-api -f
tail -f /var/log/voix-du-peuple/api-error.log
tail -f /var/log/voix-du-peuple/api-access.log

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

# Statut des services
sudo systemctl status voix-du-peuple-api
sudo systemctl status nginx
sudo systemctl status postgresql-15

Dépannage fréquent

L'API répond 502 Bad Gateway

# Vérifier que Gunicorn tourne
sudo systemctl status voix-du-peuple-api
# Vérifier SELinux
sudo setsebool -P httpd_can_network_connect 1

Erreur "could not connect to server" (PostgreSQL)

# Vérifier que PostgreSQL tourne
sudo systemctl status postgresql-15
# Vérifier l'URL de connexion dans .env
psql -U voixdupeuple -h 127.0.0.1 -d voixdupeuple

Erreur OpenAI API

# Vérifier la clé dans .env
grep OPENAI_API_KEY /opt/voix-du-peuple/.env
# Tester directement
curl https://api.openai.com/v1/models \
  -H "Authorization: Bearer $OPENAI_API_KEY"

Le frontend affiche une page blanche

# Vérifier que le build existe
ls /opt/voix-du-peuple/artifacts/voix-du-peuple/dist/public/
# Vérifier les droits
sudo chown -R voixdupeuple:voixdupeuple /opt/voix-du-peuple/artifacts/voix-du-peuple/dist/