mirror of
https://github.com/MaksTinyWorkshop/_Assistant_Lead_Tech
synced 2026-04-06 13:31:43 +02:00
docs: capitaliser les patterns valides du 16 mars
This commit is contained in:
@@ -8,7 +8,7 @@ Ce fichier contient **uniquement** des patterns back-end :
|
|||||||
|
|
||||||
Objectif : éviter de réinventer la roue et réduire le temps de debug.
|
Objectif : éviter de réinventer la roue et réduire le temps de debug.
|
||||||
|
|
||||||
Dernière mise à jour : 12-03-2026
|
Dernière mise à jour : 16-03-2026
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -32,6 +32,11 @@ Dernière mise à jour : 12-03-2026
|
|||||||
- [Restauration d’achats Stripe en 3 étapes](#pattern-restauration-achats-stripe)
|
- [Restauration d’achats Stripe en 3 étapes](#pattern-restauration-achats-stripe)
|
||||||
- [Mapping explicite de `P2002` Prisma sur update de champ unique](#pattern-prisma-p2002-update-unique)
|
- [Mapping explicite de `P2002` Prisma sur update de champ unique](#pattern-prisma-p2002-update-unique)
|
||||||
- [Autorisation interne minimale sans RBAC complet](#pattern-autorisation-interne-minimale)
|
- [Autorisation interne minimale sans RBAC complet](#pattern-autorisation-interne-minimale)
|
||||||
|
- [Anti-énumération sur endpoints auth liés à un email](#pattern-anti-enumeration-auth-email)
|
||||||
|
- [Token à usage unique — génération, hash et invalidation atomique](#pattern-token-usage-unique)
|
||||||
|
- [Guardrails multi-tenant — 403 vs 404 selon la sémantique](#pattern-guardrails-multi-tenant-403-404)
|
||||||
|
- [Repository tenant-aware — `tenantId` obligatoire dans la signature](#pattern-repository-tenant-aware)
|
||||||
|
- [Défense en profondeur — inclure `tenantId` dans les updates](#pattern-tenantid-dans-updates)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -747,3 +752,176 @@ handlePackWebhookEvent(event): PackWebhookResult | null
|
|||||||
|
|
||||||
- On préfère 5 patterns solides à 50 “bons conseils”.
|
- On préfère 5 patterns solides à 50 “bons conseils”.
|
||||||
- Un pattern = une idée actionnable + son cadre d’utilisation.
|
- Un pattern = une idée actionnable + son cadre d’utilisation.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<a id="pattern-anti-enumeration-auth-email"></a>
|
||||||
|
## Pattern : Anti-énumération sur endpoints auth liés à un email
|
||||||
|
|
||||||
|
- Objectif : empêcher qu’un endpoint auth révèle si un compte existe, n’existe pas ou n’est pas éligible.
|
||||||
|
- Contexte : reset de mot de passe, invitation, vérification de compte, login ou tout flux qui part d’un email utilisateur.
|
||||||
|
- Quand l’utiliser : dès qu’une requête auth touche un identifiant de type email.
|
||||||
|
- Quand l’éviter : jamais sur une surface exposée.
|
||||||
|
- Avantage :
|
||||||
|
- réduit la fuite d’information sur les comptes existants
|
||||||
|
- homogénéise les réponses côté client
|
||||||
|
- se combine bien avec les garde-fous anti-abus
|
||||||
|
- Limites / vigilance :
|
||||||
|
- ne protège pas seul contre le brute-force, à combiner avec du rate-limiting
|
||||||
|
- les logs internes doivent conserver la vraie cause sans l’exposer au client
|
||||||
|
- Validé le : 16-03-2026
|
||||||
|
- Contexte technique : Node.js / auth applicative / API HTTP
|
||||||
|
|
||||||
|
### Implémentation (exemple minimal)
|
||||||
|
|
||||||
|
```txt
|
||||||
|
- retourner la même réponse HTTP 200 qu’un compte existe ou non
|
||||||
|
- ne jamais distinguer "email inconnu", "email connu" ou "compte OAuth-only" dans la réponse
|
||||||
|
- journaliser la cause réelle côté serveur
|
||||||
|
- ajouter un rate-limiting basé sur email + IP
|
||||||
|
```
|
||||||
|
|
||||||
|
### Checklist
|
||||||
|
|
||||||
|
- Réponse client uniforme pour les cas compte connu/inconnu/non éligible
|
||||||
|
- Aucune fuite d’existence dans le message ou le code d’erreur
|
||||||
|
- Rate-limiting présent sur les endpoints exposés
|
||||||
|
- Logs internes exploitables
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<a id="pattern-token-usage-unique"></a>
|
||||||
|
## Pattern : Token à usage unique — génération, hash et invalidation atomique
|
||||||
|
|
||||||
|
- Objectif : standardiser la création et la consommation de tokens sensibles sans stocker de secret brut en base.
|
||||||
|
- Contexte : invitation, reset de mot de passe, vérification d’email, lien magique ou tout token one-shot.
|
||||||
|
- Quand l’utiliser : pour tout token à usage unique transmis à l’utilisateur.
|
||||||
|
- Quand l’éviter : sessions longues ou secrets devant être relus en clair côté serveur.
|
||||||
|
- Avantage :
|
||||||
|
- réduit l’impact d’une fuite de base
|
||||||
|
- garde des tokens URL-safe
|
||||||
|
- favorise une consommation atomique et réutilisable
|
||||||
|
- Limites / vigilance :
|
||||||
|
- la consommation doit rester atomique
|
||||||
|
- la politique d’expiration doit être explicite
|
||||||
|
- Validé le : 16-03-2026
|
||||||
|
- Contexte technique : Node.js `crypto` / Prisma / email ou URL signée
|
||||||
|
|
||||||
|
### Implémentation (exemple minimal)
|
||||||
|
|
||||||
|
```txt
|
||||||
|
- générer le token avec `crypto.randomBytes(32).toString("base64url")`
|
||||||
|
- stocker uniquement le hash SHA-256 du token en base
|
||||||
|
- transmettre le token brut uniquement via URL ou email
|
||||||
|
- recalculer le hash côté serveur lors de la consommation
|
||||||
|
- invalider le token dans une transaction atomique après usage
|
||||||
|
```
|
||||||
|
|
||||||
|
### Checklist
|
||||||
|
|
||||||
|
- Token brut jamais persisté en base
|
||||||
|
- Hash recalculé côté serveur pour la vérification
|
||||||
|
- Expiration explicite
|
||||||
|
- Invalidation atomique après consommation
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<a id="pattern-guardrails-multi-tenant-403-404"></a>
|
||||||
|
## Pattern : Guardrails multi-tenant — 403 vs 404 selon la sémantique
|
||||||
|
|
||||||
|
- Objectif : éviter les fuites d’information inter-tenant tout en gardant une sémantique d’erreur claire.
|
||||||
|
- Contexte : API multi-tenant avec ressources métier isolées et surfaces internes ou opérateur.
|
||||||
|
- Quand l’utiliser : dès qu’une vérification d’appartenance tenant peut soit refuser explicitement l’accès, soit masquer l’existence d’une ressource.
|
||||||
|
- Quand l’éviter : contexte mono-tenant ou endpoints purement internes sans enjeu de fuite.
|
||||||
|
- Avantage :
|
||||||
|
- clarifie la convention de sécurité
|
||||||
|
- évite les réponses incohérentes selon les modules
|
||||||
|
- facilite les tests d’isolation tenant
|
||||||
|
- Limites / vigilance :
|
||||||
|
- la convention doit être documentée et appliquée partout
|
||||||
|
- un mauvais choix entre 403 et 404 peut révéler une information sensible
|
||||||
|
- Validé le : 16-03-2026
|
||||||
|
- Contexte technique : API multi-tenant / HTTP / services métier
|
||||||
|
|
||||||
|
### Implémentation (exemple minimal)
|
||||||
|
|
||||||
|
```txt
|
||||||
|
- `assertTenantMatch(actor, expectedTenantId)` -> 403 quand la ressource est connue mais l’accès refusé
|
||||||
|
- `assertResourceBelongsToTenant(actor, resourceTenantId)` -> 404 quand il faut masquer l’existence d’une ressource d’un autre tenant
|
||||||
|
- documenter la convention dans le module
|
||||||
|
- couvrir les deux sémantiques par des tests dédiés
|
||||||
|
```
|
||||||
|
|
||||||
|
### Checklist
|
||||||
|
|
||||||
|
- Convention 403 vs 404 documentée
|
||||||
|
- Helpers distincts selon la sémantique métier
|
||||||
|
- Aucune fuite d’existence cross-tenant sur les ressources métier
|
||||||
|
- Tests dédiés sur les deux comportements
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<a id="pattern-repository-tenant-aware"></a>
|
||||||
|
## Pattern : Repository tenant-aware — `tenantId` obligatoire dans la signature
|
||||||
|
|
||||||
|
- Objectif : rendre impossible par construction une query non scopée sur un domaine multi-tenant.
|
||||||
|
- Contexte : repositories ou services d’accès aux données sur ressources tenant-scoped.
|
||||||
|
- Quand l’utiliser : dès qu’un domaine métier est massivement filtré par tenant.
|
||||||
|
- Quand l’éviter : domaines réellement globaux ou méthodes volontairement cross-tenant.
|
||||||
|
- Avantage :
|
||||||
|
- force le scoping dès la signature TypeScript
|
||||||
|
- réduit les oublis de filtre tenant dans les call sites
|
||||||
|
- rend les exceptions cross-tenant visibles
|
||||||
|
- Limites / vigilance :
|
||||||
|
- les exceptions cross-tenant doivent être rares et documentées explicitement
|
||||||
|
- ne dispense pas d’un second garde-fou dans les mutations sensibles
|
||||||
|
- Validé le : 16-03-2026
|
||||||
|
- Contexte technique : TypeScript / Prisma / architecture repository
|
||||||
|
|
||||||
|
### Implémentation (exemple minimal)
|
||||||
|
|
||||||
|
```txt
|
||||||
|
- chaque méthode métier tenant-scoped prend `tenantId` en paramètre obligatoire
|
||||||
|
- les méthodes réellement cross-tenant sont nommées et documentées comme exception
|
||||||
|
- les call sites Prisma directs sur ces domaines sont interdits ou supprimés
|
||||||
|
```
|
||||||
|
|
||||||
|
### Checklist
|
||||||
|
|
||||||
|
- `tenantId` obligatoire sur les méthodes tenant-scoped
|
||||||
|
- Exceptions cross-tenant documentées
|
||||||
|
- Appels directs concurrents à Prisma supprimés
|
||||||
|
- Tests sur scoping tenant au niveau repository
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<a id="pattern-tenantid-dans-updates"></a>
|
||||||
|
## Pattern : Défense en profondeur — inclure `tenantId` dans les updates
|
||||||
|
|
||||||
|
- Objectif : éviter une mutation cross-tenant même si un identifiant a été mal résolu en amont.
|
||||||
|
- Contexte : `update` ou `updateMany` sur une ressource tenant-scoped.
|
||||||
|
- Quand l’utiliser : dès qu’une mutation dépend d’un `id` reçu ou résolu dans un flux multi-tenant.
|
||||||
|
- Quand l’éviter : ressources globales non liées à un tenant.
|
||||||
|
- Avantage :
|
||||||
|
- ajoute une seconde barrière côté base
|
||||||
|
- réduit l’impact d’un call site mal scopé
|
||||||
|
- rend la mutation plus sûre sans complexité forte
|
||||||
|
- Limites / vigilance :
|
||||||
|
- ne remplace pas le scoping en lecture ni la vérification d’autorisation
|
||||||
|
- suppose que `tenantId` soit disponible au moment de la mutation
|
||||||
|
- Validé le : 16-03-2026
|
||||||
|
- Contexte technique : Prisma / multi-tenant / mutations métier
|
||||||
|
|
||||||
|
### Implémentation (exemple minimal)
|
||||||
|
|
||||||
|
```txt
|
||||||
|
- préférer `where: { id, tenantId }` à `where: { id }` sur les updates tenant-scoped
|
||||||
|
- appliquer la même règle sur `updateMany` et opérations de révocation
|
||||||
|
- conserver les vérifications métier amont, mais ne pas leur déléguer toute la sécurité
|
||||||
|
```
|
||||||
|
|
||||||
|
### Checklist
|
||||||
|
|
||||||
|
- `tenantId` présent dans les clauses `where` des updates sensibles
|
||||||
|
- Pas de mutation tenant-scoped basée sur `id` seul
|
||||||
|
- Revue explicite des exceptions documentées
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ Ce fichier recense des risques back-end susceptibles de provoquer :
|
|||||||
- régressions coûteuses,
|
- régressions coûteuses,
|
||||||
- incohérences de données.
|
- incohérences de données.
|
||||||
|
|
||||||
Dernière mise à jour : 10-03-2026
|
Dernière mise à jour : 16-03-2026
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -44,6 +44,8 @@ Dernière mise à jour : 10-03-2026
|
|||||||
- [Stripe `list()` sans gestion de `has_more`](#risque-stripe-list-has-more)
|
- [Stripe `list()` sans gestion de `has_more`](#risque-stripe-list-has-more)
|
||||||
- [Concurrence entre activation locale et webhook sur transition trial → payant](#risque-trial-payant-concurrence)
|
- [Concurrence entre activation locale et webhook sur transition trial → payant](#risque-trial-payant-concurrence)
|
||||||
- [`jest.clearAllMocks()` dans des `beforeEach` imbriqués avec mocks Prisma](#risque-jest-clearallmocks-imbrique)
|
- [`jest.clearAllMocks()` dans des `beforeEach` imbriqués avec mocks Prisma](#risque-jest-clearallmocks-imbrique)
|
||||||
|
- [Suppression du cookie après révocation DB sur logout](#risque-cookie-apres-revocation-db)
|
||||||
|
- [Repository layer non branché (dead layer)](#risque-repository-dead-layer)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -503,3 +505,51 @@ if (!user?.userId) {
|
|||||||
- Éviter les `clearAllMocks()` concurrents à plusieurs niveaux de nesting
|
- Éviter les `clearAllMocks()` concurrents à plusieurs niveaux de nesting
|
||||||
- Préférer un setup explicite et local par scénario quand les mocks Prisma sont structurants
|
- Préférer un setup explicite et local par scénario quand les mocks Prisma sont structurants
|
||||||
- Contexte technique : Jest / Prisma / tests NestJS — 10-03-2026
|
- Contexte technique : Jest / Prisma / tests NestJS — 10-03-2026
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<a id="risque-cookie-apres-revocation-db"></a>
|
||||||
|
## Suppression du cookie après révocation DB sur logout
|
||||||
|
|
||||||
|
### Risques
|
||||||
|
|
||||||
|
- Si la révocation DB échoue avant la suppression du cookie, l’utilisateur garde un cookie local devenu incohérent
|
||||||
|
- L’utilisateur peut rester bloqué dans un état où il ne peut plus se déconnecter proprement
|
||||||
|
- Le comportement diffère selon la disponibilité de la base
|
||||||
|
|
||||||
|
### Symptômes
|
||||||
|
|
||||||
|
- Logout qui échoue par intermittence quand la DB est instable
|
||||||
|
- Cookie de session toujours présent côté navigateur après erreur serveur
|
||||||
|
- Réessais de logout qui produisent des états difficiles à diagnostiquer
|
||||||
|
|
||||||
|
### Bonnes pratiques / mitigations
|
||||||
|
|
||||||
|
- Toujours supprimer le cookie en premier, même si la révocation DB échoue ensuite
|
||||||
|
- Traiter la suppression côté DB en best-effort ou avec gestion d’idempotence adaptée
|
||||||
|
- Vérifier en test qu’un échec DB ne laisse pas l’accès browser actif
|
||||||
|
- Contexte technique : Next.js / auth par cookie / session persistée — 16-03-2026
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<a id="risque-repository-dead-layer"></a>
|
||||||
|
## Repository layer non branché (dead layer)
|
||||||
|
|
||||||
|
### Risques
|
||||||
|
|
||||||
|
- Donner une impression de sécurité alors que le code métier continue d’appeler l’ORM directement
|
||||||
|
- Multiplier les chemins d’accès aux données avec des règles différentes
|
||||||
|
- Payer le coût d’une abstraction qui n’a aucun effet réel
|
||||||
|
|
||||||
|
### Symptômes
|
||||||
|
|
||||||
|
- Un repository est créé mais les anciens call sites Prisma restent en place
|
||||||
|
- Les nouvelles règles de scoping ou de sécurité ne s’appliquent pas partout
|
||||||
|
- La review montre des fichiers de repository peu ou jamais importés
|
||||||
|
|
||||||
|
### Bonnes pratiques / mitigations
|
||||||
|
|
||||||
|
- Vérifier qu’une nouvelle couche d’abstraction est réellement branchée dans les call sites existants
|
||||||
|
- Rechercher explicitement les appels directs restants lors de la review
|
||||||
|
- Refuser l’introduction d’une couche repository tant que la migration effective n’est pas faite
|
||||||
|
- Contexte technique : TypeScript / Prisma / refactor d’accès aux données — 16-03-2026
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ Il sert de **mémoire durable** pour éviter :
|
|||||||
- de redélibérer éternellement sur des sujets déjà tranchés,
|
- de redélibérer éternellement sur des sujets déjà tranchés,
|
||||||
- de propager des “bonnes pratiques” théoriques non éprouvées.
|
- de propager des “bonnes pratiques” théoriques non éprouvées.
|
||||||
|
|
||||||
Dernière mise à jour : 12-03-2026
|
Dernière mise à jour : 16-03-2026
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -23,6 +23,7 @@ Dernière mise à jour : 12-03-2026
|
|||||||
- [Formulaire robuste avec validation et erreurs explicites](#pattern-formulaire-robuste)
|
- [Formulaire robuste avec validation et erreurs explicites](#pattern-formulaire-robuste)
|
||||||
- [Navigation réactive post-action async (React / Expo Router)](#pattern-navigation-reactive-post-action-async)
|
- [Navigation réactive post-action async (React / Expo Router)](#pattern-navigation-reactive-post-action-async)
|
||||||
- [Refresh idempotent sur store de liste paginée](#pattern-refresh-idempotent-liste-paginee)
|
- [Refresh idempotent sur store de liste paginée](#pattern-refresh-idempotent-liste-paginee)
|
||||||
|
- [UI admin légère sur domaine existant](#pattern-ui-admin-legere-domaine-existant)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -336,6 +337,52 @@ const handleOAuth = async () => {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
<a id="pattern-ui-admin-legere-domaine-existant"></a>
|
||||||
|
## Pattern : UI admin légère sur domaine existant
|
||||||
|
|
||||||
|
### Synthèse
|
||||||
|
|
||||||
|
- **Objectif** : ajouter une capacité interne simple sans ouvrir trop tôt un back-office séparé ni dupliquer la logique métier.
|
||||||
|
- **Contexte** : app mobile ou SPA avec un domaine métier déjà structuré et quelques actions internes ponctuelles.
|
||||||
|
- **Quand l’utiliser** : publication, activation, modération légère, bascule de statut, action opérateur simple.
|
||||||
|
- **Quand l’éviter** : permissions complexes, workflows multiples, audit riche ou volume d’actions qui justifie un vrai espace d’administration.
|
||||||
|
|
||||||
|
### Analyse
|
||||||
|
|
||||||
|
- **Avantages** :
|
||||||
|
- réutilise le service et le store métier existants
|
||||||
|
- limite le coût de structure pour une capacité admin mince
|
||||||
|
- garde les mutations testables et lisibles
|
||||||
|
- **Limites / vigilance** :
|
||||||
|
- ne pas laisser cette approche dériver vers un pseudo back-office implicite
|
||||||
|
- le refresh après mutation doit être explicite sur les vues impactées
|
||||||
|
|
||||||
|
### Validation
|
||||||
|
|
||||||
|
- Validé le : 10-03-2026
|
||||||
|
- Contexte technique : React Native / Expo Router / store de domaine
|
||||||
|
|
||||||
|
### Implémentation (exemple minimal)
|
||||||
|
|
||||||
|
```txt
|
||||||
|
- ajouter une route dédiée minimale pour l’action interne
|
||||||
|
- réutiliser le service/store métier existant au lieu de créer une couche parallèle
|
||||||
|
- afficher le statut courant avant action
|
||||||
|
- bloquer les actions concurrentes avec un flag explicite (`isUpdating*`)
|
||||||
|
- déclencher un refresh explicite des vues impactées après succès
|
||||||
|
- éviter les mutations en fire-and-forget
|
||||||
|
```
|
||||||
|
|
||||||
|
### Checklist
|
||||||
|
|
||||||
|
- [ ] Route dédiée minimale, pas de mini back-office générique
|
||||||
|
- [ ] Réutilisation du domaine métier existant
|
||||||
|
- [ ] Garde-fou explicite contre les doubles actions
|
||||||
|
- [ ] Refresh explicite après mutation réussie
|
||||||
|
- [ ] Tests sur succès, erreur et action concurrente
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### Principes transverses
|
### Principes transverses
|
||||||
|
|
||||||
- Un pattern = une responsabilité claire
|
- Un pattern = une responsabilité claire
|
||||||
|
|||||||
@@ -75,6 +75,53 @@ Description courte, factuelle, orientée réutilisation.
|
|||||||
4. Une fois intégrée, la proposition doit être **supprimée de ce fichier**.
|
4. Une fois intégrée, la proposition doit être **supprimée de ce fichier**.
|
||||||
5. La structure de ce fichier est **restaurée à son état initial** (voir `70_templates/template_a_capitaliser.md`).
|
5. La structure de ce fichier est **restaurée à son état initial** (voir `70_templates/template_a_capitaliser.md`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Revue de tri — 2026-03-16
|
||||||
|
|
||||||
|
Cette section sert à qualifier les propositions en attente avant intégration manuelle.
|
||||||
|
|
||||||
|
## Rappel
|
||||||
|
|
||||||
|
L'exemple plus haut est conservé volontairement pour montrer aux agents le format attendu.
|
||||||
|
Il ne fait pas partie des propositions à intégrer.
|
||||||
|
|
||||||
|
## À intégrer
|
||||||
|
|
||||||
|
Intégré le 16-03-2026 dans les fichiers cibles.
|
||||||
|
|
||||||
|
## À fusionner avant intégration
|
||||||
|
|
||||||
|
- `2026-03-16 — app-template-resto` → `10_backend_patterns_valides.md`
|
||||||
|
`Isolation des guards purs des modules server-only`
|
||||||
|
- `2026-03-16 — app-template-resto` → `10_backend_patterns_valides.md`
|
||||||
|
``server-only` réservé aux modules avec APIs Next.js exclusivement serveur`
|
||||||
|
- `2026-03-16 — app-template-resto` → `10_backend_patterns_valides.md`
|
||||||
|
`Server Action Next.js — isoler la logique pure dans un module injectable`
|
||||||
|
|
||||||
|
Fusion recommandée :
|
||||||
|
`Next.js server-only / Server Actions : garder l'orchestration runtime-only fine et extraire la logique pure testable`
|
||||||
|
|
||||||
|
- `2026-03-16 — app-template-resto` → `10_backend_patterns_valides.md`
|
||||||
|
`Transaction obligatoire pour les opérations auth multi-étapes`
|
||||||
|
- `2026-03-16 — app-template-resto` → `10_backend_patterns_valides.md`
|
||||||
|
`Suppression de session idempotente (P2025)`
|
||||||
|
|
||||||
|
Fusion recommandée :
|
||||||
|
`Opérations auth sensibles : atomiques, idempotentes et cohérentes en cas d'erreur`
|
||||||
|
|
||||||
|
## À laisser en tampon
|
||||||
|
|
||||||
|
- `2026-03-10 — app-alexandrie` → `10_backend_patterns_valides.md`
|
||||||
|
`Progression V1 calculée sans persistance dédiée`
|
||||||
|
Motif : intéressant mais encore trop lié à un choix produit / modélisation métier.
|
||||||
|
- `2026-03-16 — app-template-resto` → `10_backend_risques_et_vigilance.md`
|
||||||
|
`Divergence schéma / spec story`
|
||||||
|
Motif : utile en review, mais trop lié au process de story pour la mémoire durable.
|
||||||
|
- `2026-03-16 — app-template-resto / code-review story 1.11` → `10_backend_risques_et_vigilance.md`
|
||||||
|
`server-only dans les repositories bloque les tests unitaires`
|
||||||
|
Motif : le problème est réel, mais la solution proposée (`stub` local) ne doit pas devenir un standard.
|
||||||
|
|
||||||
2026-03-10 — app-alexandrie
|
2026-03-10 — app-alexandrie
|
||||||
|
|
||||||
FILE_UPDATE_PROPOSAL
|
FILE_UPDATE_PROPOSAL
|
||||||
@@ -95,13 +142,97 @@ Pour une feature de progression minimum viable :
|
|||||||
|
|
||||||
Évite le scope creep vers un module `achievements` prématuré et garde un contrat stable.
|
Évite le scope creep vers un module `achievements` prématuré et garde un contrat stable.
|
||||||
|
|
||||||
2026-03-10 — app-alexandrie
|
2026-03-16 — app-template-resto
|
||||||
|
|
||||||
FILE_UPDATE_PROPOSAL
|
FILE_UPDATE_PROPOSAL
|
||||||
Fichier cible : 10_frontend_patterns_valides.md
|
Fichier cible : 10_backend_patterns_valides.md
|
||||||
|
|
||||||
Pourquoi :
|
Pourquoi :
|
||||||
Pour une capacité admin mince sur mobile, une route dédiée légère branchée sur le domaine existant et un refresh explicite du store après mutation permettent de rester testable et robuste sans lancer un back-office séparé.
|
Code review story 1.9 a révélé un pattern récurrent : les services avec opérations multi-étapes (hash + update + delete) doivent systématiquement utiliser $transaction pour éviter les race conditions.
|
||||||
|
|
||||||
Proposition :
|
Proposition :
|
||||||
Pattern "UI admin légère sur domaine existant" : pour une action interne simple (publication, activation, modération légère), ajouter une route dédiée minimale qui réutilise le service/store métier existant, afficher le statut courant, bloquer les actions concurrentes avec un flag `isUpdating*`, et déclencher un refresh explicite des vues impactées après succès au lieu d’un `fire-and-forget`.
|
**Pattern : Transaction obligatoire pour les opérations auth multi-étapes**
|
||||||
|
Toute opération qui combine hashing de mot de passe + update user + invalidation de tokens doit être enveloppée dans `prisma.$transaction()`. Sans transaction, une interruption entre les étapes laisse l'état incohérent (ex: token valide après reset du mot de passe).
|
||||||
|
Exemple : consumePasswordReset — marquer consumedAt + update passwordHash + deleteMany autres tokens dans une seule transaction.
|
||||||
|
|
||||||
|
---
|
||||||
|
2026-03-16 — app-template-resto
|
||||||
|
|
||||||
|
FILE_UPDATE_PROPOSAL
|
||||||
|
Fichier cible : 10_backend_risques_et_vigilance.md
|
||||||
|
|
||||||
|
Pourquoi :
|
||||||
|
Code review story 1.9 a révélé un anti-pattern : les tâches de story peuvent déclarer [x] consumedAt sans que le champ existe réellement dans le schéma Prisma.
|
||||||
|
|
||||||
|
Proposition :
|
||||||
|
**Anti-pattern : Divergence schéma / spec story**
|
||||||
|
Lors d'une implémentation, valider que chaque champ mentionné dans les tâches (consumedAt, tokenHash, etc.) existe réellement dans le schéma Prisma avant de marquer la tâche [x]. Une story peut décrire consumedAt comme stratégie de conception sans que le champ soit présent — toujours croiser avec schema.prisma.
|
||||||
|
|
||||||
|
---
|
||||||
|
2026-03-16 — app-template-resto
|
||||||
|
|
||||||
|
FILE_UPDATE_PROPOSAL
|
||||||
|
Fichier cible : 10_backend_patterns_valides.md
|
||||||
|
|
||||||
|
Pourquoi :
|
||||||
|
Le module sendPasswordResetEmail utilise `server-only` ce qui le rend non-importable dans le runner de tests Node. Résolution : tester la logique pure (safeHttpUrl) dans un fichier séparé sans dépendances Next.js.
|
||||||
|
|
||||||
|
Proposition :
|
||||||
|
**Pattern : Isolation des guards purs des modules server-only**
|
||||||
|
Extraire la logique pure (validation URL, sanitisation) dans des fonctions utilitaires sans import `server-only` ou `nodemailer`. Le module email orchestre uniquement. Cela permet de tester les guards en isolation sans les contraintes du runtime Next.js.
|
||||||
|
|
||||||
|
---
|
||||||
|
2026-03-16 — app-template-resto
|
||||||
|
|
||||||
|
FILE_UPDATE_PROPOSAL
|
||||||
|
Fichier cible : 10_backend_patterns_valides.md
|
||||||
|
|
||||||
|
Pourquoi :
|
||||||
|
Pattern validé sur story 1.10 — la règle `server-only` vs testabilité est implicite dans le projet mais mérite d'être explicitée pour les agents futurs.
|
||||||
|
|
||||||
|
Proposition :
|
||||||
|
**Pattern : `server-only` réservé aux modules avec APIs Next.js exclusivement serveur**
|
||||||
|
Ne pas mettre `import "server-only"` sur les modules de logique pure injectés via dépendances (ex: `deleteSession({ prisma, sessionToken })`). Réserver `server-only` aux modules qui appellent des APIs Next.js runtime-only (`cookies()`, `headers()`, `redirect()`). Les modules purs sans ces imports peuvent être importés par le test runner Node et testés unitairement sans friction.
|
||||||
|
|
||||||
|
---
|
||||||
|
2026-03-16 — app-template-resto
|
||||||
|
|
||||||
|
FILE_UPDATE_PROPOSAL
|
||||||
|
Fichier cible : 10_backend_patterns_valides.md
|
||||||
|
|
||||||
|
Pourquoi :
|
||||||
|
Pattern validé sur story 1.10 — suppression de session avec gestion idempotente, réutilisable pour toute opération de révocation.
|
||||||
|
|
||||||
|
Proposition :
|
||||||
|
**Pattern : Suppression de session idempotente (P2025)**
|
||||||
|
Lors d'une déconnexion ou révocation de session, entourer le `prisma.session.delete()` d'un try/catch qui absorbe silencieusement le code Prisma `P2025` (record not found). Une session peut déjà avoir été supprimée (expiration, logout concurrent) — ce n'est pas une erreur, ne pas la propager.
|
||||||
|
|
||||||
|
---
|
||||||
|
2026-03-16 — app-template-resto
|
||||||
|
|
||||||
|
FILE_UPDATE_PROPOSAL
|
||||||
|
Fichier cible : 10_backend_patterns_valides.md
|
||||||
|
|
||||||
|
Pourquoi :
|
||||||
|
Pattern validé sur story 1.10 — Server Action Next.js qui orchestre des dépendances Next.js runtime non-testables : isoler la logique pure dans un module injectable.
|
||||||
|
|
||||||
|
Proposition :
|
||||||
|
**Pattern : Server Action Next.js — isoler la logique pure dans un module injectable**
|
||||||
|
Une Server Action qui appelle `cookies()`, `headers()` ou `redirect()` ne peut pas être testée unitairement (imports runtime-only). Pattern : extraire la logique pure (suppression DB, validation) dans une fonction avec injection de dépendances (`performSignOut({ prismaClient, sessionToken, redirectFn })`). La Server Action reste fine et orchestre uniquement les dépendances Next.js. Le module extrait est testable sans friction avec le runner Node natif.
|
||||||
|
|
||||||
|
---
|
||||||
|
2026-03-16 — app-template-resto / code-review story 1.11
|
||||||
|
|
||||||
|
FILE_UPDATE_PROPOSAL
|
||||||
|
Fichier cible : 10_backend_risques_et_vigilance.md
|
||||||
|
|
||||||
|
Pourquoi :
|
||||||
|
`import "server-only"` dans les repositories casse les tests Node.js hors Next.js — rencontré lors de cette review.
|
||||||
|
|
||||||
|
Proposition :
|
||||||
|
## Risque : `server-only` dans les repositories bloque les tests unitaires
|
||||||
|
|
||||||
|
`import "server-only"` empêche l'exécution des fichiers hors runtime Next.js.
|
||||||
|
Solution : créer un stub `node_modules/server-only/index.js` (no-op) pour les tests.
|
||||||
|
Alternativement, ne mettre `server-only` que dans les fichiers qui utilisent des APIs
|
||||||
|
Next.js (`cookies()`, `headers()`), pas dans les repositories purs.
|
||||||
|
|||||||
Reference in New Issue
Block a user