Commit Graph
16 Commits
Author SHA1 Message Date
Claude 4bbcdd6871 Statistiques réelles sur la page d'accueil et le pied de page
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
2026-07-13 19:22:29 +00:00
Claude 8efe296b55 Rend toute la carte rencontre passée cliquable pour ouvrir le carrousel
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
2026-07-13 19:19:23 +00:00
Claude 298c3becd5 Galerie photos illimitée pour les rencontres passées + page publique dédiée + carrousel
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
2026-07-13 18:13:32 +00:00
Claude 214250bc39 Gestion des rencontres passées + interdiction des actions destructrices aux modérateurs
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
2026-07-13 14:58:32 +00:00
Claude fab64bc38a Rôle modérateur + gestion des comptes admin/modérateur
- 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
2026-07-13 14:42:29 +00:00
Claude 56ca6d8559 Mot de passe temporaire à la création d'un membre, visible admin, changement forcé
- 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
2026-07-13 09:23:38 +00:00
Claude 3ea069bfbb Fusionne l'espace admin dans l'espace membre : connexion unique
- 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
2026-07-13 08:59:06 +00:00
Claude 4d5d42c61a L'application initialise la base elle-même : plus de scripts montés
Résout définitivement « Role sbc_app does not exist » : l'initialisation
ne dépend plus de docker-entrypoint-initdb.d ni de bind mounts du dépôt
(fragiles selon le mode de déploiement Portainer).

- Nouveau bootstrap idempotent exécuté par l'app à chaque démarrage avec
  le superuser (PG_SUPERUSER_PASSWORD) : création de la base et du rôle
  sbc_app si absents, réalignement du mot de passe, schéma en
  CREATE ... IF NOT EXISTS, données de démo si la base est vide, grants
- Schéma et seed embarqués dans l'image (server/src/sql), seed par
  recherche de noms (indépendant des séquences)
- Compose réduit à deux services (db + app), suppression de db-sync et
  du montage ./db/init ; répertoire db/ retiré

Vérifié : volume totalement vierge auto-initialisé, redémarrage sans
re-seed, changement d'APP_DB_PASSWORD auto-réparé, 34 tests e2e verts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fkg15RCxNgUys4ru73He2o
2026-07-11 05:03:21 +00:00
Claude 59256ad9ff Log de diagnostic : hôte PostgreSQL ciblé au démarrage
Affiche host/port/db/user à la connexion et un message explicite en cas
d'échec d'authentification persistant, pour identifier immédiatement une
stack déployée avec une ancienne version du compose.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fkg15RCxNgUys4ru73He2o
2026-07-11 04:45:08 +00:00
Claude d3e8890100 Corrige la connexion BDD : alias unique sbc-db et variables PG discrètes
Sur le réseau partagé nginx_default, le nom « db » peut résoudre vers le
PostgreSQL d'une autre stack : l'application se connectait au mauvais
serveur (password authentication failed pour sbc_app alors que db-sync,
lui, visait le bon). Reproduit et vérifié avec un conteneur leurre.

- La base obtient l'alias réseau unique « sbc-db » ; l'app et db-sync
  s'y connectent via ce nom, jamais via « db »
- DATABASE_URL remplacé par PGHOST/PGPORT/PGDATABASE/PGUSER/PGPASSWORD :
  insensible aux caractères spéciaux du mot de passe et aux ambiguïtés
  de résolution DNS

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fkg15RCxNgUys4ru73He2o
2026-07-11 02:32:25 +00:00
Claude e3f5869bc8 Port interne de l'application : 3000 → 8321
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
2026-07-10 20:15:36 +00:00
Claude 478ec3e307 Rattache l'app au réseau externe nginx_default
- Le service app rejoint le réseau externe nginx_default (nom configurable
  via PROXY_NETWORK) avec l'alias stable « sbc-app » pour l'upstream nginx
  (proxy_pass http://sbc-app:3000)
- La base de données reste sur le réseau interne de la stack uniquement
- README : section reverse-proxy (création du réseau, TRUST_PROXY,
  COOKIE_SECURE, en-têtes à transmettre)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fkg15RCxNgUys4ru73He2o
2026-07-10 20:13:31 +00:00
Claude 2df7d37b72 Auto-répare le mot de passe du rôle BDD et change le port PostgreSQL
- Nouveau service one-shot db-sync : à chaque démarrage de la stack, il
  réaligne le mot de passe du rôle sbc_app sur APP_DB_PASSWORD (corrige
  « password authentication failed » quand le volume a été initialisé
  avec une ancienne valeur) ; l'app démarre après sa réussite
- Port hôte PostgreSQL : 56432 → 58412 (conflit avec un service existant),
  toujours en loopback uniquement
- Note de dépannage dans le README

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fkg15RCxNgUys4ru73He2o
2026-07-10 20:09:39 +00:00
Claude 3f35ea584f Prérègle les secrets : déploiement sans configuration (Portainer)
- docker-compose : valeurs par défaut pour POSTGRES_PASSWORD,
  APP_DB_PASSWORD et les mots de passe initiaux (admin SlucAdmin2026!,
  membres SlucMembre2026!) — surchargeables par variables d'environnement
- JWT_SECRET devient optionnel : l'application génère un secret aléatoire
  au démarrage s'il est absent (les sessions expirent alors au redémarrage
  du conteneur), aucun secret de signature n'est committé
- .env.example et README mis à jour (démarrage clé en main, consignes de
  surcharge pour la production)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fkg15RCxNgUys4ru73He2o
2026-07-10 20:02:55 +00:00
Claude 499b753c22 Supprime nginx : Express sert le frontend, ports peu utilisés
- 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
2026-07-10 19:52:52 +00:00
Claude 96dca2a802 Application complète SLUC Business Club (React + Express + PostgreSQL, Docker)
Implémente l'intégralité de la maquette « SLUC Business Club.dc.html » :
- Site public : accueil (héro administrable, carrousel membres, agenda avec
  inscription en ligne, rencontres passées, demande d'adhésion), annuaire
  avec recherche/filtres/fiche détaillée, page association
- Espace membre : connexion, édition de fiche avec aperçu direct, upload
  logo/photo, statut d'adhésion par saison, changement de mot de passe
- Espace admin : tableau de bord, membres (validation par saison),
  rencontres (CRUD + inscrits + impression + export Excel), inscriptions,
  catégories, contenu du site

Architecture : 3 conteneurs Docker Compose — PostgreSQL 16 (réseau interne,
rôle applicatif restreint), API Express (non-root, read-only fs), nginx
non privilégié (frontend React + reverse-proxy + CSP stricte).

Sécurité : requêtes 100 % paramétrées, bcrypt + JWT httpOnly SameSite=Strict,
vérification d'Origin (CSRF), validation zod, rate limiting, uploads vérifiés
par octets magiques avec noms aléatoires, aucun secret committé.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fkg15RCxNgUys4ru73He2o
2026-07-10 19:43:07 +00:00