Files
SBC/README.md
T
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

6.4 KiB
Raw Blame History

Business Club SLUC Nancy

Application web complète du réseau d'affaires des partenaires du SLUC Nancy Basket : site public (accueil, annuaire des membres, association), espace membre et back-office d'administration.

Architecture

Trois conteneurs orchestrés par Docker Compose :

Service Rôle Exposition
db PostgreSQL 16 (schéma + données de démo au premier démarrage) réseau interne uniquement, aucun port publié
api API REST Node.js 22 / Express (auth, membres, rencontres, inscriptions, catégories, contenu, uploads) réseau interne uniquement
web nginx (non-root) : frontend React compilé, reverse-proxy /api, service des images /uploads port 8080
Navigateur ──> web (nginx :8080) ──> api (Express :3000) ──> db (PostgreSQL :5432)
                    │  /uploads (volume partagé, lecture seule)
                    └─ fichiers statiques React
  • Frontend : React 18 + Vite + React Router — reproduction fidèle de la maquette (SLUC Business Club.dc.html).
  • Backend : Express, pg (requêtes paramétrées), zod (validation), bcryptjs (hachage), JWT en cookie httpOnly, helmet, express-rate-limit, multer (uploads).
  • Base : PostgreSQL 16, initialisée par db/init/ (rôle applicatif restreint, schéma, données de démonstration).

Démarrage

cp .env.example .env
# Éditez .env : générez des secrets forts
#   openssl rand -hex 32   → JWT_SECRET
#   openssl rand -hex 24   → POSTGRES_PASSWORD et APP_DB_PASSWORD
# puis choisissez ADMIN_INITIAL_PASSWORD / MEMBER_INITIAL_PASSWORD

docker compose up -d --build

L'application est disponible sur http://localhost:8080 (port configurable via WEB_PORT).

Comptes

Les comptes de démonstration sont créés verrouillés (aucun mot de passe en dur dans le dépôt). Au démarrage, l'API leur applique le mot de passe des variables d'environnement — uniquement s'ils n'en ont pas déjà un :

Rôle Email Mot de passe initial
Admin admin@sluc-businessclub.fr ADMIN_INITIAL_PASSWORD
Membre (×12) email de contact de chaque entreprise de démo, ex. contact@lorraine-assurances.fr MEMBER_INITIAL_PASSWORD

Changez le mot de passe admin après la première connexion (Espace membre/admin → formulaire « Mot de passe », endpoint POST /api/auth/change-password).

Fonctionnalités

Site public

  • Accueil : héro éditorial (citation et photo administrables), carrousel des membres, prochaines rencontres avec inscription en ligne (contrôle de capacité et de doublon), rencontres passées, formulaire de demande d'adhésion.
  • Annuaire : recherche plein texte, filtres par catégorie, fiche détaillée par entreprise. Seuls les membres validés pour la saison apparaissent.
  • L'association : mission, valeurs, mot du président, chronologie, chiffres clés.

Espace membre (connexion email + mot de passe)

  • Édition de sa fiche annuaire avec aperçu en direct.
  • Upload du logo et de la photo dirigeant·e (JPEG/PNG/WebP, 2 Mo max, vérification des octets magiques côté serveur).
  • Bannière d'état d'adhésion (validée / à renouveler) et changement de mot de passe.

Espace admin (rôle admin)

  • Tableau de bord : indicateurs temps réel, dernières inscriptions, prochaines rencontres.
  • Membres : création, édition, validation/suspension par saison (1er sept. → 31 août).
  • Rencontres : création, édition, suppression ; liste des inscrits avec impression et export Excel.
  • Inscriptions : modification, confirmation, annulation avec confirmation.
  • Catégories : ajout, renommage, suppression (les entreprises deviennent « Non classée »).
  • Contenu du site : citation, signature et photo de la page d'accueil.

Sécurité

  • Injection SQL : 100 % de requêtes paramétrées (pg), aucun SQL concaténé.
  • Authentification : bcrypt (coût 12), comparaison à temps constant même si l'email est inconnu, JWT HS256 signé (secret ≥ 32 caractères exigé au démarrage), session de 12 h.
  • Cookies : httpOnly, SameSite=Strict, Secure activable (COOKIE_SECURE=true derrière HTTPS).
  • CSRF : cookie SameSite=Strict + vérification de l'en-tête Origin sur toutes les mutations.
  • Autorisation : middleware de rôles (member / admin) sur chaque route protégée ; un membre ne peut modifier que sa propre fiche.
  • Validation : schémas zod sur toutes les entrées (types, longueurs, formats), contraintes CHECK en base en seconde ligne.
  • Rate limiting : global (300/min), connexion (10 / 15 min), formulaires publics (20 / h).
  • Uploads : taille ≤ 2 Mo, type vérifié par octets magiques (jamais le MIME client), nom de fichier aléatoire généré côté serveur (aucune traversée de chemin possible), servis par nginx avec X-Content-Type-Options: nosniff et types MIME forcés.
  • En-têtes : CSP stricte, X-Frame-Options: DENY, Referrer-Policy, Permissions-Policy, helmet côté API, server_tokens off.
  • Conteneurs : API en utilisateur non-root avec système de fichiers en lecture seule (read_only + tmpfs), nginx non privilégié, no-new-privileges, PostgreSQL sans port publié sur un réseau interne (internal: true).
  • Base de données : l'API se connecte avec un rôle dédié sbc_app limité au DML (pas de DDL, pas de superuser).
  • Secrets : uniquement via .env (ignoré par git) ; docker compose refuse de démarrer sans eux ; aucun hash ni mot de passe committé.
  • Erreurs : les détails restent dans les logs serveur, les clients reçoivent un message générique.

Pour la production

  • Placez l'application derrière HTTPS (reverse-proxy TLS) et passez COOKIE_SECURE=true.
  • Changez immédiatement les mots de passe initiaux.
  • Sauvegardez les volumes db_data (base) et uploads (images).

Développement local (sans Docker)

# Terminal 1 — base de données PostgreSQL locale + variables d'env, puis :
cd server && npm install && npm start
# Terminal 2
cd web && npm install && npm run dev   # proxy /api → localhost:3000

Structure

├── docker-compose.yml
├── .env.example
├── db/init/            # 01 rôle applicatif · 02 schéma · 03 données de démo
├── server/             # API Express (src/routes, src/middleware, uploads)
└── web/                # React + Vite, nginx.conf, Dockerfile multi-étages