Files
la-voix-du-peuple/docs/INSTALL_ROCKY.md
T
billisdead 8f531d2e0e Procédure de développement local + tunnel SSH
- Nouveau script scripts/dev-local.sh : lance Flask (port 8080) et le
  serveur Vite dev (port 5173) en parallèle avec nettoyage propre
  (trap EXIT/INT/TERM). VITE_API_URL est injecté pour pointer vers
  Flask local, accessible depuis le client via tunnel SSH.
- docs/INSTALL_ROCKY.md : nouvelle section 16 avec schéma ASCII,
  procédure pas-à-pas et commande tunnel SSH exacte.
- docs/INSTALL_ROCKY.md : corrige les deux références cassées à
  vite.config.selfhost.ts (sections 9 et 15.1) supprimé lors de la
  dé-Replit-isation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 13:17:06 +02:00

14 KiB

Installation sur RockyLinux — La Voix du Peuple

Cible : VM RockyLinux 9.x
Port public : HTTP 8080 (vous gérez le HTTPS en amont)
Port interne : Gunicorn 8000 (localhost uniquement)
Répertoire d'installation : /opt/voix-du-peuple
Utilisateur système : voixdupeuple


Table des matières

  1. Prérequis
  2. Préparation du serveur
  3. Installation des paquets
  4. Utilisateur système
  5. Clonage du dépôt
  6. Base de données PostgreSQL
  7. Environnement Python
  8. Configuration du domaine et variables d'environnement
  9. Build du frontend
  10. Service Gunicorn (systemd)
  11. Nginx
  12. Firewall
  13. Vérification finale
  14. Disaster Recovery
  15. Maintenance et mises à jour
  16. Développement local avec tunnel SSH

1. Prérequis

  • VM RockyLinux 9.x avec accès root (ou sudo)
  • 1 vCPU minimum, 1 Go RAM (2 Go recommandés)
  • 10 Go de disque
  • Accès réseau sortant (pour cloner le dépôt et télécharger les paquets)
  • Nom de domaine configuré (enregistrement DNS A pointant vers l'IP de la VM)
  • Clé SSH ou accès console

2. Préparation du serveur

# Mise à jour complète du système
dnf update -y

# Outils de base
dnf install -y curl wget git tar unzip vim

# Activer le dépôt EPEL (nécessaire pour certains paquets)
dnf install -y epel-release

3. Installation des paquets

Python 3.11

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

Node.js 20 et pnpm

# Dépôt Node.js 20 LTS
curl -fsSL https://rpm.nodesource.com/setup_20.x | bash -
dnf install -y nodejs

# pnpm
npm install -g pnpm
pnpm --version
node --version   # doit afficher 20.x

PostgreSQL 15

# Dépôt officiel PostgreSQL
dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-9-x86_64/pgdg-redhat-repo-latest.noarch.rpm
dnf -qy module disable postgresql
dnf install -y postgresql15-server postgresql15

# Initialisation et démarrage
postgresql-15-setup initdb
systemctl enable postgresql-15
systemctl start postgresql-15
systemctl status postgresql-15   # doit afficher "active (running)"

Nginx

dnf install -y nginx
systemctl enable nginx

4. Utilisateur système

# Créer un utilisateur dédié sans shell de connexion
useradd -r -s /sbin/nologin -d /opt/voix-du-peuple voixdupeuple

# Répertoire de logs
mkdir -p /var/log/voix-du-peuple
chown voixdupeuple:voixdupeuple /var/log/voix-du-peuple

5. Clonage du dépôt

# Créer le répertoire d'installation
mkdir -p /opt/voix-du-peuple
cd /opt/voix-du-peuple

# Cloner depuis votre Gitea
git clone https://homegit.gyozamancave.fr/billisdead/la-voix-du-peuple.git .
# (ou via SSH si une clé est configurée sur votre serveur Gitea)

# Rendre le répertoire accessible à l'utilisateur système
chown -R voixdupeuple:voixdupeuple /opt/voix-du-peuple

6. Base de données PostgreSQL

# Se connecter en tant que postgres
su - postgres

# Créer l'utilisateur et la base
psql <<EOF
CREATE USER voix_user WITH PASSWORD 'MOT_DE_PASSE_FORT';
CREATE DATABASE voix_du_peuple OWNER voix_user;
GRANT ALL PRIVILEGES ON DATABASE voix_du_peuple TO voix_user;
EOF

exit   # revenir à root

Tester la connexion :

psql -U voix_user -d voix_du_peuple -h 127.0.0.1 -c '\l'

7. Environnement Python

cd /opt/voix-du-peuple

# Créer le virtualenv
python3.11 -m venv .venv

# Installer les dépendances
.venv/bin/pip install --upgrade pip
.venv/bin/pip install -r artifacts/flask-api/requirements.txt

8. Configuration du domaine et variables d'environnement

8.1 Créer le fichier .env

cp .env.example .env    # si le fichier exemple existe, sinon créer manuellement
vim .env

Contenu minimal du .env :

# Base de données
DATABASE_URL=postgresql://voix_user:MOT_DE_PASSE_FORT@127.0.0.1:5432/voix_du_peuple

# IA Mistral
MISTRAL_API_KEY=votre_cle_mistral_ici

# Sécurité Flask
SESSION_SECRET=generez-une-chaine-aleatoire-longue-ici

# Panel admin — mot de passe de connexion à /admin (OBLIGATOIRE)
ADMIN_SECRET=choisissez-un-mot-de-passe-long-et-complexe-ici

# Modèles IA (valeurs par défaut si omis)
# FILTER_MODEL=mistral-small-latest
# SYNTHESIS_MODEL=mistral-large-latest

# Domaine public — utilisé par le QR code du flyer
VITE_APP_URL=https://votredomaine.fr

Générer SESSION_SECRET et ADMIN_SECRET :

python3 -c "import secrets; print(secrets.token_hex(32))"
# Exécuter deux fois pour obtenir deux valeurs distinctes

8.2 Changer le domaine (QR code)

Le flyer génère automatiquement son QR code à partir de VITE_APP_URL. Pour changer le domaine :

# Depuis /opt/voix-du-peuple
bash scripts/set-domain.sh https://votredomaine.fr

Ce script met à jour VITE_APP_URL dans .env et reconstruit le frontend.
Appliquer ensuite : systemctl reload nginx


9. Build du frontend

cd /opt/voix-du-peuple

# Installer les dépendances Node
pnpm install

# Construire le frontend (lit VITE_APP_URL depuis .env)
source .env
export VITE_APP_URL
pnpm --filter @workspace/voix-du-peuple run build

# Vérifier que le build est présent
ls artifacts/voix-du-peuple/dist/public/

10. Service Gunicorn (systemd)

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

# Recharger systemd et activer le service
systemctl daemon-reload
systemctl enable voix-du-peuple-api
systemctl start voix-du-peuple-api
systemctl status voix-du-peuple-api

Vérifier que Gunicorn écoute bien sur le port interne :

ss -tlnp | grep 8000
# doit afficher : 127.0.0.1:8000

Tester l'API directement :

curl http://127.0.0.1:8000/health
# réponse attendue : {"status": "ok"}

11. Nginx

11.1 Installer la configuration

# Copier la config fournie
cp deploy/nginx.conf /etc/nginx/conf.d/voix-du-peuple.conf

# (Optionnel) Ajuster le nom de serveur dans la config
vim /etc/nginx/conf.d/voix-du-peuple.conf
# Modifier :  server_name _;
# En :        server_name votredomaine.fr;

# Tester la configuration
nginx -t

# Démarrer Nginx
systemctl start nginx
systemctl status nginx

11.2 Vérifier que Nginx écoute sur 8080

ss -tlnp | grep 8080
# doit afficher : 0.0.0.0:8080

12. Firewall

# Autoriser le port 8080 (HTTP)
firewall-cmd --permanent --add-port=8080/tcp
firewall-cmd --reload

# Vérifier
firewall-cmd --list-ports

Le port 8000 (Gunicorn) ne doit PAS être ouvert — il n'est accessible que depuis localhost via Nginx.


13. Vérification finale

# Health check API (via Nginx)
curl http://localhost:8080/health
# {"status": "ok"}

# Page principale
curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/
# 200

# Depuis l'extérieur (remplacer par l'IP de votre VM)
curl http://IP_DE_LA_VM:8080/health

Checklist :

  • systemctl status postgresql-15 → active
  • systemctl status voix-du-peuple-api → active
  • systemctl status nginx → active
  • curl http://localhost:8080/health{"status":"ok"}
  • L'interface s'affiche dans le navigateur sur http://IP:8080
  • Le flyer /flyer affiche le bon nom de domaine dans le QR code

14. Disaster Recovery

14.1 Sauvegarde de la base de données

# Dump complet (à planifier via cron)
pg_dump -U voix_user -h 127.0.0.1 voix_du_peuple > /opt/backups/voix_$(date +%Y%m%d_%H%M).sql

# Automatiser avec cron (tous les jours à 3h)
crontab -e
# Ajouter :
# 0 3 * * * pg_dump -U voix_user -h 127.0.0.1 voix_du_peuple > /opt/backups/voix_$(date +\%Y\%m\%d).sql
mkdir -p /opt/backups

14.2 Restauration de la base de données

# Stopper l'API pendant la restauration
systemctl stop voix-du-peuple-api

# Vider et restaurer la base
psql -U voix_user -h 127.0.0.1 voix_du_peuple -c "DROP SCHEMA public CASCADE; CREATE SCHEMA public;"
psql -U voix_user -h 127.0.0.1 voix_du_peuple < /opt/backups/voix_20260401.sql

# Redémarrer
systemctl start voix-du-peuple-api

14.3 Réinstallation complète depuis zéro

Si la VM est perdue, répéter les étapes 1 à 13 puis restaurer la base depuis le dernier backup :

psql -U voix_user -h 127.0.0.1 voix_du_peuple < /opt/backups/voix_DERNIER.sql

Les backups doivent être stockés hors de la VM (NFS, S3, objet distant…).

14.4 Rollback de code

cd /opt/voix-du-peuple

# Voir l'historique des commits
git log --oneline -10

# Revenir à un commit précédent
git checkout <COMMIT_HASH>

# Reconstruire le frontend si nécessaire
bash scripts/set-domain.sh https://votredomaine.fr

# Redémarrer l'API
systemctl restart voix-du-peuple-api
systemctl reload nginx

14.5 En cas de service hors ligne

# Diagnostic rapide
systemctl status voix-du-peuple-api
journalctl -u voix-du-peuple-api -n 50

systemctl status nginx
journalctl -u nginx -n 20

systemctl status postgresql-15

# Relance forcée
systemctl restart voix-du-peuple-api
systemctl restart nginx

15. Maintenance et mises à jour

15.1 Mise à jour du code

cd /opt/voix-du-peuple

# Récupérer les derniers commits depuis Gitea
git pull origin main

# Si les dépendances Python ont changé (requirements.txt modifié)
.venv/bin/pip install -r artifacts/flask-api/requirements.txt

# Si les dépendances Node ont changé (package.json modifié)
pnpm install

# Reconstruire le frontend (toujours faire après un pull)
source .env && export VITE_APP_URL
pnpm --filter @workspace/voix-du-peuple run build

# Appliquer
systemctl restart voix-du-peuple-api
systemctl reload nginx

15.2 Changer de nom de domaine

cd /opt/voix-du-peuple
bash scripts/set-domain.sh https://nouveaudomaine.fr
systemctl reload nginx

Le QR code est régénéré automatiquement dans le build.

15.3 Changer la clé API Mistral

vim /opt/voix-du-peuple/.env
# Modifier MISTRAL_API_KEY

systemctl restart voix-du-peuple-api

15.4 Purger les contributions

psql -U voix_user -h 127.0.0.1 voix_du_peuple
-- Tout purger (irréversible sans backup)
TRUNCATE ideas;
TRUNCATE synthesis;

-- Supprimer seulement les refusées
DELETE FROM ideas WHERE accepted = false;

-- Forcer une re-synthèse au prochain envoi
DELETE FROM synthesis;

15.5 Consulter les logs

# Logs de l'API en temps réel
journalctl -u voix-du-peuple-api -f

# Logs d'accès Gunicorn
tail -f /var/log/voix-du-peuple/api-access.log

# Logs d'erreur Gunicorn
tail -f /var/log/voix-du-peuple/api-error.log

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

15.6 Rotation des logs

# Créer un fichier logrotate
cat > /etc/logrotate.d/voix-du-peuple <<EOF
/var/log/voix-du-peuple/*.log {
    daily
    rotate 14
    compress
    missingok
    notifempty
    postrotate
        systemctl kill -s USR1 voix-du-peuple-api
    endscript
}
EOF

15.7 Vérification de santé (cron)

# Ajouter un health check toutes les 5 minutes
crontab -e
# Ajouter :
# */5 * * * * curl -sf http://127.0.0.1:8080/health || systemctl restart voix-du-peuple-api

15.8 Tableau de bord des commandes rapides

Opération Commande
Statut global systemctl status voix-du-peuple-api nginx postgresql-15
Relancer l'API systemctl restart voix-du-peuple-api
Recharger Nginx systemctl reload nginx
Logs API live journalctl -u voix-du-peuple-api -f
Mise à jour complète git pull && bash scripts/set-domain.sh $VITE_APP_URL && systemctl restart voix-du-peuple-api && systemctl reload nginx
Changer de domaine bash scripts/set-domain.sh https://domaine.fr
Backup BDD pg_dump -U voix_user -h 127.0.0.1 voix_du_peuple > backup.sql
Purger les données psql -U voix_user -h 127.0.0.1 voix_du_peuple -c "TRUNCATE ideas; TRUNCATE synthesis;"
Health check curl http://127.0.0.1:8080/health

16. Développement local avec tunnel SSH

Cette procédure permet de prévisualiser l'application directement depuis votre machine sans avoir à configurer Nginx ni reconstruire le frontend à chaque modification.

16.1 Principe

Votre machine locale          Serveur distant
┌─────────────────┐           ┌──────────────────────────┐
│ navigateur      │  tunnel   │ Vite dev server :5173     │
│ localhost:5173  │◄─────────►│                          │
│ localhost:8080  │           │ Flask API        :8080     │
└─────────────────┘           └──────────────────────────┘

Le tunnel SSH redirige les deux ports localement — aucun port n'est ouvert sur Internet.

16.2 Démarrer les serveurs

cd /opt/voix-du-peuple
bash scripts/dev-local.sh

Le script :

  1. Charge .env
  2. Démarre l'API Flask sur le port PORT (défaut : 8080)
  3. Démarre le serveur Vite sur le port 5173
  4. Affiche la commande tunnel SSH à exécuter depuis votre machine
  5. S'arrête proprement (Ctrl+C) en tuant les deux processus

Note

: PostgreSQL doit être en cours d'exécution et DATABASE_URL correctement renseignée dans .env.

16.3 Ouvrir le tunnel SSH

Depuis votre machine locale (pas le serveur) :

ssh -L 5173:localhost:5173 -L 8080:localhost:8080 utilisateur@votre-serveur

Maintenez cette connexion SSH ouverte pendant toute la session de travail.

16.4 Accéder à l'interface

Ouvrez http://localhost:5173 dans votre navigateur local.

Le rechargement à chaud (HMR) fonctionne : toute modification dans artifacts/voix-du-peuple/src/ est reflétée instantanément sans rebuild.

16.5 Arrêter

  • Ctrl+C dans le terminal où tourne dev-local.sh (arrête Flask et Vite)
  • Fermer la connexion SSH (ou Ctrl+C dans le terminal du tunnel)