From 39eb8cfc135743d2137928f0a23ab56d9b9b3db0 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 14 Sep 2026 16:23:32 +0000 Subject: [PATCH] Documente le fil d'informations et la messagerie MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CLAUDE.md gagne les deux sections et, surtout, les pièges qui coûteraient une soirée à retrouver : `db.execute` qui rend les colonnes brutes, les deux interrupteurs de boucle qu'il ne faut pas confondre, la rotation du jeton Microsoft, l'adresse d'expédition lue chez le fournisseur, et la raison pour laquelle les mots de passe temporaires ne passent jamais par la file. README et .env.example décrivent les trois transports, le piège des sept jours côté Google, et le repli SMTP par variables d'environnement qui permet un premier déploiement sans démarche préalable. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_014QsLjRAnuLwivqxCbM4WeP --- .env.example | 26 ++++++++++++++ CLAUDE.md | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++- README.md | 49 +++++++++++++++++++++++++-- 3 files changed, 167 insertions(+), 4 deletions(-) diff --git a/.env.example b/.env.example index 1f6a36f..9cb66b3 100644 --- a/.env.example +++ b/.env.example @@ -72,3 +72,29 @@ FACEBOOK_PAGE_ACCESS_TOKEN= LINKEDIN_ORGANIZATION_URN= LINKEDIN_ORGANIZATION_ID= LINKEDIN_ACCESS_TOKEN= + +# ---- Boîte mail de l'association ---- +# La configuration se fait dans Backend › Boîte mail : on y branche Google +# (API Gmail), Microsoft (API Graph) ou un serveur SMTP, et les secrets sont +# stockés chiffrés. Rien à mettre ici dans le cas normal. +# +# Les jetons et mots de passe utilisent la même clé que les réseaux sociaux +# (SOCIAL_TOKEN_KEY, avec repli sur AUTH_SECRET). +# +# `off` arrête la file d'envoi sans toucher au reste (le libérateur de +# publications programmées a son propre interrupteur, PROMO_SCHEDULER). +#MAIL_WORKER=off +# Débit de la file, en messages par passage (défaut : 20). Gmail plafonne +# autour de 500 destinataires par jour sur un compte gratuit. +#MAIL_RATE_PER_MINUTE=20 + +# --- Repli avant toute configuration depuis le backoffice --- +# Permet un premier déploiement sans passer par l'écran. Dès qu'un compte est +# enregistré dans Backend › Boîte mail, il prime sur ces variables. +SMTP_HOST= +SMTP_PORT=465 +SMTP_SECURE=true +SMTP_USER= +SMTP_PASSWORD= +SMTP_FROM= +SMTP_FROM_NAME= diff --git a/CLAUDE.md b/CLAUDE.md index 638e0e8..f213cf9 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -202,7 +202,11 @@ même raison. - Sessions JWT limitées à 7 jours (`auth.config.ts`), HSTS et suppression de `X-Powered-By` dans `next.config.mjs`. Le port Postgres de `docker-compose` n'est publié que sur `127.0.0.1`. -- `npm test` verrouille ces protections (`tests/security.test.ts`). +- `npm test` verrouille ces protections (`tests/security.test.ts`) : XSS du + journal, limitation des connexions, chiffrement des secrets, coordonnées du + référent, **mots de passe temporaires jamais mis en file**, secrets de + messagerie jamais renvoyés au navigateur, aucune copie cachée dans la chaîne + d'envoi, et rien rendu en HTML brut côté informations. ## Référencement (SEO) @@ -274,6 +278,96 @@ même raison. `admin` > `moderator` > `editor` are staff; `member` is an adhérent linked to a `members` row via `users.memberId`. Capability matrix is in `src/lib/rbac.ts`. +`manageInformations` (admin + modérateur) ouvre la rédaction des informations ; +écrire à tous les adhérents reste sous `manageEmails` (admin seul), et la +configuration de la boîte mail sous `manageSettings`. Un modérateur publie donc +une information sans pouvoir la diffuser. + +## Informations adhérents + +Le fil de l'espace adhérent (`/backend/espace/informations`, onglet en tête) +porte ce que publie le bureau depuis `/backend/informations`. Deux tables : +`informations` (brouillon → publiée, `pinned`, `email_sent_at`) et +`information_reads`. + +- **Texte riche** : `src/lib/rich-text.ts` est **pur** (`tests/rich-text.test.ts`) + et analyse un sous-ensemble de Markdown — `**gras**`, `*italique*`, `- puce`, + `1. numéro`, `[texte](https://…)`, `## sous-titre`. Deux rendus, un seul + analyseur : `richTextNodes()` pour l'écran (des éléments React, jamais + `dangerouslySetInnerHTML`), `richTextToEmailHtml()` pour le message. L'aperçu + du formulaire passe par le premier, il est donc fidèle par construction. Un + lien hors `http`/`https` perd sa cible et ne garde que son libellé + (`safeHttpUrl`, partagé avec `email-templates.ts`). +- **Pas de WYSIWYG** : il produirait du HTML, qu'il faudrait stocker puis + assainir — seconde dépendance, seconde surface d'attaque. La saisie est un + `