Aller au contenu principal

Architecture technique

Stack

CoucheTechnologieVersion
FrameworkNext.js16.2.3 (App Router, RSC)
AuthNextAuth5.0.0-beta.31 (JWT, credentials + LinkedIn OAuth)
DatabasePostgreSQL + Prisma18 + 5.22.0
Frontend UITailwind CSS + shadcn/ui4.2.2 + composants headless
PaymentsHelloAsso APIOAuth2 client credentials
EmailResend6.12.0
ChartsRecharts3.8.0
InfrastructureDocker + Nginx + VPSReverse proxy Let's Encrypt
CI/CDGitHub ActionsDeploy sur push main

Où vit quoi

Répertoires de premier niveau

CheminResponsabilité
app/Routes Next.js App Router (UI + API)
components/Composants React réutilisables (admin, membres, ateliers, UI)
lib/Utilitaires métier (auth, paiements, emails, badges, permissions, pricing)
prisma/Schéma de données, migrations, seed
scripts/Scripts utilitaires d'exploitation (migration Pretix, reconciliation, génération de clés)
.github/workflows/CI/CD (quality + deploy)
public/Assets statiques (icons, images, SVG du logo)

Structure de app/

Groupes de routes (sans préfixe URL)

CheminRôlePages
app/(auth)/Routes d'authentification (pas d'auth gate)login, logout, forgot-password, reset-password, link-linkedin
app/(no-header)/Pages publiques sans headerévénements publics (events/[slug]), pages sans chrome
app/(inscription)/Funnel d'inscription publicconfirmation
app/(fullscreen)/Pages fullscreen (réservées)satisfaction/[token] (questionnaire de satisfaction)

Routes authentifiées

CheminPérimètreComposants clés
app/members/Espace membre (animateur, formateur)dashboard, profile, events/new (création atelier), events/[id] (fiche atelier), directory (annuaire), library (médiathèque), resources (supports pédagogiques), organisations
app/admin/Espace administrateur (isAdmin gate)dashboard, events, members, participants, organisations, blog (actualités), library (médiathèque), cards (modèles plateau), emails (templates), settings (paramètres multi-sections), pricing, discount-codes
app/verify/Vérification publique de badgepage/badge/[id]

Racine

CheminRôle
app/page.tsxRedirect vers /members (ou login si non auth)
app/layout.tsxRoot layout avec RootLayout (metadata, fonts)
app/not-found.tsxFallback 404

Structure de app/api/

CheminResponsabilité
app/api/auth/Endpoints auth (NextAuth catch-all, forgot/reset password, pending-link LinkedIn)
app/api/admin/Endpoints admin (events, members, organisations, participants, blog, library, resources, cards, discount-codes, settings, etc.)
app/api/members/Endpoints espace membre (profile, workshops/animators)
app/api/workshops/Endpoints ateliers publics et privés ([id]/registrations, [id]/cancel, [id]/waitlist)
app/api/public/Endpoints publics (workshops, articles, resources, satisfaction, registration-cancel, site-mode)
app/api/checkout/Création d'intention de paiement HelloAsso
app/api/webhooks/helloasso/Webhook HelloAsso (paiements d'inscriptions + rapports d'utilisation)
app/api/cron/Tâches planifiées (reminders)
app/api/badges/Endpoints badges Open Badges 3.0 (issuer, assertions, classes)
app/api/board-auth/SSO animateur vers LeBoard (token signé)
app/api/board-training/Création d'atelier de test (training board)
app/api/usage-reports/Rapports d'utilisation pro (checkout/ pour le paiement)
app/api/invitations/Tokens d'invitation pour les nouveaux membres
app/api/discount-codes/Validation des codes avantage
app/api/workshop-models/Modèles de plateau (GET)
app/api/.well-known/jwks.json/Clé publique JWKS pour la vérification des badges

Structure de components/

RépertoireContenu
components/admin/Composants spécifiques admin (tableaux, formulaires, sections)
components/layout/Layout header, sidebar, navigation
components/membres/Composants espace membre (cartes, formulaires)
components/ui/Composants shadcn/ui non modifiés (Button, Card, Dialog, Select, etc.)
components/workshops/Composants ateliers (fiches, filtres, registrations)

Structure de lib/

FichierResponsabilité
auth.tsNextAuth config (providers, callbacks, session shape)
permissions.tsHiérarchie des rôles (canCreateWorkshop, hasProHabilitation)
helloasso.tsClient HelloAsso (OAuth2, checkout intent, order lookup)
email.tsConfig Resend, rendu emails
email-template-defaults.tsTemplates des 14 emails transactionnels (contenu codé)
email-blocks.tsComposants réutilisables pour les emails (SectionLabel, TextCard, etc.)
badges.tsÉmission badges Open Badges 3.0 (types, metadata, JWT)
notification.tsNotifications email/Telegram admin
pricing.tsLogique de tarification (lecture de PricingPolicy)
discount-codes.tsValidation et application de codes avantage
registration-cancellation.tsLogique d'annulation/remboursement
cotisation-webhook.tsTraitement des paiements de cotisation depuis HelloAsso
workshop-form.tsClasses CSS et helpers pour formulaires ateliers (timezone, datetime UTC)
workshop-status.tsStatuts et traduction des ateliers (draft, published, cancelled)
directory.tsAnnuaire des membres (avatars Gravatar, labels de badge)
settings.tsSingleton Settings (identité, config email/integrations)
prisma.tsSingleton Prisma client
urls.tsURLs publiques (HUB_URL, BOARD_URL)
usage-rights.tsParsing des rapports d'utilisation pro
satisfaction-window.tsFenêtres d'envoi questionnaire satisfaction
satisfaction-results.tsAgrégation résultats satisfaction
workshop-lead.tsRésolution du responsable d'un atelier (lead ou créateur)
board.tsHelpers plateau LeBoard
lots-config.tsConfiguration des lots (cartes, slides)
lot-cards.tsExtraction IDs cartes par lot
lot-slides.tsGestion diapositives lots
google-slides.tsAPI Google Slides (oauth, lecture)
siret.tsLookup SIRET (API recherche-entreprises)
organisation.tsQueries organisations et gestionnaires
cotisation.tsStatuts et calculs cotisation
parcours.ts2 axes parcours (animateur/formateur × public/pro)
pedagogical-slots.tsSections et slots supports pédagogiques
rate-limit.tsRate limiting par IP (checkout, cancellation)
csrf.tsProtection CSRF
api-auth.tsVérification auth pour endpoints API (requireAdmin, requireAuth)
admin-dashboard-stats.tsKPIs tableau de bord admin
new-registrations.tsBadge "nouveaux inscrits"
satisfaction-questions.tsQuestions questionnaire satisfaction
utils.tsUtilities génériques
gender.tsApplication du genre aux templates emails
rgesn.tsRGPD / droit à l'oubli
image-type.tsDétection type image (PNG, WebP, etc.)
card-thumbnails.tsThumbnails cartes plateau
badge-keys.tsClé EdDSA pour signature badges JWT
board-cards.tsMapping cartes Board
cancellation-policy.tsPolitique d'annulation (délais)

prisma/

FichierContenu
schema.prismaModèle de données complet (40+ modèles, voir page Données)
migrations/Fichiers migration SQL (historique versionné)
seed.tsSeeding DB optionnel pour dev

scripts/

ScriptObjectif
migrate-pretix.tsImport idempotent ateliers/inscriptions depuis Pretix (ancien système)
reconcile-registrations.tsRattrapage quotidien des inscriptions pending vs HelloAsso
reconcile-usage-reports.tsRattrapage rapports utilisation pro
backfill-workshop-owner.tsBackfill champ createdById (migration historique)
generate-badge-key.tsGénération clé EdDSA pour badges JWT
google-slides-oauth.tsOAuth Google Slides (setup initial)
import-nocodb-events.tsImport depuis NocoDB (migration)
import-wp-mediatheque.tsImport ressources depuis WordPress
render-email-logo.mjsRendu SVG logo pour emails

Conventions de routage

Next.js App Router

  • Route groups (name) : n'ajoutent pas de segment URL, permettent une organisation logique (ex: (auth)/login → URL /login).
  • Route dynamique [id] : paramètres d'URL, accessible via params.id.
  • Fallback optionnel [[...slug]] : zéro ou plus segments (non utilisé ici).

Layout hierarchy

app/layout.tsx (RootLayout)
├── app/(auth)/layout.tsx (no auth gate)
│ ├── (auth)/login/page.tsx
│ ├── (auth)/forgot-password/page.tsx
│ └── (auth)/reset-password/[token]/page.tsx
├── app/(no-header)/layout.tsx (public pages, no header)
│ ├── (no-header)/events/[slug]/page.tsx (workshop public detail)
│ └── (no-header)/verify/page.tsx (badge verification)
├── app/members/layout.tsx (requires auth, calls auth() + redirects)
│ ├── members/dashboard/page.tsx
│ ├── members/events/page.tsx (Mes événements)
│ ├── members/events/new/page.tsx (create workshop)
│ ├── members/events/[id]/page.tsx (workshop detail + registrations)
│ ├── members/profile/page.tsx
│ ├── members/directory/page.tsx
│ ├── members/library/page.tsx
│ ├── members/resources/page.tsx (supports pédagogiques)
│ └── members/organisations/page.tsx
├── app/admin/layout.tsx (requires isAdmin, calls auth() + checks isAdmin)
│ ├── admin/dashboard/page.tsx
│ ├── admin/events/page.tsx
│ ├── admin/members/page.tsx
│ ├── admin/participants/page.tsx
│ ├── admin/organisations/page.tsx
│ ├── admin/blog/page.tsx
│ ├── admin/library/page.tsx
│ ├── admin/cards/page.tsx (plateau models)
│ ├── admin/cards/[id]/page.tsx (model detail)
│ ├── admin/emails/page.tsx
│ ├── admin/settings/page.tsx
│ ├── admin/pricing/page.tsx
│ ├── admin/discount-codes/page.tsx
│ └── admin/resources/page.tsx (supports pédagogiques)
└── app/(fullscreen)/layout.tsx (no header/sidebar)
└── (fullscreen)/satisfaction/[token]/page.tsx

Conventions UI

  • Tokens CSS : uniquement shadcn/ui (Tailwind + CSS variables custom). Zéro tokens personnalisés brand-* ou slate-*.
  • Statuts ateliers : centralisés dans lib/workshop-status.ts (Draft/Published/Cancelled) avec traductions et couleurs.
  • Alerte : classe bg-amber-100 ou shadcn Alert component (amber par défaut pour avertissements non critiques).
  • Pagination admin : composant shadcn Pagination sur toutes les tables.

Authentification et autorisation

Session

  • Type : JWT (NextAuth v5).
  • Durée : 1 heure par défaut ; 30 jours si rememberMe=true au login credentials.
  • Domaine : hôte-local par défaut ; .fresquesystemique.org en prod (pont auth avec LeSite via AUTH_COOKIE_DOMAIN).
  • Nom du cookie : __Secure-fresque.session-token (doit être identique côté LeSite pour le partage).

Middleware

  • middleware.ts (racine du repo si présent) : ne redirige pas, laisse les layouts gérer l'auth gating.
  • Les layouts app/members/layout.tsx et app/admin/layout.tsx appelent auth() et redirigent si non authentifié ou non autorisé.

Contrôle d'accès

// Hiérarchie de rôles (ROLE_LEVEL dans lib/permissions.ts)
adherent (1) < animateur (2) < coordinateur (3) < formateur (4) < admin (99)

// Attention, deux échelles cohabitent dans ce fichier. ROLE_LEVEL ci-dessus
// note le rôle du membre ; RESOURCE_LEVEL note le niveau exigé par une
// ressource, et ne connaît que trois valeurs :
adherent (1) < animateur (2) < formateur (3)

// Le niveau 1 sert à comparer des ressources, pas à ouvrir le Hub : un membre
// au rôle `adherent` est refusé dès la connexion, le plancher d'accès au Hub
// est animateur.
//
// `coordinateur` n'est produit par aucune dérivation de lib/parcours.ts : il
// n'existe que dans cette table et ne peut être posé qu'en base.

// Vérification
canAccessResource(role, resourceAccess) // level >= level
canCreateWorkshop(role) // role in [animateur, formateur, admin]
hasProHabilitation(hab, cotisStatus, cotisExpiry) // TOUTES les 3 args requises

Important : les administrateurs ont role='formateur' ET isAdmin=true. Toujours vérifier isAdmin pour les opérations admin, jamais seulement le rôle pédagogique.

OAuth LinkedIn

  • Fournisseur NextAuth natif.
  • Première connexion sans compte existant → crée PendingLink (TTL 15 min) et redirige vers /login/link-linkedin?token=... pour attacher à un compte existant.
  • Connexion directe possible une fois linkedinId stocké sur Member.