- 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
6.7 KiB
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 | 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,Secureactivable (COOKIE_SECURE=truederrière HTTPS). - CSRF : cookie SameSite=Strict + vérification de l'en-tête
Originsur 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
zodsur toutes les entrées (types, longueurs, formats), contraintesCHECKen 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: nosniffet une CSPdefault-src 'none'. - En-têtes : CSP stricte,
X-Frame-Options: DENYet toute la panopliehelmetsur 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 sur127.0.0.1(inaccessible depuis le réseau). - Base de données : l'API se connecte avec un rôle dédié
sbc_applimité au DML (pas de DDL, pas de superuser). - Secrets : uniquement via
.env(ignoré par git) ;docker composerefuse 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=trueetTRUST_PROXY=true. - Changez immédiatement les mots de passe initiaux.
- Sauvegardez les volumes
db_data(base) etuploads(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)