Remplace la feuille à imprimer par un recueil électronique du consentement,
construit selon l'article 9 du Code civil (droit à l'image = droit de la
personnalité, personnel) :
- Le membre signe une fois, dans son espace, une autorisation GÉNÉRALE
couvrant toute photo de lui — plus besoin de valider chaque publication.
Capture d'une signature dessinée (pad canvas), nom, horodatage et IP :
une signature électronique simple, preuve de consentement (art. 7 RGPD).
Étape proposée au premier accès mais NON bloquante (« Plus tard »).
- Un adulte ne pouvant pas consentir pour un autre, l'image des
accompagnants est recueillie PAR PERSONNE au moment de l'inscription à
la rencontre : case « autorise / n'autorise pas » par participant +
attestation du membre d'avoir informé chaque personne et obtenu son
accord (mineur : représentant légal).
- La publication n'est plus bloquée par le statut de consentement (évite
le blocage systématique) ; le consentement est la preuve conservée.
- Admin : statut d'autorisation dans la liste des membres (avec la
signature consultable) et par participant dans la liste des inscrits
(badges + exports Excel/impression).
- Table image_consents (journal append-only) et colonne
inscriptions.image_consent, migrations idempotentes.
- Politique de confidentialité mise à jour (consentement par personne,
fondement art. 9, cas des mineurs).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fkg15RCxNgUys4ru73He2o
Registre des traitements (art. 30 RGPD) et formulaires d'autorisation de
droit à l'image (majeur / mineur) générés depuis un nouvel onglet « RGPD »
de l'administration (admins uniquement) : documents A4 imprimables et
téléchargeables, pré-remplis à partir des Paramètres de l'association. Le
registre recense les 7 traitements réels de l'application (adhésions,
annuaire, inscriptions, facturation, espace membre, photos, e-mails) avec
finalité, base légale, personnes, données, destinataires et durées ; la
facturation est conservée 10 ans (obligation comptable, art. L123-22).
Politique de confidentialité complétée du traitement de facturation
(adresse, factures, paiements), de la conservation comptable de 10 ans et
des destinataires (trésoriers, expert-comptable, administration fiscale).
HTTPS en production : en-tête HSTS (1 an, includeSubDomains) et redirection
HTTP→HTTPS (308) des requêtes reçues via le reverse proxy (X-Forwarded-Proto),
sans impacter les appels directs (healthcheck). Nouveau drapeau FORCE_HTTPS
dans docker-compose et .env.example, aux côtés de COOKIE_SECURE et
TRUST_PROXY documentés pour la production.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fkg15RCxNgUys4ru73He2o
Nouvelles pages publiques /mentions-legales et /confidentialite,
conformes aux obligations d'une association loi 1901 : LCEN (identité de
l'éditeur, RNA, directeur de la publication, hébergeur), RGPD art. 13
(responsable de traitement, données/finalités/bases légales par
formulaire, durées de conservation, destinataires, droits, réclamation
CNIL), cookies (unique cookie de session exempté de consentement — pas
de bandeau nécessaire), droit à l'image et propriété intellectuelle.
Les pages sont alimentées par les paramètres de l'association existants,
étendus des champs légalement requis : numéro RNA, hébergeur (nom,
adresse, téléphone) et date de mise à jour des pages légales — nouvelle
carte « Pages légales & hébergeur » dans l'onglet Paramètres, avec
alerte listant les champs requis manquants. Un champ vide s'affiche
« à compléter » sur la page publique plutôt que de disparaître.
Également : liens cliquables Mentions légales / Confidentialité dans le
pied de page, et mentions d'information RGPD sous les formulaires
publics (demande d'adhésion, inscription à une rencontre).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fkg15RCxNgUys4ru73He2o
L'email généré était réduit au bloc d'invitation. Il devient une vraie
communication du Club : salutation, message d'introduction, carte de la
rencontre (titre, description, date/heure/lieu/places), bouton
d'inscription, message de conclusion, signature et pied de page contact.
La modale d'administration devient un composeur : les quatre zones de
texte (salutation, introduction, conclusion, signature) sont pré-remplies
avec une rédaction par défaut et modifiables, avec régénération de
l'aperçu pendant la saisie ; vider un champ retire le bloc de l'email.
La route passe en POST avec validation zod des textes (longueurs max).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fkg15RCxNgUys4ru73He2o
Les URL absolues de l'email (logo, lien d'inscription) étaient construites
côté serveur à partir de req.protocol : derrière un reverse proxy HTTPS
(sans TRUST_PROXY), le serveur voit « http » et produit des URL en contenu
mixte que le navigateur bloque — logo cassé dans l'aperçu et dans les
webmails. Le frontend transmet désormais window.location.origin (?base=),
c'est-à-dire l'adresse publique exacte vue par l'admin ; le serveur la
valide (http/https uniquement) et ne garde l'hôte de la requête qu'en
secours.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fkg15RCxNgUys4ru73He2o
Chaque rencontre dispose d'un bouton « ✉ Email » dans l'administration
(admins et modérateurs) qui génère un email HTML prêt à envoyer, aux
couleurs du site avec le logo du club : titre, description, encadré
date/heure/lieu/places restantes et bouton d'inscription.
- Nouvelle route GET /api/admin/rencontres/:id/email : construit l'email
(mise en page en tables + styles inline, compatible clients mail) avec
des URL absolues dérivées de l'hôte de la requête.
- Le bouton de l'email pointe vers /?inscription=<id> : la page d'accueil
ouvre alors automatiquement la modale d'inscription de la rencontre.
- Modale d'aperçu dans l'admin : copie de l'email (HTML enrichi presse-
papiers pour coller directement dans un logiciel de messagerie), copie
de l'objet et du lien d'inscription, téléchargement du fichier .html.
- Les images publiques (logo, uploads) sont maintenant servies avec
Cross-Origin-Resource-Policy: cross-origin pour que le logo se charge
dans les webmails et l'aperçu.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fkg15RCxNgUys4ru73He2o
Le fondu en CSS mask-image ne s'affichait pas correctement : les cartes
en bord de carrousel étaient tronquées net au lieu de s'estomper.
Remplacé par un vrai dégradé en overlay (deux calques blanc→transparent
superposés aux bords), plus fiable visuellement, avec une largeur de
fondu réduite sur mobile.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fkg15RCxNgUys4ru73He2o
- Le carrousel « Nos entreprises membres » est maintenant contenu dans le
même conteneur que le reste des sections (au lieu de déborder pleine
largeur jusqu'aux bords de l'écran).
- Header : la barre de navigation passe en hauteur flexible pour ne plus
être tronquée quand les liens (dont « Rencontres passées ») passent à
la ligne sur petit écran.
- Back-office admin : la barre latérale fixe de 250px passait en dehors
de l'écran sur mobile ; elle se replie désormais en liste verticale
au-dessus du contenu sous 860px.
- Cartes de rencontres passées et carrousel photo plein écran : paddings,
tailles de police et de flèches réduits sur petit écran ; le fond du
carrousel plein écran passe d'un rgba semi-transparent à une couleur
opaque (le fond semi-transparent laissait apparaître le header du site
par transparence sur mobile).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fkg15RCxNgUys4ru73He2o
Le nombre d'entreprises membres et de rencontres organisées provient
maintenant des données réelles (nombre de membres validés, nombre de
rencontres passées) au lieu de chiffres codés en dur. Le nombre de
membres est aussi affiché dans le pied de page.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fkg15RCxNgUys4ru73He2o
Seules les vignettes photo individuelles ouvraient le carrousel ; un clic
ailleurs sur la carte (titre, texte) ne faisait rien. Toute la carte est
maintenant cliquable dès qu'une rencontre a au moins une photo.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fkg15RCxNgUys4ru73He2o
Photos :
- Nouvelle table rencontre_passee_photos (photos illimitées par rencontre
passée, avec position). Migration idempotente au démarrage des anciennes
colonnes image_path/_2/_3 vers cette table (puis colonnes nullées)
- Le nombre de photos affiché est désormais celui réellement uploadé
(calculé), le champ manuel nb_photos n'est plus utilisé
- Gestion admin : ajout illimité, choix de la photo « principale » (mise
en avant), suppression réservée aux admins (les modérateurs ajoutent et
réordonnent mais ne suppriment pas)
Participants auto :
- rencontres_passees.rencontre_id (FK, ON DELETE SET NULL) ; quand une
rencontre passée est liée à une rencontre, le nombre de participants est
calculé depuis ses inscriptions ; sinon valeur manuelle de repli. Le
formulaire admin propose la liaison et affiche le compte auto
Public :
- Nouvelle page /rencontres-passees (menu « Rencontres passées ») listant
toutes les rencontres passées
- Carrousel plein écran (composant Carousel) montrant TOUTES les photos
(flèches, clavier ←/→/Échap, compteur, miniatures) ; ouvert au clic sur
une carte. La grille d'accueil montre 3 aperçus avec badge « +N » et un
lien « Voir toutes les rencontres → »
- Composant PastEventCard partagé entre l'accueil et la page dédiée
Vérifié : 34 + 21 + 26 tests existants au vert, 19 nouveaux tests
(participants auto liés/déliés, upload illimité, nb_photos calculé,
principale, restrictions modérateur sur suppression de photo, migration
legacy des colonnes inline), parcours navigateur complet (galerie admin,
page publique, carrousel).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fkg15RCxNgUys4ru73He2o
Rencontres passées (section « Ils y étaient » de l'accueil) :
- Nouvel onglet « Rencontres passées » (admin + modérateur) : création,
édition, jusqu'à 3 photos par rencontre (1 grande + 2 petites, upload
après enregistrement — même schéma que la photo des rencontres à venir)
- Nouvelles colonnes rencontres_passees.image_path/_2/_3 (migration
ALTER TABLE IF NOT EXISTS)
- Home.jsx affiche désormais les vraies photos uploadées (repli sur le
motif placeholder si absentes) ; ordre passé de ORDER BY id à
ORDER BY id DESC (les plus récentes en premier, seed réordonné en
conséquence pour préserver l'affichage actuel)
- Suppression réservée aux administrateurs
Modérateurs : plus aucune action destructrice possible, imposé côté
serveur (adminOnly) et masqué côté interface :
- Validation/suspension d'un membre (toggle-valide)
- Suppression d'une rencontre, d'une rencontre passée, d'une inscription
Vérifié : 34 + 21 + 26 tests existants toujours au vert, 21 nouveaux
tests (CRUD rencontres passées, upload des 3 emplacements photo, ordre
d'affichage, restrictions modérateur sur chaque action désormais admin-
only), parcours navigateur complet (upload des 3 photos, rendu réel sur
la page d'accueil).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fkg15RCxNgUys4ru73He2o
- Nouveau rôle users.role='moderator' (contrainte CHECK migrée via
DROP+ADD CONSTRAINT, idempotent) : accès à Membres, Rencontres,
Inscriptions uniquement. Dashboard, Catégories (écriture), Contenu du
site, Demandes d'adhésion et gestion des comptes restent admin-only,
imposé côté serveur (requireAuth accepte désormais un tableau de rôles)
- Nouvel onglet « Administrateurs » (admin-only) : créer un compte admin
ou modérateur (mot de passe temporaire généré, même mécanique que pour
les membres — visible tant que non changé, changement forcé à la
première connexion), réinitialiser l'accès, promouvoir/rétrograder,
supprimer. Garde-fous : impossible de se supprimer ou de se rétrograder
soi-même, impossible de supprimer le dernier administrateur
- users.full_name (colonne migrée) pour l'affichage des comptes staff
- AccessCell/CredentialsModal extraits dans AccessControls.jsx, partagés
entre la gestion des membres et celle des comptes admin/modérateur
- AdminShell filtre ses onglets par rôle ; Espace.jsx route admin et
modérateur vers le back-office
Corrigé en cours de route : GET /api/admin/categories doit rester lisible
par les modérateurs (nécessaire au formulaire membre) même si sa gestion
en écriture reste admin-only — sans quoi le Promise.all du frontend
échouait silencieusement et vidait la liste des membres.
Vérifié : 34 + 21 tests existants toujours au vert, 26 nouveaux tests de
permissions par rôle, parcours navigateur complet (création modérateur,
connexion, changement forcé, menu restreint, accès aux membres).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fkg15RCxNgUys4ru73He2o
- Nouveau membre créé avec email : un mot de passe temporaire aléatoire
(~69 bits, sans caractères ambigus) est généré et haché ; il est retourné
à l'admin et reste lisible en clair (users.temp_password) tant que le
membre ne l'a pas changé
- Colonnes users.temp_password / must_change_password (migration
ALTER TABLE IF NOT EXISTS, appliquée au démarrage par le bootstrap)
- POST /api/admin/members/:id/reset-access : régénère l'accès (création
s'il n'existait pas, ou réinitialisation si le membre a perdu son mot
de passe) ; login/me exposent mustChangePassword ; change-password
efface systématiquement le mot de passe temporaire et lève le blocage
- Frontend : colonne « Accès » dans la liste des membres (mot de passe
visible + copier + réinitialiser, ou « Défini », ou « Créer l'accès »),
modal de confirmation après création/réinitialisation, écran de
changement de mot de passe obligatoire avant tout accès au portail ou
au back-office
Vérifié : cycle complet (création → mot de passe visible côté admin →
connexion → changement forcé → effacement automatique → réinitialisation
admin en cas de perte) via 21 tests e2e dédiés + parcours navigateur ;
34 tests existants toujours au vert.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fkg15RCxNgUys4ru73He2o
- Le menu public n'affiche plus « Espace admin » — un seul lien
« Espace membre » pour tous les comptes
- /espace-membre devient le point de connexion unique : après connexion,
le contenu affiché dépend du rôle du compte (portail membre avec header
du site, ou back-office admin plein écran avec sa propre barre latérale)
- Admin.jsx exporte désormais AdminShell (coquille admin authentifiée,
sans logique de connexion) réutilisée par Espace.jsx ; l'export par
défaut devient une redirection de /admin vers /espace-membre pour ne
pas casser d'anciens liens
- App.jsx masque le header/footer public uniquement quand un admin est
affiché sur /espace-membre (ou sur l'ancienne route /admin)
Vérifié : navigation sans lien admin, /admin redirige, connexion membre
→ portail avec header, connexion admin → back-office sans header ; 34
tests e2e toujours au vert.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fkg15RCxNgUys4ru73He2o
Plus aucune référence au port 3000 : l'application écoute sur 8321 dans
le conteneur (PORT, EXPOSE, healthcheck, mapping 8321:8321, upstream
nginx sbc-app:8321, proxy de dev Vite, README).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fkg15RCxNgUys4ru73He2o
- Un seul conteneur applicatif : Express sert l'API, le frontend React
compilé (fallback SPA, cache immutable sur /assets) et les images
/uploads (nosniff + CSP default-src 'none')
- En-têtes de sécurité (CSP stricte, X-Frame-Options DENY, etc.) portés
de nginx vers helmet, compression gzip ajoutée
- Application exposée sur le port 8321 (APP_PORT), PostgreSQL sur
127.0.0.1:56432 (DB_PORT, loopback uniquement pour l'admin locale)
- TRUST_PROXY pilote la confiance aux en-têtes X-Forwarded-* (désactivé
par défaut hors reverse-proxy)
- Dockerfile multi-étages unique à la racine (build React → deps → image
finale non-root, fs en lecture seule)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fkg15RCxNgUys4ru73He2o