Initialisation du dépôt : specs (socle plateforme, modèle de données, site vitrine) et steering
This commit is contained in:
@@ -0,0 +1,169 @@
|
||||
# Design Requirements — Socle Plateforme SchotzimmO (aspects techniques)
|
||||
|
||||
## Introduction
|
||||
|
||||
Ce document rassemble les exigences **techniques** dérivées du besoin fonctionnel métier décrit dans `requirements.md`. Il est organisé **en miroir** : chaque section reprend le même numéro et le même titre que `requirements.md`, suivi de « — aspects techniques ». Seules les exigences comportant effectivement des critères techniques y figurent.
|
||||
|
||||
Ces exigences sont arbitrées conjointement par le PO business et le PO IT. Les choix d'architecture, modèles de données et contrats d'API détaillés sont, eux, décrits dans `design.md`.
|
||||
|
||||
> Les mots-clés EARS (WHEN / IF / WHILE / WHERE / THE ... SHALL) sont conservés. Les termes techniques, identifiants, codes HTTP, en-têtes et noms d'API restent en anglais ou sous leur forme littérale.
|
||||
|
||||
---
|
||||
|
||||
## Glossary (technique)
|
||||
|
||||
- **Base_Socle** : La base MariaDB centrale et unique portant l'authentification, le référentiel des Reseaux et leurs informations de connexion, les abonnements, le catalogue des Modules/Packs et les KPI d'usage par Reseau. Aucune donnée métier produite par les Mandataires.
|
||||
- **Base_Reseau** : La base MariaDB propre à un Reseau, portant toutes ses données métier (profil mandataire, parrainage, données des Modules). Une Base_Reseau par Reseau.
|
||||
- **Reference_Logique** : Un identifiant (`AccountId`) généré par la Base_Socle et réutilisé dans une Base_Reseau sans contrainte FK SQL ni jointure inter-bases ; la cohérence est assurée applicativement.
|
||||
- **NetworkConnectionResolver** : Le composant qui, à partir du claim `network` du JWT, résout la clé logique (`db_connection_key`) du Reseau vers la chaîne de connexion de sa Base_Reseau fournie par configuration.
|
||||
- **JWT** : *JSON Web Token* signé émis à la connexion, portant l'identité du Mandataire (`sub` = AccountId), son Reseau (`network`), son rôle (`role` ∈ { `mandataire`, `admin` }) et une échéance (`exp`).
|
||||
- **Claim** : Une assertion portée par le JWT (identité, Reseau, rôle), utilisée côté Backend pour le contrôle d'accès.
|
||||
- **Access_Token** : Le JWT d'accès transmis par le Frontend dans l'en-tête `Authorization: Bearer <token>` à chaque appel protégé.
|
||||
- **Hash_Mot_De_Passe** : L'empreinte du Mot_De_Passe calculée avec un algorithme de hachage adapté aux mots de passe (ex. PBKDF2/BCrypt/Argon2), avec sel ; le Mot_De_Passe n'est jamais stocké en clair ni journalisé.
|
||||
- **SMTP** : Le service d'envoi d'e-mails (invitations, réinitialisation). Configurable ; un `Smtp:Host` vide active un repli de journalisation du lien plutôt qu'un envoi.
|
||||
- **Config_BDD** : Les chaînes de connexion MariaDB/MySQL (Base_Socle et chaque Base_Reseau), fournies par configuration (variables d'environnement), pointant soit vers des instances conteneurisées, soit vers un serveur externe. La Base_Socle ne stocke qu'une clé logique par Reseau, jamais la chaîne elle-même.
|
||||
- **Migration** : Une migration de schéma gérée par l'ORM (EF Core), appliquée de façon idempotente au démarrage ou via commande.
|
||||
- **DTO** : *Data Transfer Object*, objet de transport exposé par l'API, distinct des entités persistées.
|
||||
- **Code_HTTP** : Le statut HTTP retourné par l'API (ex. 200, 201, 400, 401, 403, 404, 409, 422, 500).
|
||||
- **Portee_Hierarchique** : Le périmètre de données qu'un appel peut retourner compte tenu de la Visibilite_Descendante (soi-même, Descendance agrégée, Filleuls_Directs en détail).
|
||||
|
||||
---
|
||||
|
||||
## Requirements (aspects techniques)
|
||||
|
||||
### Requirement 1: Inscription d'un Mandataire — aspects techniques
|
||||
|
||||
1. THE Backend SHALL exposer l'inscription via `POST /api/auth/register` acceptant un DTO { email, password, firstName, lastName, networkId, networkMemberId, legalStatus?, siren?, referralToken? }.
|
||||
2. WHEN l'inscription réussit, THE Backend SHALL répondre `201 Created` et ne jamais retourner le Hash_Mot_De_Passe ni aucune donnée de sécurité.
|
||||
3. IF l'Adresse_Email est déjà utilisée, THEN THE Backend SHALL répondre `409 Conflict` avec un message générique ne révélant aucune donnée du Compte existant.
|
||||
4. IF le DTO est invalide (champ obligatoire manquant, email mal formé, password < 8 caractères, siren au format incorrect), THEN THE Backend SHALL répondre `422 Unprocessable Entity` avec le détail des champs en erreur.
|
||||
5. THE Backend SHALL stocker le Mot_De_Passe uniquement sous forme de Hash_Mot_De_Passe salé ; en aucun cas en clair.
|
||||
6. THE Backend SHALL valider l'Adresse_Email par une expression de forme standard et normaliser sa casse (minuscule) avant unicité et Rapprochement_Email.
|
||||
7. WHEN un `referralToken` valide et non expiré est fourni, THE Backend SHALL en extraire `networkId` et l'identité du Parrain et ignorer tout `networkId` divergent fourni par le client.
|
||||
8. IF le `referralToken` est fourni mais invalide ou expiré, THEN THE Backend SHALL répondre `400 Bad Request` et signaler que le lien d'invitation n'est plus valide.
|
||||
|
||||
---
|
||||
|
||||
### Requirement 2: Authentification et session — aspects techniques
|
||||
|
||||
1. THE Backend SHALL exposer `POST /api/auth/login` { email, password, rememberMe? } et retourner, en cas de succès, un Access_Token (JWT) et son échéance.
|
||||
2. THE Backend SHALL signer le JWT avec une clé secrète fournie par configuration (`JWT:SecretKey`), échouer au démarrage si la clé est absente, et appliquer une durée de validité configurable (`JWT:ExpirationMinutes`, défaut 720).
|
||||
3. THE JWT SHALL porter les claims `sub` (AccountId), `network` (NetworkId), `role` et `exp`.
|
||||
4. WHEN les identifiants sont invalides, THE Backend SHALL répondre `401 Unauthorized` avec un message générique, sans distinguer email inconnu et mot de passe erroné, et SHALL appliquer une temporisation ou limitation de débit contre le forçage.
|
||||
5. THE Backend SHALL protéger toutes les routes métier par une authentification Bearer et répondre `401 Unauthorized` en l'absence d'un Access_Token valide.
|
||||
6. THE Frontend SHALL transmettre l'Access_Token dans l'en-tête `Authorization: Bearer <token>` et effacer le token local à la déconnexion.
|
||||
7. THE Frontend SHALL stocker l'Identifiant_Mémorisé dans le stockage local du navigateur et ne jamais y stocker le Mot_De_Passe ni l'Access_Token au-delà de la politique de session définie.
|
||||
8. THE Backend SHALL exposer `POST /api/auth/forgot-password` { email } répondant toujours `200 OK` quel que soit l'existence de l'Adresse_Email (anti-énumération), et générer le Jeton_Reinitialisation uniquement si le Compte existe.
|
||||
9. THE Backend SHALL générer un Jeton_Reinitialisation cryptographiquement aléatoire, à usage unique, de durée de vie configurable (`Auth:ResetTokenLifetimeMinutes`, défaut 60), stocké sous forme hachée.
|
||||
10. THE Backend SHALL exposer `POST /api/auth/reset-password` { token, newPassword } répondant `200 OK` en cas de succès, `400 Bad Request` si le token est invalide, expiré ou déjà consommé, et `422` si le nouveau mot de passe est trop court.
|
||||
11. THE Backend SHALL construire les liens d'e-mail à partir d'une base d'URL configurable (`Auth:FrontendBaseUrl`).
|
||||
|
||||
---
|
||||
|
||||
### Requirement 3: Réseaux — aspects techniques
|
||||
|
||||
1. THE Backend SHALL persister dans la Base_Socle un référentiel `Networks` (code, libellé, `db_connection_key`, `theme_settings`, `is_active`, `is_provisioned`) et exposer `GET /api/networks` (Reseaux actifs et provisionnés) accessible sans authentification pour alimenter l'inscription.
|
||||
2. THE Backend SHALL stocker toutes les données métier d'un Reseau dans sa Base_Reseau dédiée ; les tables de Base_Reseau ne portent pas de colonne `NetworkId` (le cloisonnement est physique).
|
||||
3. THE NetworkConnectionResolver SHALL déterminer la Base_Reseau de chaque requête métier à partir du claim `network` du JWT, indépendamment de tout paramètre fourni par le client.
|
||||
4. IF le claim `network` ne correspond à aucun Reseau actif et provisionné, THEN THE Backend SHALL répondre `403 Forbidden` et n'ouvrir aucune connexion.
|
||||
5. WHEN l'Administrateur réalise l'Onboarding_Reseau, THE Backend SHALL créer/initialiser la Base_Reseau (schéma + données de référence) via `POST /api/admin/networks`, enregistrer `db_connection_key` et `theme_settings`, puis positionner `is_provisioned = 1`.
|
||||
6. IF la Base_Reseau d'un Reseau est indisponible ou non provisionnée, THEN THE Backend SHALL répondre `503 Service Unavailable` sans exposer de détail d'infrastructure.
|
||||
7. THE Backend SHALL fournir au Frontend la Personnalisation_Reseau (`theme_settings`) du Reseau courant pour l'application du thème et des libellés.
|
||||
|
||||
---
|
||||
|
||||
### Requirement 4: Parrainage et hiérarchie — aspects techniques
|
||||
|
||||
1. THE Backend SHALL modéliser l'Arbre_Parrainage dans la Base_Reseau par une relation d'auto-référence `mandataire_profiles.sponsor_account_id` (vraie FK intra-base), et un état d'Affiliation (`supposee`, `validee`).
|
||||
2. THE Backend SHALL modéliser le Membre_Declare dans la Base_Reseau comme une entité porteuse d'Adresse_Email normalisée, lien hiérarchique et statut, sans credentials ni données métier (le cloisonnement Reseau étant physique, pas de `NetworkId`).
|
||||
3. THE Backend SHALL exposer `POST /api/referrals/declare` (déclarer un Parrain ou des Filleuls) et `POST /api/referrals/{id}/invite` (envoyer l'Invitation par SMTP).
|
||||
4. WHEN une Invitation est envoyée, THE Backend SHALL générer un Lien_Parrainage signé (token portant `networkId` et identité du Parrain), de durée de vie configurable, et l'inclure dans l'e-mail via `Auth:FrontendBaseUrl`.
|
||||
5. WHEN `Smtp:Host` est vide, THE Backend SHALL journaliser le Lien_Parrainage et le lien de réinitialisation au lieu de les envoyer, pour les environnements sans SMTP.
|
||||
6. THE Backend SHALL effectuer le Rapprochement_Email sur l'Adresse_Email normalisée (casse ignorée) lors de toute inscription, et convertir le Membre_Declare (Base_Reseau) en profil mandataire lié au Compte créé (Base_Socle), en coordonnant les deux bases avec compensation en cas d'échec partiel.
|
||||
7. IF une déclaration introduit un cycle (le descendant deviendrait ancêtre), THEN THE Backend SHALL répondre `409 Conflict` sans persister.
|
||||
8. THE Backend SHALL exposer `POST /api/referrals/confirmations` et `POST /api/referrals/confirmations/{id}/approve|reject` pour la Confirmation_Affiliation entre Comptes déjà inscrits, et répondre `403 Forbidden` si l'appelant n'est pas la partie concernée.
|
||||
9. THE Backend SHALL garantir, par contrainte applicative, au plus un `sponsor_account_id` validé par mandataire à un instant donné.
|
||||
|
||||
---
|
||||
|
||||
### Requirement 5: Visibilité hiérarchique descendante — aspects techniques
|
||||
|
||||
1. THE Backend SHALL dériver la Portee_Hierarchique d'un appel à partir du claim `sub`, en calculant la Descendance via la relation `sponsor_account_id` au sein de la Base_Reseau de l'appelant.
|
||||
2. WHEN une Vue_Agregee est demandée, THE Backend SHALL calculer les agrégats sur l'ensemble de la Descendance par requête serveur, sans retourner d'identifiants individuels dans la réponse agrégée.
|
||||
3. WHEN une Vue_Detail_Direct est demandée, THE Backend SHALL restreindre le jeu de résultats aux mandataires dont le `sponsor_account_id` est directement l'appelant (N+1).
|
||||
4. IF un appel vise un AccountId hors de la Portee_Hierarchique de l'appelant, THEN THE Backend SHALL répondre `403 Forbidden` sans révéler l'existence de la ressource (ne pas distinguer de `404` pour les ressources hors périmètre lorsque la divulgation serait sensible).
|
||||
5. THE Backend SHALL exposer les capacités de partage hiérarchique via un contrat commun que chaque Module implémente (fourniture d'agrégats et, le cas échéant, de détail N+1), sans que le socle ne présume des données de chaque Module.
|
||||
|
||||
> **Note de calcul (indicateurs envisagés, non figés)** : les premiers agrégats pressentis sont le nombre de mandats en entrée, le nombre de mandats en sortie, le chiffre d'affaires et le nombre de Filleuls par niveau. Leur définition précise relève des specs des Modules fournisseurs.
|
||||
|
||||
---
|
||||
|
||||
### Requirement 6: Annuaire des abonnés — aspects techniques
|
||||
|
||||
1. THE Backend SHALL exposer `GET /api/directory` retournant les Comptes et Membres_Declares du `network` du claim, paginé, avec un indicateur d'état (inscrit / déclaré).
|
||||
2. THE Backend SHALL exposer `GET /api/directory/tree` retournant l'Arbre_Parrainage de la Descendance de l'appelant.
|
||||
3. THE Backend SHALL n'exposer dans ces endpoints que des champs d'annuaire (nom, prénom, Identifiant_Reseau, état, lien hiérarchique) et aucune donnée métier de Module.
|
||||
4. THE Backend SHALL supporter une recherche serveur par nom, prénom ou Identifiant_Reseau et limiter la taille de page par défaut (ex. 50) avec pagination.
|
||||
|
||||
---
|
||||
|
||||
### Requirement 7: Abonnements et Packs — aspects techniques
|
||||
|
||||
1. THE Backend SHALL persister les Packs et les Abonnements, un Abonnement portant AccountId, PackId, dates d'effet et statut (`actif`, `expire`).
|
||||
2. THE Backend SHALL exposer `GET /api/subscription` (abonnement courant et Modules résultants) et `POST /api/subscription` (changement de Pack).
|
||||
3. WHEN un changement de Pack est appliqué, THE Backend SHALL recalculer côté serveur la liste des Modules_Accessibles par inclusion des Packs inférieurs.
|
||||
4. THE Backend SHALL déterminer les Modules_Consultation_Seule en comparant les Modules non inclus dans le Pack courant avec la présence de données persistées du Mandataire pour ces Modules.
|
||||
5. IF une action d'écriture est tentée sur un Module hors Abonnement, THEN THE Backend SHALL répondre `403 Forbidden` avec un code applicatif indiquant « abonnement requis ».
|
||||
6. WHERE un paiement en ligne sera introduit ultérieurement, THE Backend SHALL isoler la logique de facturation d'abonnement derrière une abstraction (le MVP gère l'activation manuelle par l'Administrateur ou l'auto-attribution Starter), sans dépendance de paiement dans le socle initial.
|
||||
|
||||
---
|
||||
|
||||
### Requirement 8: Catalogue et activation des Modules — aspects techniques
|
||||
|
||||
1. THE Backend SHALL persister un Catalogue_Modules { ModuleKey, libellé, PackId requis, actif } et l'exposer via `GET /api/modules`.
|
||||
2. THE Backend SHALL initialiser le Catalogue avec : `carnet-adresses`, `kms`, `facturation` → Pack Starter ; `crm`, `agenda` → Pack Pro.
|
||||
3. THE Backend SHALL protéger les routes de chaque Module par un contrôle d'autorisation vérifiant, à partir des claims et de l'Abonnement, le droit d'accès (plein accès ou consultation seule), et répondre `403 Forbidden` à défaut.
|
||||
4. THE Backend SHALL distinguer, dans la réponse `GET /api/modules`, l'état de chaque Module pour l'appelant (`accessible`, `consultation`, `verrouille`) afin que le Frontend adapte la navigation.
|
||||
5. THE Frontend SHALL masquer ou désactiver les actions d'écriture d'un Module en état `consultation` et proposer un parcours de montée en abonnement pour un Module `verrouille`.
|
||||
|
||||
---
|
||||
|
||||
### Requirement 9: Administration de la Plateforme — aspects techniques
|
||||
|
||||
1. THE Backend SHALL matérialiser le rôle Administrateur par le claim `role=admin` et protéger les routes `/api/admin/**` par une autorisation dédiée répondant `403 Forbidden` à tout appelant non admin.
|
||||
2. THE Backend SHALL exposer `GET /api/admin/accounts` (tous Reseaux, via la Base_Socle, avec Reseau, Abonnement, statut), `PUT /api/admin/accounts/{id}/subscription`, et `PUT /api/admin/modules` (gestion Module->Pack).
|
||||
3. THE Backend SHALL exposer `POST /api/admin/networks` et `PUT /api/admin/networks/{id}` pour l'Onboarding_Reseau et le paramétrage (Personnalisation_Reseau).
|
||||
4. THE Backend SHALL exposer `GET /api/admin/stats` retournant les KPI d'usage **remontés par chaque Reseau** (table `network_usage_kpis` de la Base_Socle), sans requête ni jointure traversant les Base_Reseau et sans donnée métier individuelle.
|
||||
5. THE Backend SHALL exposer des endpoints d'administration de l'Arbre_Parrainage (Base_Reseau concernée) permettant de rattacher, détacher ou réaffecter un mandataire, en refusant (`409 Conflict`) toute opération créant un cycle.
|
||||
|
||||
---
|
||||
|
||||
### Requirement 10: Tableau de bord — aspects techniques
|
||||
|
||||
1. THE Backend SHALL exposer `GET /api/dashboard` agrégeant les contributions des seuls Modules_Accessibles de l'appelant via un contrat de fourniture commun.
|
||||
2. THE Backend SHALL borner le périmètre des données du Tableau_De_Bord à l'appelant et à sa Portee_Hierarchique.
|
||||
3. THE Frontend SHALL afficher un état vide explicite par section lorsque le Module correspondant ne fournit aucun élément.
|
||||
4. THE Backend SHALL concevoir l'agrégation du Tableau_De_Bord de façon résiliente : la défaillance de la contribution d'un Module ne doit pas empêcher le rendu des autres sections.
|
||||
|
||||
---
|
||||
|
||||
### Requirement 11: Cadre applicatif et navigation — aspects techniques
|
||||
|
||||
1. THE Frontend SHALL être une application Angular (TypeScript) à routage protégé par un garde d'authentification redirigeant vers la connexion en l'absence de Session valide.
|
||||
2. THE Frontend SHALL construire dynamiquement la navigation des Modules à partir de `GET /api/modules` (états `accessible` / `consultation` / `verrouille`).
|
||||
3. THE Frontend SHALL présenter un composant d'identité (avatar par initiales) donnant accès au Profil, au paramétrage, à l'Abonnement et à la déconnexion.
|
||||
4. THE Frontend SHALL centraliser la présentation des erreurs d'API par mappage des Codes_HTTP vers des messages utilisateur en français (401 → reconnexion, 403 → accès/abonnement requis, 422 → erreurs de champ, 5xx → indisponibilité).
|
||||
5. THE Backend SHALL exposer `GET /api/profile` et `PUT /api/profile` permettant de modifier l'identité et les coordonnées, en interdisant la modification du `NetworkId`.
|
||||
|
||||
---
|
||||
|
||||
### Requirement 12: Persistance et isolation des données par Réseau — aspects techniques
|
||||
|
||||
1. THE Backend SHALL utiliser MariaDB/MySQL avec une Base_Socle et une Base_Reseau par Reseau ; chaque Config_BDD est fournie par variable d'environnement, pointant vers une instance conteneurisée **ou** un serveur externe, sans changement de code.
|
||||
2. THE Backend SHALL gérer deux jeux de Migrations EF Core : le schéma socle appliqué à la Base_Socle, et le schéma réseau appliqué de façon idempotente à **chaque** Base_Reseau.
|
||||
3. THE Backend SHALL utiliser deux DbContext (`SocleDbContext`, `ReseauDbContext`), le `ReseauDbContext` recevant sa chaîne de connexion de la requête via le NetworkConnectionResolver.
|
||||
4. THE Backend SHALL garantir qu'aucune requête ni jointure ne traverse deux Base_Reseau, et qu'aucune donnée d'un Reseau n'est atteignable depuis le contexte d'un autre Reseau ; les requêtes métier sont en outre bornées à la Portee_Hierarchique de l'appelant.
|
||||
5. WHEN une opération écrit plusieurs entités au sein d'une même base, THE Backend SHALL l'exécuter dans une transaction unique avec rollback complet en cas d'échec ; WHEN une opération touche à la fois la Base_Socle et une Base_Reseau (ex. création de Compte + profil), THE Backend SHALL la séquencer avec compensation applicative (pas de transaction distribuée).
|
||||
6. THE Backend SHALL ne jamais journaliser de secret (Mot_De_Passe, token, chaîne de connexion) et masquer ces valeurs dans les logs et messages d'erreur ; la Base_Socle ne stocke qu'une clé logique de connexion par Reseau.
|
||||
7. THE Backend SHALL exposer `GET /health` pour la sonde de disponibilité du déploiement (healthcheck conteneur).
|
||||
8. THE deployment SHALL s'orchestrer via Docker Compose (service Backend ASP.NET Core, service Frontend statique), les bases MariaDB (Base_Socle et Base_Reseau) étant soit des services du Compose, soit un serveur externe référencé par la Config_BDD.
|
||||
Reference in New Issue
Block a user