PD-285 — Plan d'implémentation¶
1. Découpage en composants¶
| # | Composant | Responsabilité | Fichier(s) | Impact |
|---|---|---|---|---|
| C1 | Migration SQL | UPDATE max_size_bytes de 104857600 à 524288000 pour DEFAULT et B2C_EVIDENCE_MINOR | src/database/migrations/<timestamp>-RaiseMaxSize500MB.ts | Données DB |
| C2 | Tests unitaires config | Mettre à jour les valeurs de mock et assertions de 104857600 à 524288000 | test/unit/**/category-config.service.spec.ts, test/unit/**/deposit.controller.spec.ts | Tests |
| C3 | Amendement PD-252 | Modifier PD-252-besoin.md et PD-252-specification.md : 100 MB → 500 MB | ProbatioVault-doc/docs/epics/legal-compliance/PD-252-*/PD-252-besoin.md, PD-252-specification.md | Documentation |
| C4 | Cohérence inter-EB | Vérifier que la pipeline coherence-report.py ne signale plus l'écart PD-101 vs backend | Script existant scripts/coherence/coherence-report.py | Validation |
Pas de composant logiciel modifié : Le middleware (category-size.middleware.ts) et le service (category-config.service.ts) lisent maxSizeBytes depuis la DB. Il n'y a aucune constante hardcodée dans le code applicatif — seule la migration seed contient 104857600. Le relèvement se fait par migration SQL uniquement.
2. Flux techniques¶
Flux F1 — Migration DB (C1)¶
1. TypeORM exécute la migration
2. UPDATE vault_secure.document_category_configs
SET max_size_bytes = 524288000, updated_at = NOW()
WHERE category IN ('DEFAULT', 'B2C_EVIDENCE_MINOR')
3. Le cache CategoryConfigService expire après 60s (INV-T5-03)
4. Les requêtes suivantes utilisent la nouvelle limite
Flux F2 — Validation upload (existant, inchangé)¶
1. Requête upload → CategorySizeMiddleware
2. Middleware lit Content-Length, compare à resolve(null).maxSizeBytes (désormais 524288000)
3. Si > 524288000 → PayloadTooLargeException ERR-79-004
4. Sinon → DepositController → CategoryConfigService.assertSizeAllowed()
5. Si taille réelle > 524288000 → PayloadTooLargeException ERR-79-004
6. Sinon → flux nominal (dépôt, hash, stockage)
Flux F3 — Amendement documentaire (C3)¶
1. Modifier PD-252-besoin.md : remplacer "max 100 MB" par "max 500 MB" pour les 2 catégories
2. Modifier PD-252-specification.md : remplacer la valeur 100 par 500 dans le tableau des seuils
3. Vérifier cohérence textuelle entre les deux documents
2bis. Diagrammes Mermaid¶
Graphe de dépendances entre composants¶
graph TD
C1["C1 — Migration SQL<br/><code>RaiseMaxSize500MB.ts</code>"]
C2["C2 — Tests unitaires config<br/><code>category-config.service.spec.ts</code><br/><code>deposit.controller.spec.ts</code>"]
C3["C3 — Amendement PD-252<br/><code>PD-252-besoin.md</code><br/><code>PD-252-specification.md</code>"]
C4["C4 — Cohérence inter-EB<br/><code>coherence-report.py</code>"]
DB[("vault_secure.<br/>document_category_configs")]
MW["CategorySizeMiddleware<br/>(existant, inchangé)"]
SVC["CategoryConfigService<br/>(existant, inchangé)"]
CACHE["Cache TTL 60s<br/>(INV-T5-03)"]
C1 -->|UPDATE max_size_bytes| DB
DB -->|lecture| CACHE
CACHE -->|resolve()| SVC
SVC -->|maxSizeBytes| MW
C2 -->|mock maxSizeBytes = 524288000| SVC
C2 -->|mock maxSizeBytes = 524288000| MW
C3 -.->|aligne doc 100→500 MB| C4
C1 -.->|valide cohérence post-migration| C4
style C1 fill:#4CAF50,color:#fff
style C2 fill:#2196F3,color:#fff
style C3 fill:#FF9800,color:#fff
style C4 fill:#9C27B0,color:#fff
style DB fill:#607D8B,color:#fff
style MW fill:#78909C,color:#fff
style SVC fill:#78909C,color:#fff
style CACHE fill:#78909C,color:#fff Séquence — Flux F1 : Migration DB + invalidation cache¶
sequenceDiagram
participant TOM as TypeORM Runner
participant DB as PostgreSQL<br/>(vault_secure)
participant SVC as CategoryConfigService
participant CACHE as Cache (TTL 60s)
participant MW as CategorySizeMiddleware
participant CLIENT as Client (upload)
Note over TOM,DB: Déploiement — Migration C1
TOM->>DB: UPDATE document_category_configs<br/>SET max_size_bytes = 524288000<br/>WHERE category IN ('DEFAULT','B2C_EVIDENCE_MINOR')
DB-->>TOM: 2 rows updated
Note over CACHE: Cache expire après ≤ 60s (INV-T5-03)
CLIENT->>MW: POST /deposit (Content-Length: 450000000)
MW->>SVC: resolve(category)
SVC->>CACHE: get(category)
CACHE-->>SVC: miss (expiré)
SVC->>DB: SELECT max_size_bytes WHERE category = 'DEFAULT'
DB-->>SVC: 524288000
SVC->>CACHE: set(category, config, TTL=60s)
SVC-->>MW: maxSizeBytes = 524288000
MW->>MW: 450000000 ≤ 524288000 → OK
MW-->>CLIENT: next() → flux nominal
Note over CLIENT,MW: Cas rejet (Content-Length > 524288000)
CLIENT->>MW: POST /deposit (Content-Length: 524288001)
MW->>SVC: resolve(category)
SVC-->>MW: maxSizeBytes = 524288000
MW->>MW: 524288001 > 524288000 → REJET
MW-->>CLIENT: 413 PayloadTooLargeException (ERR-79-004) 3. Mapping invariants → mécanismes¶
| Invariant ID | Exigence | Mécanisme | Composant | Observable | Risque |
|---|---|---|---|---|---|
| INV-285-01-coherence | Borne backend = 524288000 bytes | Migration UPDATE SQL | C1 | SELECT max_size_bytes FROM vault_secure.document_category_configs WHERE category = 'DEFAULT' retourne 524288000 | Faible — UPDATE simple |
| INV-285-02-categories-cible | Seules DEFAULT et B2C_EVIDENCE_MINOR relevées | Clause WHERE restrictive dans la migration | C1 | Query count des catégories à 524288000 = exactement 2 | Faible |
| INV-285-03-rejet-hors-borne | Taille > 524288000 rejetée | Middleware declaredSize > maxSizeBytes + Service sizeBytes > config.maxSizeBytes | Existant (non modifié) | ERR-79-004 sur requête 524288001 ; acceptation sur 524288000 | Nul — logique inchangée |
| INV-285-04-unite-et-bornes | Paramètre en bytes, bornes [1, 524288000] | Colonne BIGINT DB + parseInt service | Existant | Valeur numérique lue = 524288000 | Nul |
| INV-285-05-erreur-metier | Code ERR-79-004 pour dépassement | PayloadTooLargeException hardcodé | Existant (non modifié) | Response body contient code: 'ERR-79-004' | Nul |
| INV-285-06-non-regression | Fichiers 1..104857600 acceptés | Borne relevée (524288000 > 104857600) | Aucun (garanti mathématiquement) | Tests existants passent sans modification | Nul |
| INV-285-07-doc-sync | PD-252 contient storage.category_max_bytes: 500 MB | Édition manuelle des 2 artefacts | C3 | Grep textuel dans les 2 fichiers | Faible |
| INV-285-08-checklist-temporelle | Aucune transition temporelle | Non applicable | — | Revue de code | Nul |
| INV-285-09-etats-retour | Aucune transition retour | Non applicable (stateless) | — | Revue de code | Nul |
| INV-285-10-protection-distribuee | Aucun mécanisme distribué | Non applicable | — | Revue de code | Nul |
| INV-285-11-atomicite-ddl | Aucune migration DDL | Non applicable (UPDATE data, pas DDL) | — | Migration ne contient pas de CREATE/ALTER TABLE | Nul |
| INV-285-12-intermodule | Aucune contrainte inter-module | Non applicable | — | Diff ne touche aucun import/guard/middleware | Nul |
4. Mapping critères d'acceptation → mécanismes¶
| Critère ID | Mécanisme(s) | Composant | Observable | Risque |
|---|---|---|---|---|
| CA-01 | Migration UPDATE DEFAULT.max_size_bytes = 524288000 | C1 | TC-NOM-01 : lecture config retourne 524288000 | Faible |
| CA-02 | Migration UPDATE B2C_EVIDENCE_MINOR.max_size_bytes = 524288000 | C1 | TC-NOM-02 : lecture config retourne 524288000 | Faible |
| CA-03 | Middleware existant compare Content-Length à maxSizeBytes (désormais 524288000) | Existant | TC-NOM-03 : 524288001 rejeté ; TC-NOM-04 : 524288000 accepté | Nul |
| CA-04 | Service existant assertSizeAllowed() rejette avec ERR-79-004 | Existant | TC-NOM-05 : ERR-79-004 sur 524288001 | Nul |
| CA-05 | Mise à jour des mocks dans les tests unitaires | C2 | TC-NOM-09 : suite passante | Faible |
| CA-06 | Édition PD-252 besoin + specification | C3 | TC-NOM-07 : grep storage.category_max_bytes: 500 MB | Faible |
| CA-07 | Exécution pipeline inter-EB post-migration | C4 | TC-NOM-08 : rapport sans écart PD-101/backend | Faible |
5. Mapping tests (TC-*) → mécanismes + observables¶
| Test ID | Référence spec | Mécanisme(s) | Point(s) d'observation | Niveau de test visé |
|---|---|---|---|---|
| TC-NOM-01 | INV-285-01, CA-01 | Migration UPDATE + CategoryConfigService.resolve() | config.maxSizeBytes === 524288000 | Unit |
| TC-NOM-02 | INV-285-01, CA-02 | Migration UPDATE + CategoryConfigService.resolve() | config.maxSizeBytes === 524288000 pour B2C_EVIDENCE_MINOR | Unit |
| TC-NOM-03 | INV-285-03, CA-03 | CategorySizeMiddleware declaredSize > maxSizeBytes | PayloadTooLargeException levée | Unit |
| TC-NOM-04 | INV-285-03, CA-03 | CategorySizeMiddleware declaredSize <= maxSizeBytes | next() appelé | Unit |
| TC-NOM-05 | INV-285-03, INV-285-05, CA-04 | CategoryConfigService.assertSizeAllowed() | code === 'ERR-79-004' dans exception | Unit |
| TC-NOM-06 | INV-285-03 | CategoryConfigService.assertSizeAllowed() | Pas d'exception levée | Unit |
| TC-NOM-07 | INV-285-07, CA-06 | Diff textuel PD-252 | Chaîne storage.category_max_bytes: 500 MB présente dans les 2 fichiers | Documentation (revue manuelle) |
| TC-NOM-08 | INV-285-01, CA-07 | Pipeline coherence-report.py | Rapport sans écart PD-101/backend | Integration (script) |
| TC-NOM-09 | CA-05 | Suite tests unitaires complète | Tous tests passants | Unit |
| TC-ERR-01 | E-01, CA-03 | Middleware Content-Length > 524288000 | Rejet immédiat | Unit |
| TC-ERR-02 | E-02, CA-04 | Service taille > 524288000 | ERR-79-004 | Unit |
| TC-ERR-03 | E-03 | CategoryConfigService.resolve() avec catégorie inconnue | ERR-79-001 | Unit (existant) |
| TC-ERR-04 | E-04 | Middleware parseInt NaN check | next() passthrough | Unit (existant) |
| TC-ERR-05 | E-05, CA-06 | Revue documentaire | Grep 2 fichiers | Documentation |
| TC-ERR-06 | E-06, CA-07 | Pipeline inter-EB | Rapport de cohérence | Integration |
| TC-NR-01 | INV-285-06 | Borne 524288000 > 104857600 | Tests existants plage 1..104857600 passent | Unit |
| TC-NR-02 | INV-285-06 | assertSizeAllowed(config, 104857600) | Pas d'exception | Unit |
| TC-NEG-01 | INV-285-02 | Query DB exhaustive | Seules 2 catégories à 524288000 | Unit |
| TC-NEG-02 | INV-285-04 | assertSizeAllowed avec valeurs limites | Rejet 524288001, 0, -1 | Unit |
| TC-INV-08..12 | INV-285-08..12 | Revue de code (preuve d'absence) | Diff ne contient aucun mécanisme temporel/distribué/DDL/intermodule | Documentation |
6. Gestion des erreurs¶
| Erreur | Code | Composant | Traitement | Observable |
|---|---|---|---|---|
| Upload > 524288000 (Content-Length) | ERR-79-004 | CategorySizeMiddleware | PayloadTooLargeException, requête coupée avant Multer | HTTP 413 + code: 'ERR-79-004' |
| Upload > 524288000 (taille réelle) | ERR-79-004 | CategoryConfigService | PayloadTooLargeException après réception fichier | HTTP 413 + code: 'ERR-79-004' |
| Catégorie inconnue | ERR-79-001 | CategoryConfigService | BadRequestException | HTTP 400 (inchangé) |
| Config DB absente/inactive | ERR-79-002 | CategoryConfigService | UnprocessableEntityException | HTTP 422 (inchangé) |
Aucun nouveau code d'erreur introduit. Tous les traitements d'erreur sont existants et inchangés.
7. Impacts sécurité¶
| Risque | Mitigation | Statut |
|---|---|---|
| Augmentation surface d'abus (fichiers plus gros) | Quotas utilisateur (cumul, concurrence, journalier) inchangés — hors périmètre PD-285 | Couvert par mécanismes existants |
| DoS par upload volumineux | Middleware Content-Length pré-coupe le flux + rate limiting existant | Inchangé |
| Incohérence client/serveur | PD-101 (client) déjà à 500 MB — cette story aligne le backend | Résolu par PD-285 |
Aucune nouvelle surface d'attaque. La limite passe de 100 à 500 MB mais les garde-fous existants (quotas, rate-limit, middleware) restent actifs.
8. Hypothèses techniques¶
| ID | Hypothèse | Impact si faux |
|---|---|---|
| HT-01 | La migration peut UPDATE les lignes existantes sans conflit (pas de lock concurrent sur la table document_category_configs) | Migration échoue — relancer après vérification |
| HT-02 | Le cache CategoryConfigService (TTL 60s) expirera naturellement après migration — pas besoin de restart | Valeur stale pendant max 60s après déploiement. Acceptable car la migration s'exécute avant le trafic en déploiement blue-green |
| HT-03 | maxSizeBytes est stocké en BIGINT — 524288000 est dans la plage BIGINT | Toujours vrai (BIGINT max = 9.2×10¹⁸) |
| HT-04 | Les tests existants de middleware et service utilisent des mocks pour maxSizeBytes — la valeur du mock doit être mise à jour | Si des tests utilisent la vraie DB, ils prendront la nouvelle valeur automatiquement |
| HT-05 | PD-252 besoin et specification sont dans ProbatioVault-doc/docs/epics/legal-compliance/PD-252-politique-formats-preservation/ | Si chemin différent, localiser par find |
| HT-06 | Content-Length absent → middleware laisse passer (comportement existant ligne 25-28 de category-size.middleware.ts) — inchangé par PD-285 | Spec mentionne ce cas comme point à clarifier (Q-03), mais le comportement existant est safe : la validation métier ultérieure est l'autorité finale |
9. Points de vigilance (risques, dette, pièges)¶
-
Cache TTL 60s : Après migration, les instances en cours peuvent servir l'ancienne valeur pendant 60s max. En déploiement blue-green, les nouvelles instances démarrent avec cache vide → pas de risque.
-
Migration data vs DDL : Cette migration est un UPDATE de données, pas un changement de schéma. Pas de risque
ALTER TYPE ADD VALUE(cf. learning PD-282/PD-279). Pas besoin decommitTransaction()intermédiaire. -
Pas de rollback automatique : Conformément au pattern existant (
down()vide pour production safety), le rollback nécessiterait une migration inverse manuelle. Risque faible car le changement est réversible par un simple UPDATE. -
Multipart 5 TB inchangé :
NORMATIVE_DEFAULTS.MAX_FILE_SIZEdansupload-config.service.tsn'est PAS modifié. Attention à ne pas confondre les deux limites (catégorie vs multipart). -
Cohérence inter-EB : Le pipeline
coherence-report.pydoit être exécuté après la migration ET après l'amendement PD-252 pour valider CA-07. Ordre important.
10. Hors périmètre¶
- Changement de
NORMATIVE_DEFAULTS.MAX_FILE_SIZE(multipart, 5 TB) - Changement des quotas utilisateur (cumul, concurrence, journalier)
- Changement client mobile PD-101
- Changement d'infrastructure S3/OVH
- Changement des politiques MIME / audit mineurs hors taille
- Ajout de nouvelles catégories documentaires
- Modification du middleware ou du service (logique inchangée, seule la donnée DB change)
11. Périmètre de test¶
| Niveau de test | In scope | Hors scope (justification) |
|---|---|---|
| Unitaire | Tous les composants : migration (valeurs), mocks config service, assertions middleware et service sur borne 524288000 | — |
| Intégration | Pipeline coherence-report.py (CA-07), exécution migration sur base de test | — |
| E2E | Hors périmètre : aucun nouveau flux applicatif, seule une valeur de configuration DB change. Les tests E2E existants couvrent déjà le flux upload et restent valides | Pas de nouveau endpoint/flux à tester E2E |
Tous les niveaux de test pertinents sont couverts. Les tests E2E existants sur le flux upload restent valides sans modification (ils utilisent des fichiers < 500 MB). Aucune exclusion non justifiée.
12. Mécanismes cross-module¶
Aucune modification d'autres modules. Le changement est limité à : - Une migration SQL (données, pas de schéma) - Des tests unitaires (mocks) - De la documentation externe (PD-252)
Aucun guard, middleware ou intercepteur n'est ajouté sur des routes d'autres modules.