-
- Destination du QR code
-
-
- setInputValue(e.target.value)}
- onKeyDown={(e) => e.key === "Enter" && applyUrl()}
- placeholder="https://lavoixdupeuple.fr"
- className="font-mono text-sm"
- />
-
-
-
-
-
-
-
-
+
+
+
+
+
{/* Flyer imprimable */}
@@ -83,7 +57,7 @@ export default function Flyer() {
@@ -106,7 +80,7 @@ export default function Flyer() {
className="text-lg font-bold text-primary tracking-wide"
style={{ fontFamily: "'Bahnschrift', 'DIN Alternate', sans-serif" }}
>
- {qrUrl.replace(/^https?:\/\//, "")}
+ {QR_URL.replace(/^https?:\/\//, "")}
diff --git a/deploy/nginx.conf b/deploy/nginx.conf
index b2c6f2c..4f104d3 100644
--- a/deploy/nginx.conf
+++ b/deploy/nginx.conf
@@ -1,9 +1,6 @@
server {
- listen 80;
- server_name voix-du-peuple.example.com;
-
- # Redirection HTTPS (décommentez après avoir configuré Certbot)
- # return 301 https://$host$request_uri;
+ listen 8080;
+ server_name _; # remplacer par votre nom de domaine si nécessaire
root /opt/voix-du-peuple/artifacts/voix-du-peuple/dist/public;
index index.html;
@@ -14,7 +11,13 @@ server {
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
- # API Flask — proxy vers Gunicorn
+ # Health check
+ location /health {
+ proxy_pass http://127.0.0.1:8000/health;
+ proxy_set_header Host $host;
+ }
+
+ # API Flask — proxy vers Gunicorn (interne)
location /api/ {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
@@ -24,7 +27,7 @@ server {
proxy_read_timeout 60s;
}
- # Frontend React — Single Page Application
+ # Frontend React — SPA
location / {
try_files $uri $uri/ /index.html;
}
@@ -34,42 +37,6 @@ server {
expires 1y;
add_header Cache-Control "public, immutable";
}
-}
-# HTTPS — décommentez et adaptez après avoir obtenu votre certificat SSL
-# server {
-# listen 443 ssl http2;
-# server_name voix-du-peuple.example.com;
-#
-# ssl_certificate /etc/letsencrypt/live/voix-du-peuple.example.com/fullchain.pem;
-# ssl_certificate_key /etc/letsencrypt/live/voix-du-peuple.example.com/privkey.pem;
-# ssl_protocols TLSv1.2 TLSv1.3;
-# ssl_ciphers HIGH:!aNULL:!MD5;
-#
-# add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
-# add_header X-Content-Type-Options "nosniff" always;
-# add_header X-Frame-Options "DENY" always;
-# add_header X-XSS-Protection "1; mode=block" always;
-# add_header Referrer-Policy "strict-origin-when-cross-origin" always;
-#
-# root /opt/voix-du-peuple/artifacts/voix-du-peuple/dist/public;
-# index index.html;
-#
-# 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 $scheme;
-# proxy_read_timeout 60s;
-# }
-#
-# location / {
-# try_files $uri $uri/ /index.html;
-# }
-#
-# location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
-# expires 1y;
-# add_header Cache-Control "public, immutable";
-# }
-# }
+ client_max_body_size 1m;
+}
diff --git a/docs/INSTALL_ROCKY.md b/docs/INSTALL_ROCKY.md
new file mode 100644
index 0000000..de27447
--- /dev/null
+++ b/docs/INSTALL_ROCKY.md
@@ -0,0 +1,538 @@
+# 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](#1-prérequis)
+2. [Préparation du serveur](#2-préparation-du-serveur)
+3. [Installation des paquets](#3-installation-des-paquets)
+4. [Utilisateur système](#4-utilisateur-système)
+5. [Clonage du dépôt](#5-clonage-du-dépôt)
+6. [Base de données PostgreSQL](#6-base-de-données-postgresql)
+7. [Environnement Python](#7-environnement-python)
+8. [Configuration du domaine et variables d'environnement](#8-configuration-du-domaine-et-variables-denvironnement)
+9. [Build du frontend](#9-build-du-frontend)
+10. [Service Gunicorn (systemd)](#10-service-gunicorn-systemd)
+11. [Nginx](#11-nginx)
+12. [Firewall](#12-firewall)
+13. [Vérification finale](#13-vérification-finale)
+14. [Disaster Recovery](#14-disaster-recovery)
+15. [Maintenance et mises à jour](#15-maintenance-et-mises-à-jour)
+
+---
+
+## 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
+
+```bash
+# 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
+
+```bash
+dnf install -y python3.11 python3.11-pip python3.11-devel
+python3.11 --version # doit afficher 3.11.x
+```
+
+### Node.js 20 et pnpm
+
+```bash
+# 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
+
+```bash
+# 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
+
+```bash
+dnf install -y nginx
+systemctl enable nginx
+```
+
+---
+
+## 4. Utilisateur système
+
+```bash
+# 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
+
+```bash
+# 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
+
+```bash
+# Se connecter en tant que postgres
+su - postgres
+
+# Créer l'utilisateur et la base
+psql <
Le port 8000 (Gunicorn) ne doit PAS être ouvert — il n'est accessible que depuis localhost via Nginx.
+
+---
+
+## 13. Vérification finale
+
+```bash
+# 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
+
+```bash
+# 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
+```
+
+```bash
+mkdir -p /opt/backups
+```
+
+### 14.2 Restauration de la base de données
+
+```bash
+# 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 :
+
+```bash
+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
+
+```bash
+cd /opt/voix-du-peuple
+
+# Voir l'historique des commits
+git log --oneline -10
+
+# Revenir à un commit précédent
+git checkout
+
+# 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
+
+```bash
+# 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
+
+```bash
+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 --config vite.config.selfhost.ts
+
+# Appliquer
+systemctl restart voix-du-peuple-api
+systemctl reload nginx
+```
+
+### 15.2 Changer de nom de domaine
+
+```bash
+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
+
+```bash
+vim /opt/voix-du-peuple/.env
+# Modifier MISTRAL_API_KEY
+
+systemctl restart voix-du-peuple-api
+```
+
+### 15.4 Purger les contributions
+
+```bash
+psql -U voix_user -h 127.0.0.1 voix_du_peuple
+```
+
+```sql
+-- 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
+
+```bash
+# 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
+
+```bash
+# Créer un fichier logrotate
+cat > /etc/logrotate.d/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` |
diff --git a/scripts/set-domain.sh b/scripts/set-domain.sh
new file mode 100644
index 0000000..ca46938
--- /dev/null
+++ b/scripts/set-domain.sh
@@ -0,0 +1,51 @@
+#!/usr/bin/env bash
+# set-domain.sh — Configure le nom de domaine et reconstruit le frontend
+#
+# Usage : bash scripts/set-domain.sh https://votredomaine.fr
+#
+# Ce script :
+# 1. Met à jour la variable VITE_APP_URL dans .env
+# 2. Reconstruit le frontend (le QR code sera mis à jour)
+#
+# Prérequis : pnpm installé, être à la racine du projet
+
+set -e
+
+DOMAIN="${1:-}"
+
+if [ -z "$DOMAIN" ]; then
+ echo "Usage : bash scripts/set-domain.sh https://votredomaine.fr"
+ echo ""
+ echo "Exemples :"
+ echo " bash scripts/set-domain.sh https://lavoixdupeuple.fr"
+ echo " bash scripts/set-domain.sh http://192.168.1.10:8080"
+ exit 1
+fi
+
+ENV_FILE=".env"
+
+# Créer .env si absent
+if [ ! -f "$ENV_FILE" ]; then
+ touch "$ENV_FILE"
+ echo "Fichier .env créé."
+fi
+
+# Mettre à jour ou ajouter VITE_APP_URL
+if grep -q "^VITE_APP_URL=" "$ENV_FILE"; then
+ sed -i "s|^VITE_APP_URL=.*|VITE_APP_URL=${DOMAIN}|" "$ENV_FILE"
+ echo "VITE_APP_URL mis à jour : ${DOMAIN}"
+else
+ echo "VITE_APP_URL=${DOMAIN}" >> "$ENV_FILE"
+ echo "VITE_APP_URL ajouté : ${DOMAIN}"
+fi
+
+# Exporter pour que Vite le lise pendant le build
+export VITE_APP_URL="${DOMAIN}"
+
+echo ""
+echo "Reconstruction du frontend..."
+pnpm --filter @workspace/voix-du-peuple run build --config vite.config.selfhost.ts
+
+echo ""
+echo "Terminé. Le QR code pointe maintenant vers : ${DOMAIN}"
+echo "Redémarrez Nginx si le build est en production : systemctl reload nginx"