From 5da165f7390767058050342261e8dfc6a2aebad7 Mon Sep 17 00:00:00 2001 From: Matthieu Date: Wed, 20 May 2026 17:37:17 +0200 Subject: [PATCH] =?UTF-8?q?docs=20:=20corrige=20le=20d=C3=A9ploiement=20pr?= =?UTF-8?q?od=20(Docker)=20et=20documente=20les=20variables=20d'env=20mail?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - README : section Variables d'environnement (ENCRYPTION_KEY, LOCK_DSN) + section Déploiement passée au flow Docker (deploy.sh) - mail-cron-setup : sépare dev (make, php-lesstime-fpm) et prod (lesstime-app, docker compose exec), cron prod réel - infra/prod/.env.example : ajoute ENCRYPTION_KEY et LOCK_DSN (manquaient, requis pour la sync mail) Co-Authored-By: Claude Opus 4.7 (1M context) --- README.md | 43 +++++++++-- docs/mail-cron-setup.md | 153 ++++++++++++++++++++++------------------ infra/prod/.env.example | 7 ++ 3 files changed, 128 insertions(+), 75 deletions(-) diff --git a/README.md b/README.md index 6b863e0..ba7b601 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,10 @@ make install L'application est accessible sur **http://localhost:8082**. +Les valeurs par défaut du `.env` committé suffisent pour démarrer en local. Pour la prod +(et pour activer la messagerie), surcharger les variables sensibles dans `.env.local` — +voir « Variables d'environnement » ci-dessous. + ### Comptes de test (fixtures) | Utilisateur | Mot de passe | Rôle | Détails | @@ -56,6 +60,25 @@ L'application est accessible sur **http://localhost:8082**. | `client-liot` | `client` | ROLE_CLIENT | Client LIOT (projet SIRH) | | `client-acme` | `client` | ROLE_CLIENT | Client ACME (projet CRM) | +## Variables d'environnement + +Les variables sont définies dans `.env` (committé, valeurs par défaut pour le dev) et +peuvent être surchargées dans `.env.local` (jamais committé). En prod, elles vont dans le +`.env` du serveur (`/var/www/lesstime/.env`, voir `infra/prod/.env.example`). + +| Variable | Rôle | Défaut dev | À fixer en prod | +|----------|------|-----------|-----------------| +| `APP_SECRET` | Secret Symfony | placeholder | ✅ (hex 32) | +| `JWT_PASSPHRASE` | Passphrase des clés JWT | placeholder | ✅ | +| `DATABASE_URL` | Connexion PostgreSQL | container `db` | ✅ (`host.docker.internal`) | +| `CORS_ALLOW_ORIGIN` | Origines CORS autorisées | localhost | ✅ (domaine prod) | +| **`ENCRYPTION_KEY`** | **Clé hex 32 bytes chiffrant les credentials IMAP/SMTP (feature mail)** | placeholder | ✅ — doit rester **stable**, sinon les credentials mail stockés deviennent illisibles | +| **`LOCK_DSN`** | **Store de verrous Symfony pour la sync mail (anti-chevauchement)** | `flock` | `flock` suffit | + +> **Messagerie** : `ENCRYPTION_KEY` et `LOCK_DSN` sont introduites par l'intégration mail. +> Détails de config et cron de synchronisation : `docs/mail-integration.md` et `docs/mail-cron-setup.md`. +> Générer une clé : `php -r "echo bin2hex(random_bytes(32));"`. + ## Commandes ### Docker @@ -218,13 +241,19 @@ docker exec -u www-data php-lesstime-fpm php bin/console app:generate-api-token ## Déploiement -1. Déployer le code sur le serveur -2. `composer install --no-dev --optimize-autoloader` -3. `php bin/console doctrine:migrations:migrate --no-interaction` -4. `php bin/console cache:clear --env=prod` -5. `cd frontend && npm install && npm run build:dist` -6. `docker restart nginx-lesstime` -7. Ouvrir le port 8082 sur le firewall (LAN uniquement) +La prod tourne en **Docker** : l'image est buildée par la CI Gitea sur push de tag `v*` +(`gitea.malio.fr/malio-dev/lesstime:`), puis déployée par le script `deploy.sh` sur +le serveur (dossier `/var/www/lesstime`, container `lesstime-app`). + +```bash +# Sur le serveur, depuis /var/www/lesstime +sudo ./deploy.sh # déploie la dernière image (latest) +sudo ./deploy.sh v0.4.2 # déploie une version précise +``` + +Le script active la maintenance, pull l'image, redémarre le container, lance les migrations +et vide le cache. Guide complet (première installation, BDD, Nginx, JWT, rollback) : +**`doc/deployment-docker.md`**. ## Licence diff --git a/docs/mail-cron-setup.md b/docs/mail-cron-setup.md index 78a5b2e..74c51cb 100644 --- a/docs/mail-cron-setup.md +++ b/docs/mail-cron-setup.md @@ -3,44 +3,24 @@ ## Vue d'ensemble La synchronisation IMAP est déclenchée par un cron OS toutes les 10 minutes. -Elle appelle la commande Symfony `app:mail:sync` qui s'exécute dans le container PHP. +Elle appelle la commande Symfony `app:mail:sync` qui s'exécute **dans le container PHP**. Un Symfony Lock (`mail.sync`, TTL 10 min, store `flock` via `LOCK_DSN=flock`) empêche les runs de se chevaucher si une sync prend plus de 10 min. +> **Dev vs prod** — en dev le container s'appelle `php-lesstime-fpm` et on passe par `make`. +> En **production** le container s'appelle `lesstime-app` (service `app` du `docker-compose.yml` +> dans `/var/www/lesstime`), il n'y a **pas de `make`** : tout passe par `docker compose` / `docker exec`. + ## Prérequis -- Container `php-lesstime-fpm` démarré (`make start`) -- `MailConfiguration.enabled = true` (configurable depuis l'admin — Phase 7) -- `ENCRYPTION_KEY` défini dans `infra/dev/.env.docker.local` (ou production env) - -## Installation du cron - -Sur la **machine hôte** (pas dans le container) : - -```bash -crontab -e -``` - -Ajouter la ligne suivante (adapter le chemin) : - -```cron -*/10 * * * * cd /home/r-dev/malio-dev/Lesstime && make mail-sync >> /var/log/lesstime-mail-sync.log 2>&1 -``` - -Ou directement via `docker exec` (sans dépendance à `make`) : - -```cron -*/10 * * * * docker exec php-lesstime-fpm php bin/console app:mail:sync >> /var/log/lesstime-mail-sync.log 2>&1 -``` - -### Avec un utilisateur système dédié - -Si le cron est configuré pour un utilisateur système spécifique (ex: `www-data` ou `deploy`) : - -```bash -sudo crontab -u deploy -e -``` +- `MailConfiguration.enabled = true` (configurable depuis l'admin — onglet « Mail ») +- `ENCRYPTION_KEY` (clé hex 32 bytes) défini dans l'environnement : + - **dev** : `infra/dev/.env.docker.local` + - **prod** : `/var/www/lesstime/.env` +- Container démarré : + - **dev** : `make start` (container `php-lesstime-fpm`) + - **prod** : déployé via `sudo ./deploy.sh` (container `lesstime-app`) ## Variables d'environnement nécessaires @@ -49,63 +29,100 @@ sudo crontab -u deploy -e | `ENCRYPTION_KEY` | Clé hex 32 bytes pour déchiffrer le password IMAP | `$(php -r "echo bin2hex(random_bytes(32));")` | | `LOCK_DSN` | DSN du store de verrous Symfony | `flock` (défaut, fichier local) | -La clé doit être la même que celle utilisée pour chiffrer le password lors de la configuration. +La clé `ENCRYPTION_KEY` doit être **identique** à celle utilisée pour chiffrer le password +lors de la configuration depuis l'admin. Si elle change, les credentials stockés deviennent illisibles. -## Checklist setup production +--- -1. [ ] Définir `ENCRYPTION_KEY` dans les variables d'environnement production -2. [ ] Créer le compte mail dédié (ex: `lesstime@votre-domaine.fr`) chez OVH -3. [ ] Accéder à `/admin` → onglet "Mail" → renseigner les credentials IMAP/SMTP -4. [ ] Cliquer "Tester la connexion" → vérifier le succès -5. [ ] Cocher "Activer la synchronisation" → Enregistrer -6. [ ] Installer le cron OS (voir section "Installation du cron") -7. [ ] Vérifier les logs après la première sync : `make logs-dev` (chercher `mail.sync`) +## Dev -## Commandes utiles +### Lancer une sync à la main ```bash -# Sync complète (toutes les boîtes) -make mail-sync +make mail-sync # sync complète (toutes les boîtes) +make mail-sync FOLDER=INBOX # un seul dossier (doit déjà exister en base) +make mail-sync DRYRUN=1 # simulation (dry-run, pas d'écriture BDD) +``` -# Sync d'un seul dossier (le dossier doit déjà exister en base) -make mail-sync FOLDER=INBOX +Ou directement dans le container : -# Simulation (dry-run, pas d'écriture BDD) -make mail-sync DRYRUN=1 - -# Directement dans le container +```bash docker exec php-lesstime-fpm php bin/console app:mail:sync docker exec php-lesstime-fpm php bin/console app:mail:sync --folder=INBOX docker exec php-lesstime-fpm php bin/console app:mail:sync --dry-run ``` -## Logs - -Les logs Symfony sont dans `var/log/dev.log` (ou `prod.log` en production). -Suivre les logs en temps réel : +### Logs (dev) ```bash -make logs-dev +make logs-dev # tail -f var/log/dev.log ``` Les messages loggés par `MailSyncService` sont préfixés `mail.sync`. +--- + +## Production + +En prod, l'app tourne dans le container `lesstime-app` déployé par `sudo ./deploy.sh` +(dossier `/var/www/lesstime`). La commande s'exécute en tant que `www-data` (uid 33), +comme les migrations lancées par `deploy.sh`. + +### Lancer une sync à la main + +Depuis `/var/www/lesstime` : + +```bash +sudo docker compose exec -T -u www-data app php bin/console app:mail:sync +sudo docker compose exec -T -u www-data app php bin/console app:mail:sync --folder=INBOX +sudo docker compose exec -T -u www-data app php bin/console app:mail:sync --dry-run +``` + +### Installer le cron + +Sur la **machine hôte** (pas dans le container). Comme `docker` requiert `sudo` en prod, +installer le cron sous root : + +```bash +sudo crontab -e +``` + +Ajouter : + +```cron +*/10 * * * * cd /var/www/lesstime && docker compose exec -T -u www-data app php bin/console app:mail:sync >> /var/log/lesstime-mail-sync.log 2>&1 +``` + +> Le crontab de root exécute déjà les commandes en root → pas de `sudo` à l'intérieur de la ligne cron. +> La commande est **idempotente** (UIDs uniques en base) : la relancer ne duplique pas les données. + +### Logs (prod) + +```bash +cd /var/www/lesstime +docker compose logs -f --tail=100 app # logs container +docker compose exec app cat var/log/prod.log # log Symfony (volume lesstime_logs) +``` + +### Checklist setup production + +1. [ ] Définir `ENCRYPTION_KEY` (hex 32 bytes) et `LOCK_DSN=flock` dans `/var/www/lesstime/.env` +2. [ ] Créer le compte mail dédié (ex: `lesstime@votre-domaine.fr`) chez OVH +3. [ ] Accéder à `/admin` → onglet « Mail » → renseigner les credentials IMAP/SMTP +4. [ ] Cliquer « Tester la connexion » → vérifier le succès +5. [ ] Cocher « Activer la synchronisation » → Enregistrer +6. [ ] Lancer une sync manuelle pour valider (commande ci-dessus) +7. [ ] Installer le cron OS (voir « Installer le cron ») +8. [ ] Vérifier les logs après la première sync (`docker compose logs -f app`, chercher `mail.sync`) + +--- + ## Sécurité - Le password IMAP est **toujours stocké chiffré** (libsodium secretbox) - Les corps de mails, passwords et pièces jointes ne sont **jamais loggés** - Le lock `flock` évite les runs parallèles (fichier dans `/tmp/sf.mail.sync..lock`) - -## Rappels sécurité - - La page `/mail` et tous les endpoints `/api/mail/*` sont refusés aux `ROLE_CLIENT` exclusifs -- Le sidebar "Messagerie" est masqué pour les utilisateurs ROLE_CLIENT sans ROLE_USER -- Le password IMAP est chiffré via libsodium secretbox avant stockage (jamais en clair en base) -- Les corps de mails sont sanitisés via DOMPurify avant affichage (voir `frontend/utils/sanitizeMailHtml.ts`) -- Les pixels tracking distants sont remplacés par un placeholder -- Aucun body mail, password ou contenu de pièce jointe n'est loggé - -## Production - -En production, préférer un cron système ou un job scheduler (Kubernetes CronJob, ECS Scheduled Task, etc.). -La commande est idempotente : relancer plusieurs fois ne duplique pas les données (UIDs uniques en base). +- Le sidebar « Messagerie » est masqué pour les utilisateurs `ROLE_CLIENT` sans `ROLE_USER` +- Les corps de mails sont sanitisés via DOMPurify avant affichage (`frontend/utils/sanitizeMailHtml.ts`) +- Les pixels de tracking distants sont remplacés par un placeholder diff --git a/infra/prod/.env.example b/infra/prod/.env.example index d0caa7c..781f0b2 100644 --- a/infra/prod/.env.example +++ b/infra/prod/.env.example @@ -15,6 +15,13 @@ JWT_COOKIE_SAMESITE=lax JWT_TOKEN_TTL=86400 JWT_COOKIE_TTL=86400 +# Mail (intégration IMAP/SMTP) +# Clé hex 32 bytes chiffrant les credentials mail stockés en base. +# Générer : php -r "echo bin2hex(random_bytes(32));" — doit rester STABLE. +ENCRYPTION_KEY=change-me +# Store de verrous Symfony pour la sync mail (anti-chevauchement du cron). +LOCK_DSN=flock + # CORS CORS_ALLOW_ORIGIN='^https?://project\.malio-dev\.fr$'