groupe-meditation/docs/DEPLOIEMENT.md
2026-04-17 04:04:04 -04:00

11 KiB

Guide de déploiement — Groupe Méditation

Debian Bookworm + NGINX + PostgreSQL

Testé sur Debian 12 (Bookworm) avec un utilisateur ansible ayant accès sudo.

Principe : on utilise ce que Debian fournit. Pas de dépôt externe.


0. Prérequis système

# Définir un mot de passe pour l'utilisateur ansible (indispensable pour récupération console)
sudo passwd ansible

sudo apt update && sudo apt upgrade -y
sudo apt install -y curl ca-certificates gnupg

1. Installer PostgreSQL

On installe le PostgreSQL fourni par Debian Bookworm (version 15) :

sudo apt install -y postgresql postgresql-client
sudo systemctl enable postgresql
sudo systemctl start postgresql

Créer l'utilisateur et la base. Choisissez votre mot de passe maintenant — il sera réutilisé à l'étape 6 :

sudo -u postgres psql << 'SQL'
CREATE USER grpmed WITH PASSWORD 'votre_mot_de_passe_ici';
CREATE DATABASE groupe_meditation OWNER grpmed;
GRANT ALL PRIVILEGES ON DATABASE groupe_meditation TO grpmed;
\c groupe_meditation
GRANT ALL ON SCHEMA public TO grpmed;
SQL

Vérifier la connexion. Important : utiliser 127.0.0.1, pas localhost (asyncpg résout localhost en IPv6 ::1 sur Debian Bookworm, tandis que PostgreSQL n'écoute que sur 127.0.0.1) :

PGPASSWORD='votre_mot_de_passe_ici' psql -U grpmed -h 127.0.0.1 -d groupe_meditation -c "SELECT 1;"

2. Installer Node.js

On installe le Node.js fourni par Debian Bookworm (version 18) :

sudo apt install -y nodejs npm

Vérifier :

node --version
npm --version

3. Installer Python et les dépendances système

sudo apt install -y python3 python3-pip python3-venv

# Dépendances système pour WeasyPrint (rapports PDF)
sudo apt install -y libpango-1.0-0 libpangocairo-1.0-0 \
  libgdk-pixbuf2.0-0 libffi-dev libcairo2

4. Installer NGINX

sudo apt install -y nginx
sudo systemctl enable nginx
sudo systemctl start nginx

5. Déployer le code source

# Copier l'archive sur le serveur, puis :
cd ~
unzip groupe-meditation-v12.zip

# Backend
sudo mkdir -p /opt/groupe-meditation
sudo cp -r ~/groupe-meditation/backend/ /opt/groupe-meditation/backend/
sudo cp -r ~/groupe-meditation/deploy/ /opt/groupe-meditation/deploy/

# Environnement virtuel Python
cd /opt/groupe-meditation/backend
sudo python3 -m venv venv
sudo /opt/groupe-meditation/backend/venv/bin/pip install -r requirements.txt

6. Configurer le backend

Générer le secret JWT :

openssl rand -base64 48

Créer le .env :

cd /opt/groupe-meditation/backend
sudo cp .env.example .env
sudo nano .env

Modifier .env avec le même mot de passe que l'étape 1.

⚠️ Utiliser 127.0.0.1, jamais localhost dans DATABASE_URL :

DATABASE_URL=postgresql+asyncpg://grpmed:votre_mot_de_passe_ici@127.0.0.1:5432/groupe_meditation
JWT_SECRET=valeur_generee_ci-dessus
CORS_ORIGINS=["https://groupe-meditation.87-16.org"]
INVITATION_EXPIRE_HOURS=48

Permissions du .env — obligatoire (le service tourne en www-data) :

sudo chown www-data:www-data /opt/groupe-meditation/backend/.env
sudo chmod 640 /opt/groupe-meditation/backend/.env

7. Initialiser la base de données

Le seed doit être lancé depuis le dossier backend/ pour que le fichier .env soit trouvé :

cd /opt/groupe-meditation/backend
/opt/groupe-meditation/backend/venv/bin/python ../deploy/seed.py

Si le .env n'est pas trouvé, le script utilisera les valeurs par défaut de config.py (qui ont un autre mot de passe) et la connexion échouera.

Le script crée interactivement :

  • Les tables (23+)
  • Les 10 postes prédéfinis
  • La littérature de base (5 titres)
  • Le premier groupe et le premier membre
  • Un code d'invitation valide 7 jours

7b. Mise à jour d'une base existante (v11 → v12)

Si la base existe déjà, exécuter ces commandes SQL avant de relancer le service :

PGPASSWORD='votre_mot_de_passe_ici' psql -U grpmed -h 127.0.0.1 -d groupe_meditation << 'SQL'
-- ══ Membre : courriel obligatoire, nom optionnel, téléphone obligatoire ══
-- Remplir les courriels manquants avant de rendre NOT NULL
UPDATE membres SET courriel = CONCAT(LOWER(prenom), '_', LEFT(id::text, 4), '@temp.local')
  WHERE courriel IS NULL OR courriel = '';
UPDATE membres SET telephone = '000-000-0000'
  WHERE telephone IS NULL OR telephone = '';
ALTER TABLE membres ALTER COLUMN courriel SET NOT NULL;
ALTER TABLE membres ALTER COLUMN telephone SET NOT NULL;
ALTER TABLE membres ALTER COLUMN nom DROP NOT NULL;
-- Contrainte d'unicité courriel+groupe
ALTER TABLE membres ADD CONSTRAINT uq_courriel_groupe UNIQUE (courriel, groupe_id);

-- ══ Tâches ══
CREATE TABLE IF NOT EXISTS taches (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  nom VARCHAR(200) NOT NULL,
  informations TEXT,
  ordre_affichage INTEGER DEFAULT 99,
  cree_le TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS postes_taches (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  poste_id UUID NOT NULL REFERENCES postes(id),
  tache_id UUID NOT NULL REFERENCES taches(id),
  cree_le TIMESTAMPTZ DEFAULT NOW()
);

-- ══ Poste : champ informations ══
ALTER TABLE postes ADD COLUMN IF NOT EXISTS informations TEXT;

-- ══ Dépenses : preuve, methode nullable ══
ALTER TABLE depenses ADD COLUMN IF NOT EXISTS preuve_nom VARCHAR(255);
ALTER TABLE depenses ALTER COLUMN methode DROP NOT NULL;

-- ══ Candidatures : type, nomination, retrait ══
-- Ajouter les nouvelles valeurs au type ENUM
DO $$ BEGIN
  ALTER TYPE statut_candidature ADD VALUE IF NOT EXISTS 'retiree';
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
DO $$ BEGIN
  CREATE TYPE type_candidature AS ENUM ('candidature', 'nomination');
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
ALTER TABLE candidatures ADD COLUMN IF NOT EXISTS type VARCHAR(20) DEFAULT 'candidature';
ALTER TABLE candidatures ADD COLUMN IF NOT EXISTS nomme_par UUID REFERENCES membres(id);
ALTER TABLE candidatures ADD COLUMN IF NOT EXISTS acceptee_par_membre BOOLEAN;
ALTER TABLE candidatures ADD COLUMN IF NOT EXISTS proposition_id UUID REFERENCES propositions(id);

-- ══ Envoi contributions : résolution, engagement ══
ALTER TABLE envois_contributions ADD COLUMN IF NOT EXISTS resolution_id UUID REFERENCES propositions(id);
ALTER TABLE envois_contributions ADD COLUMN IF NOT EXISTS engagee BOOLEAN DEFAULT false;
ALTER TABLE envois_contributions ADD COLUMN IF NOT EXISTS debitee BOOLEAN DEFAULT false;

-- ══ Propositions : type et lien candidature ══
ALTER TABLE propositions ADD COLUMN IF NOT EXISTS type_proposition VARCHAR(20) DEFAULT 'generale';
ALTER TABLE propositions ADD COLUMN IF NOT EXISTS candidature_id UUID REFERENCES candidatures(id);

-- ══ Répertoire des preuves ══
-- Créer le répertoire manuellement :
-- sudo mkdir -p /opt/groupe-meditation/uploads/preuves
-- sudo chown www-data:www-data /opt/groupe-meditation/uploads/preuves
SQL

Ensuite créer le répertoire des preuves et relancer :

sudo mkdir -p /opt/groupe-meditation/uploads/preuves
sudo chown www-data:www-data /opt/groupe-meditation/uploads/preuves
sudo systemctl restart groupe-meditation

8. Créer le service systemd

sudo cp /opt/groupe-meditation/deploy/groupe-meditation.service /etc/systemd/system/
sudo chown -R www-data:www-data /opt/groupe-meditation
sudo systemctl daemon-reload
sudo systemctl enable groupe-meditation
sudo systemctl start groupe-meditation

Vérifier :

sudo systemctl status groupe-meditation
curl -s http://127.0.0.1:8000/api/health

Doit retourner {"status":"ok","app":"Groupe Méditation","version":"0.1.0"}


9. Construire et déployer le frontend

cd ~/groupe-meditation/frontend
npm install
npm run build

sudo mkdir -p /var/www/groupe-meditation
sudo cp -r dist/* /var/www/groupe-meditation/
sudo chown -R www-data:www-data /var/www/groupe-meditation

10. Configurer NGINX

NGINX fait tout : fichiers statiques du frontend + reverse proxy vers FastAPI.

sudo cp ~/groupe-meditation/deploy/nginx-groupe-meditation.conf \
  /etc/nginx/sites-available/groupe-meditation
sudo ln -sf /etc/nginx/sites-available/groupe-meditation /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default

sudo nginx -t
sudo systemctl reload nginx

Tester en HTTP :

curl -s http://groupe-meditation.87-16.org/api/health

11. Certificat TLS (Let's Encrypt)

sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d groupe-meditation.87-16.org

Certbot modifie automatiquement la config NGINX pour ajouter HTTPS et la redirection 80→443.


12. Sauvegarde automatique

sudo cp ~/groupe-meditation/deploy/backup.sh /etc/cron.daily/groupe-meditation-backup
sudo chmod +x /etc/cron.daily/groupe-meditation-backup

13. Vérification finale

# API
curl -s https://groupe-meditation.87-16.org/api/health | python3 -m json.tool

# Documentation OpenAPI
# https://groupe-meditation.87-16.org/api/docs

# Frontend
# https://groupe-meditation.87-16.org

Mise à jour de l'application

# 1. Copier les nouveaux fichiers
cd ~
unzip groupe-meditation-vXX.zip

# 2. Backend
sudo cp -r ~/groupe-meditation/backend/ /opt/groupe-meditation/backend/
cd /opt/groupe-meditation/backend
sudo venv/bin/pip install -r requirements.txt

# 3. Migrations SQL si nécessaire (voir section 7b)

# 4. Permissions
sudo chown -R www-data:www-data /opt/groupe-meditation
sudo chown www-data:www-data /opt/groupe-meditation/backend/.env
sudo chmod 640 /opt/groupe-meditation/backend/.env

# 5. Relancer le service
sudo systemctl restart groupe-meditation

# 6. Frontend
cd ~/groupe-meditation/frontend
npm install && npm run build
sudo cp -r dist/* /var/www/groupe-meditation/

Dépannage

Problème Commande / Solution
API ne démarre pas sudo journalctl -u groupe-meditation -f
Workers bloqués au démarrage DATABASE_URL doit utiliser 127.0.0.1 (pas localhost)
InvalidPasswordError au seed Lancer depuis backend/, vérifier .env lisible
InvalidPasswordError à l'API sudo chown www-data:www-data .env && sudo chmod 640 .env
NGINX 502 Bad Gateway sudo systemctl status groupe-meditation — Uvicorn démarré ?
NGINX 504 Gateway Timeout Worker bloqué DB — vérifier 127.0.0.1 dans DATABASE_URL
WeasyPrint échoue sudo apt install libpango-1.0-0 libpangocairo-1.0-0
seed.py ImportError Vérifier les noms de classes dans models/
Console inaccessible sudo passwd ansible — à faire en tout début
type "..." does not exist après ALTER Relancer le service pour que create_all crée les types manquants
Nouvelle colonne absente Exécuter les ALTER TABLE de la section 7b

Hébergé au Québec · 100 % logiciel libre · Zéro traçage