# Déploiement Le pipeline [`.gitlab-ci.yml`](../.gitlab-ci.yml) se déclenche sur **chaque push** : 1. tests (backend : `ruff` + `pytest` + `alembic check` ; frontend : `typecheck` + `lint` + `build` + `smoke`) ; 2. sur `main` seulement : build des images `backend` et `frontend`, poussées sur le registre de conteneurs du projet GitLab ; 3. copie de `deploy/docker-compose.prod.yml` sur le VPS par SSH ; 4. `docker compose pull`, migration Alembic, `up -d`. Le VPS n'expose rien directement : un **Caddy déjà installé sur le serveur** (service système, hors de ce dépôt) reçoit le trafic public sur 443 et reverse-proxy vers les deux conteneurs, qui n'écoutent que sur `127.0.0.1`. ## Avant d'ouvrir au public — check-list sécurité - `JWT_SECRET` : une vraie valeur générée (`openssl rand -hex 32`), jamais la valeur par défaut de `.env.example`. Le backend refuse de démarrer en `ENVIRONMENT=production` sinon (`app/config.py:check_production_secrets`). - `EMAIL_ENABLED=true` avec un SMTP qui fonctionne (`docker compose run --rm backend uv run python -m app.email.check ` pour vérifier) — sinon les liens magiques sont journalisés en clair au lieu d'être envoyés (le backend avertit au démarrage tant que c'est le cas, voir plus bas). - Édition créée via `python -m app.bootstrap` (étape 3), **jamais** via `python -m app.seed` : ce dernier crée un compte de démonstration (`coordination@lucioles.test`) avec un mot de passe versionné dans le dépôt — un vrai identifiant de connexion COORDINATION public si la base de démo devient la base de production. - `CORS_ORIGINS` : sans objet ici tant que le frontend appelle l'API en même origine via `/api/*` (voir le vhost Caddy, étape 5) — à ne renseigner que si un autre client (domaine séparé, appli mobile) doit appeler l'API directement. - `/auth/login` et `/auth/magic-link` sont limités en tentatives par email (5/min et 3/5 min) — voir `app/auth/rate_limit.py`. Limité par email et non par IP (le VPS ne transmet pas encore l'IP réelle du client à travers Caddy) : suffisant contre la force brute sur un compte connu, pas contre une attaque distribuée sur de nombreux emails différents. ## Étape 0 — pousser le dépôt sur GitLab ```bash # Créer d'abord un projet vide sur GitLab (sans README ni .gitignore), puis : git remote add origin git@gitlab.com:/.git git push -u origin main ``` ## Variables CI/CD à définir (Paramètres → CI/CD → Variables) | Variable | Description | |-------------------|-------------------------------------------------------------------| | `SSH_HOST` | Hôte du VPS | | `SSH_USER` | Utilisateur SSH | | `SSH_KEY` | Clé privée SSH (format OpenSSH) — **masquée**, sur `main` seulement| | `DEPLOY_PATH` | Dossier du projet sur le VPS (ex. `/opt/benevoles`) | | `VITE_EDITION_ID` | UUID de l'édition de production — **à ajouter après** l'étape 3 | `CI_REGISTRY`, `CI_REGISTRY_USER`, `CI_REGISTRY_PASSWORD` et `CI_REGISTRY_IMAGE` sont fournies automatiquement par GitLab, rien à configurer. La clé publique correspondant à `SSH_KEY` doit être dans le `~/.ssh/authorized_keys` de `SSH_USER` sur le VPS. ## Étape 1 — préparation du VPS (une fois) ```bash mkdir -p /opt/benevoles cd /opt/benevoles cat > .env <<'EOF' POSTGRES_USER=benevoles POSTGRES_PASSWORD= POSTGRES_DB=benevoles JWT_SECRET= PUBLIC_BASE_URL=https://benevoles.mon-domaine.fr # Emails (récapitulatifs de planning + liens de connexion). Laisser # EMAIL_ENABLED=false tant que le SMTP n'est pas prêt : les emails sont alors # seulement journalisés — un jeton de lien magique en clair dans les logs du # conteneur permet de se connecter à la place de n'importe qui, donc à # repasser à true avant l'ouverture au public (le backend affiche un # avertissement au démarrage tant que c'est le cas). EMAIL_ENABLED=true SMTP_HOST=smtp.mon-domaine.fr SMTP_PORT=587 SMTP_USER=no-reply@mon-domaine.fr SMTP_PASSWORD= SMTP_STARTTLS=true SMTP_SSL=false SMTP_FROM=Festival EOF ``` Le pipeline dépose `docker-compose.prod.yml` dans ce dossier et l'utilise tel quel — il n'y a rien d'autre à y copier. ## Étape 2 — premier déploiement (backend + base seuls) Le frontend a besoin de l'UUID d'une édition pour être construit (voir plus bas) : le tout premier déploiement se fait donc en deux temps. 1. Sur GitLab, lancer le pipeline sur `main` normalement (Push → CI/CD → Pipelines, ou un simple commit). Le job `build-frontend` échouera ou produira une image sans édition tant que `VITE_EDITION_ID` n'existe pas : c'est attendu, on l'ignore pour l'instant. 2. Une fois `build-backend` et `deploy` passés, `db` et `backend` tournent sur le VPS. ## Étape 3 — créer l'édition de production ```bash ssh SSH_USER@SSH_HOST cd /opt/benevoles docker compose -f docker-compose.prod.yml run --rm backend \ uv run python -m app.bootstrap \ --name "Festival 2026" --start 2026-07-01 --end 2026-07-04 \ --email coordination@mon-domaine.fr --password ``` La commande affiche l'UUID de l'édition créée. Récupérer aussi celui-ci à tout moment avec : ```bash docker compose -f docker-compose.prod.yml exec -T db \ psql -U benevoles -tAc "select id from edition order by created_at desc limit 1" ``` ## Étape 4 — brancher le frontend sur cette édition Dans GitLab, ajouter la variable CI/CD `VITE_EDITION_ID` avec cet UUID, puis relancer le pipeline sur `main` (bouton *Retry* sur le dernier pipeline, ou un nouveau commit). Cette fois `build-frontend` construit une image qui pointe sur la bonne édition, et `deploy` démarre le service `frontend`. `VITE_EDITION_ID` ne change plus ensuite : elle ne se touche à nouveau que pour une nouvelle édition (festival de l'année suivante) qu'on voudrait publier sur ce même domaine. ## Étape 5 — le vhost Caddy Caddy tourne déjà sur ce serveur (service système) : ajouter le bloc [`deploy/Caddyfile.benevoles`](./Caddyfile.benevoles) à son `Caddyfile` (ou au fichier qu'il importe pour les sites), avec le vrai nom de domaine, puis : ```bash sudo caddy validate --config /etc/caddy/Caddyfile sudo systemctl reload caddy ``` Le DNS du domaine doit déjà pointer (enregistrement `A`/`AAAA`) vers ce VPS — Caddy obtient et renouvelle seul le certificat HTTPS (Let's Encrypt) au premier accès. Vérifier ensuite que `PUBLIC_BASE_URL` dans le `.env` du VPS (étape 1) correspond bien à `https://` — c'est cette valeur qui est utilisée dans les liens des emails envoyés aux bénévoles. ## Déploiements suivants Un simple push sur `main` suffit : tests → build → déploiement → migration → redémarrage, sans aucune intervention sur le VPS ni sur Caddy.