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

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

Deux conteneurs orchestrés par Docker Compose, sur des ports peu utilisés (configurables dans .env) :

Service Rôle Port hôte
app Node.js 22 / Express : API REST et frontend React compilé et images /uploads 8321 (APP_PORT)
db PostgreSQL 16 (schéma + données de démo au premier démarrage) 127.0.0.1:56432 (DB_PORT) — loopback uniquement, pour l'administration locale
Navigateur ──> app (Express :8321) ──> db (PostgreSQL, 127.0.0.1:56432)
                 ├─ /api/…      API REST
                 ├─ /uploads/…  images (volume persistant)
                 └─ /…          frontend React (fallback SPA)

Le mapping PostgreSQL est lié à 127.0.0.1 : la base reste inaccessible depuis le réseau. Supprimez la section ports: du service db pour la fermer totalement.

  • 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:8321 (port configurable via APP_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 avec X-Content-Type-Options: nosniff et une CSP default-src 'none'.
  • En-têtes : CSP stricte, X-Frame-Options: DENY et toute la panoplie helmet sur l'ensemble des réponses (API, frontend, uploads).
  • Conteneurs : application en utilisateur non-root avec système de fichiers en lecture seule (read_only + tmpfs), no-new-privileges, PostgreSQL publié uniquement sur 127.0.0.1 (inaccessible depuis le réseau).
  • 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 et TRUST_PROXY=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
├── Dockerfile          # multi-étages : build React → dépendances API → image finale
├── .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, statique)
└── web/                # sources React + Vite (compilées dans l'image)
S
Description
No description provided
Readme
3.9 MiB
0 Stars 1 Watchers 0 Forks
Languages
JavaScript 95.8%
CSS 3.8%
Dockerfile 0.2%
HTML 0.2%