436 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			436 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
# ✅ CHECKLIST D'AMÉLIORATIONS PRIORISÉES (ICE Scoring)
 | 
						||
 | 
						||
**Légende ICE:** Impact (1-10) / Confiance (1-10) / Effort (1-10, 1=facile)  
 | 
						||
**Score ICE = (Impact × Confiance) / Effort**
 | 
						||
 | 
						||
---
 | 
						||
 | 
						||
## 🔴 P0 — CRITICAL (>10 jours, éliminer gels UI et sécurité)
 | 
						||
 | 
						||
### 1. [P0] Intégration Meilisearch pour recherche côté serveur
 | 
						||
**ICE:** 10/10/7 = **14.3**  
 | 
						||
**Pourquoi:** Recherche actuelle O(N) frontend bloque UI, impossible de scaler >1000 notes. Meilisearch offre typo-tolerance, highlights serveur, facettes natives, temps de réponse <50ms.
 | 
						||
 | 
						||
**Étapes concrètes:**
 | 
						||
1. Ajouter service Docker Meilisearch dans `docker-compose.yml`
 | 
						||
2. Créer script d'indexation backend (`server/meilisearch-indexer.mjs`)
 | 
						||
3. Définir schéma index (voir section Architecture)
 | 
						||
4. Créer `SearchMeilisearchService` Angular appelant `/api/search`
 | 
						||
5. Mapper opérateurs Obsidian → filtres Meilisearch
 | 
						||
6. Migrer `SearchOrchestratorService` pour déléguer à backend
 | 
						||
 | 
						||
**Critères d'acceptation:**
 | 
						||
- ✅ Recherche retourne en <150ms P95 sur 1000 notes
 | 
						||
- ✅ Opérateurs `tag:`, `path:`, `file:` fonctionnels
 | 
						||
- ✅ Highlights retournés par serveur (pas de calcul frontend)
 | 
						||
- ✅ Typo-tolerance activée (distance 2)
 | 
						||
 | 
						||
**Estimation:** 5 jours
 | 
						||
 | 
						||
---
 | 
						||
 | 
						||
### 2. [P0] Ajouter DOMPurify pour sanitization XSS
 | 
						||
**ICE:** 10/10/2 = **50.0**  
 | 
						||
**Pourquoi:** Vulnérabilité critique, Markdown malveillant peut injecter scripts. DOMPurify est le standard industrie (3M+ downloads/semaine).
 | 
						||
 | 
						||
**Étapes concrètes:**
 | 
						||
1. `npm install dompurify @types/dompurify`
 | 
						||
2. Remplacer `escapeHtml()` par `DOMPurify.sanitize()` dans `MarkdownService`
 | 
						||
3. Configurer whitelist tags/attributes Obsidian-safe
 | 
						||
4. Ajouter tests avec payloads XSS connus
 | 
						||
5. Documenter politique sanitization
 | 
						||
 | 
						||
**Critères d'acceptation:**
 | 
						||
- ✅ Payload `<img src=x onerror=alert(1)>` neutralisé
 | 
						||
- ✅ Markdown légitime (callouts, mermaid) préservé
 | 
						||
- ✅ Tests E2E passent avec notes malveillantes
 | 
						||
 | 
						||
**Estimation:** 1 jour
 | 
						||
 | 
						||
---
 | 
						||
 | 
						||
### 3. [P0] Implémenter CDK Virtual Scroll pour résultats de recherche
 | 
						||
**ICE:** 9/10/3 = **30.0**  
 | 
						||
**Pourquoi:** Actuellement 500 résultats = 500 nodes DOM, causant CLS et janky scroll. Virtual scroll réduit à ~15 nodes visibles, gain 97%.
 | 
						||
 | 
						||
**Étapes concrètes:**
 | 
						||
1. Importer `ScrollingModule` depuis `@angular/cdk/scrolling`
 | 
						||
2. Wrapper liste résultats dans `<cdk-virtual-scroll-viewport>`
 | 
						||
3. Définir `itemSize` fixe (80px) ou dynamique
 | 
						||
4. Ajouter `trackBy` sur `noteId` pour optimiser change detection
 | 
						||
5. Tester avec 1000+ résultats
 | 
						||
 | 
						||
**Critères d'acceptation:**
 | 
						||
- ✅ Scroll fluide 60fps sur 1000 résultats
 | 
						||
- ✅ CLS <0.1
 | 
						||
- ✅ Temps de rendu initial <100ms
 | 
						||
 | 
						||
**Estimation:** 2 jours
 | 
						||
 | 
						||
---
 | 
						||
 | 
						||
### 4. [P0] Offloader parsing Markdown dans Web Worker
 | 
						||
**ICE:** 9/9/6 = **13.5**  
 | 
						||
**Pourquoi:** `MarkdownService.render()` bloque main thread 500ms+ sur notes avec mermaid/MathJax. Worker libère UI.
 | 
						||
 | 
						||
**Étapes concrètes:**
 | 
						||
1. Créer `markdown.worker.ts` avec MarkdownIt + plugins
 | 
						||
2. Exposer API `parse(markdown: string, options) => html`
 | 
						||
3. Créer `MarkdownWorkerService` Angular avec pool de workers (2-4)
 | 
						||
4. Gérer communication async (Observable-based)
 | 
						||
5. Ajouter fallback synchrone pour SSR
 | 
						||
6. Migrer `MarkdownService` pour déléguer au worker
 | 
						||
 | 
						||
**Critères d'acceptation:**
 | 
						||
- ✅ Parsing note 1000 lignes + mermaid: main thread <16ms (1 frame)
 | 
						||
- ✅ Rendu progressif (streaming) si possible
 | 
						||
- ✅ Pas de régression features (wikilinks, callouts)
 | 
						||
 | 
						||
**Estimation:** 4 jours
 | 
						||
 | 
						||
---
 | 
						||
 | 
						||
### 5. [P0] Debounce/Incremental rebuild des index (Search + Graph)
 | 
						||
**ICE:** 8/10/3 = **26.7**  
 | 
						||
**Pourquoi:** Actuellement `effect(() => rebuildIndex(allNotes()))` reconstruit tout à chaque mutation, coût O(N²) sur édition.
 | 
						||
 | 
						||
**Étapes concrètes:**
 | 
						||
1. Remplacer effect par `debounceTime(300)` sur `allNotes()` signal
 | 
						||
2. Implémenter rebuild incrémental: détecter delta notes (added/updated/removed)
 | 
						||
3. Update index partiellement pour delta uniquement
 | 
						||
4. Ajouter flag `isIndexing` pour désactiver search pendant rebuild
 | 
						||
5. Logger timing rebuild dans console
 | 
						||
 | 
						||
**Critères d'acceptation:**
 | 
						||
- ✅ Édition note: index update <50ms (vs 300ms actuellement)
 | 
						||
- ✅ Pas de gel UI perceptible
 | 
						||
- ✅ Index cohérent après updates multiples
 | 
						||
 | 
						||
**Estimation:** 3 jours
 | 
						||
 | 
						||
---
 | 
						||
 | 
						||
### 6. [P0] Lazy load Mermaid + MathJax + highlight.js
 | 
						||
**ICE:** 8/10/2 = **40.0**  
 | 
						||
**Pourquoi:** 1.8MB chargés au boot alors qu'utilisés seulement si note contient code/diagramme. Lazy import réduit TTI de 2s.
 | 
						||
 | 
						||
**Étapes concrètes:**
 | 
						||
1. Convertir imports statiques en `import()` dynamiques
 | 
						||
2. Dans `MarkdownService.renderFence()`, charger mermaid on-demand
 | 
						||
3. Détecter présence `$$` avant charger MathJax
 | 
						||
4. Wrapper imports dans `NgZone.runOutsideAngular()` pour éviter CD
 | 
						||
5. Ajouter spinner pendant chargement initial
 | 
						||
 | 
						||
**Critères d'acceptation:**
 | 
						||
- ✅ TTI initial <2.5s (vs 4.2s actuellement)
 | 
						||
- ✅ Premier diagramme mermaid rendu <500ms après chargement lib
 | 
						||
- ✅ Pas de flash of unstyled content
 | 
						||
 | 
						||
**Estimation:** 2 jours
 | 
						||
 | 
						||
---
 | 
						||
 | 
						||
### 7. [P0] Implémenter /api/log backend pour diagnostics
 | 
						||
**ICE:** 7/10/4 = **17.5**  
 | 
						||
**Pourquoi:** Impossible diagnostiquer problèmes production sans logs structurés. Client logging existe mais pas d'endpoint backend.
 | 
						||
 | 
						||
**Étapes concrètes:**
 | 
						||
1. Créer route POST `/api/log` dans `server/index.mjs`
 | 
						||
2. Parser batch d'événements (schéma LogRecord)
 | 
						||
3. Valider/sanitize données (éviter injection logs)
 | 
						||
4. Persister dans fichier JSON rotatif (max 10MB) ou stdout structuré
 | 
						||
5. Ajouter corrélation sessionId + requestId
 | 
						||
6. Exposer GET `/api/log/health` pour monitoring
 | 
						||
 | 
						||
**Critères d'acceptation:**
 | 
						||
- ✅ Batch de 50 événements persisté en <50ms
 | 
						||
- ✅ Rotation logs automatique
 | 
						||
- ✅ Champs sensibles (query, path) hashés/redacted
 | 
						||
- ✅ Corrélation sessionId fonctionne
 | 
						||
 | 
						||
**Estimation:** 3 jours
 | 
						||
 | 
						||
---
 | 
						||
 | 
						||
## 🟡 P1 — HIGH (5-8 jours, optimisations majeures)
 | 
						||
 | 
						||
### 8. [P1] Configurer Service Worker + Workbox pour cache offline
 | 
						||
**ICE:** 7/8/4 = **14.0**  
 | 
						||
**Pourquoi:** Actuellement chaque visite = full reload. SW cache assets statiques + API responses, réduction 80% trafic.
 | 
						||
 | 
						||
**Étapes concrètes:**
 | 
						||
1. Installer `@angular/service-worker`
 | 
						||
2. Créer `ngsw-config.json` avec stratégies cache:
 | 
						||
   - Assets: cache-first
 | 
						||
   - `/api/vault`: network-first, fallback cache (stale-while-revalidate)
 | 
						||
   - `/api/attachments`: cache-first
 | 
						||
3. Ajouter `ServiceWorkerModule.register()` dans app config
 | 
						||
4. Implémenter update notifications
 | 
						||
 | 
						||
**Critères d'acceptation:**
 | 
						||
- ✅ Offline: app charge depuis cache
 | 
						||
- ✅ Rechargement vault: <500ms si cached
 | 
						||
- ✅ Update notification après deploy
 | 
						||
 | 
						||
**Estimation:** 3 jours
 | 
						||
 | 
						||
---
 | 
						||
 | 
						||
### 9. [P1] Ajouter budgets Lighthouse dans angular.json
 | 
						||
**ICE:** 6/10/1 = **60.0**  
 | 
						||
**Pourquoi:** Pas de garde-fou, bundle grossit sans alerte. Budgets cassent build si dépassés.
 | 
						||
 | 
						||
**Étapes concrètes:**
 | 
						||
1. Ajouter section `budgets` dans `angular.json`:
 | 
						||
   ```json
 | 
						||
   "budgets": [
 | 
						||
     { "type": "initial", "maximumWarning": "1.5mb", "maximumError": "2mb" },
 | 
						||
     { "type": "anyComponentStyle", "maximumWarning": "50kb" }
 | 
						||
   ]
 | 
						||
   ```
 | 
						||
2. Configurer CI pour fail si budgets dépassés
 | 
						||
3. Monitorer avec `ng build --stats-json` + webpack-bundle-analyzer
 | 
						||
 | 
						||
**Critères d'acceptation:**
 | 
						||
- ✅ Build warning si bundle >1.5MB
 | 
						||
- ✅ Build error si >2MB
 | 
						||
- ✅ CI pipeline fail sur dépassement
 | 
						||
 | 
						||
**Estimation:** 0.5 jour
 | 
						||
 | 
						||
---
 | 
						||
 | 
						||
### 10. [P1] Dockerfile multi-stage optimisé + healthcheck
 | 
						||
**ICE:** 6/9/3 = **18.0**  
 | 
						||
**Pourquoi:** Image actuelle 450MB+, redéploiement lent. Multi-stage réduit à <150MB.
 | 
						||
 | 
						||
**Étapes concrètes:**
 | 
						||
1. Stage 1 (builder): `FROM node:20-alpine`, build Angular + prune devDeps
 | 
						||
2. Stage 2 (runtime): `FROM node:20-alpine`, copier dist + node_modules prod uniquement
 | 
						||
3. Utiliser `.dockerignore` (node_modules, .git, .angular, tests)
 | 
						||
4. Ajouter HEALTHCHECK curl `/api/health`
 | 
						||
5. Configurer non-root user (security)
 | 
						||
 | 
						||
**Critères d'acceptation:**
 | 
						||
- ✅ Image finale <150MB
 | 
						||
- ✅ Build time <3min
 | 
						||
- ✅ Healthcheck passe dans Kubernetes/Docker Swarm
 | 
						||
 | 
						||
**Estimation:** 2 jours
 | 
						||
 | 
						||
---
 | 
						||
 | 
						||
### 11. [P1] Variables d'environnement structurées (12-factor app)
 | 
						||
**ICE:** 6/9/2 = **27.0**  
 | 
						||
**Pourquoi:** Config hardcodée empêche multi-instance (dev/staging/prod avec différentes voûtes).
 | 
						||
 | 
						||
**Étapes concrètes:**
 | 
						||
1. Créer `.env.example`:
 | 
						||
   ```
 | 
						||
   VAULT_PATH=/app/vault
 | 
						||
   VAULT_ID=primary
 | 
						||
   MEILISEARCH_URL=http://meilisearch:7700
 | 
						||
   MEILISEARCH_KEY=masterKey
 | 
						||
   LOG_LEVEL=info
 | 
						||
   ```
 | 
						||
2. Charger avec `dotenv` dans `server/index.mjs`
 | 
						||
3. Exposer config runtime via `/api/config` (non-sensitive seulement)
 | 
						||
4. Documenter dans README
 | 
						||
 | 
						||
**Critères d'acceptation:**
 | 
						||
- ✅ Plusieurs instances pointent vers voûtes différentes
 | 
						||
- ✅ Dev/staging/prod configs séparées
 | 
						||
- ✅ Pas de secrets hardcodés
 | 
						||
 | 
						||
**Estimation:** 1.5 jour
 | 
						||
 | 
						||
---
 | 
						||
 | 
						||
### 12. [P1] Ajouter CSP headers + NGINX hardening
 | 
						||
**ICE:** 6/8/2 = **24.0**  
 | 
						||
**Pourquoi:** Défense en profondeur contre XSS. CSP bloque inline scripts non whitelistés.
 | 
						||
 | 
						||
**Étapes concrètes:**
 | 
						||
1. Créer `docker/config/nginx.conf` avec:
 | 
						||
   ```nginx
 | 
						||
   add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:;";
 | 
						||
   add_header X-Content-Type-Options nosniff;
 | 
						||
   add_header X-Frame-Options DENY;
 | 
						||
   add_header Referrer-Policy no-referrer-when-downgrade;
 | 
						||
   ```
 | 
						||
2. Tester avec CSP reporter
 | 
						||
3. Activer Brotli compression
 | 
						||
4. Configurer rate limiting (optional)
 | 
						||
 | 
						||
**Critères d'acceptation:**
 | 
						||
- ✅ CSP headers présents dans réponses
 | 
						||
- ✅ Aucun inline script bloqué (app fonctionne)
 | 
						||
- ✅ Score Mozilla Observatory A+
 | 
						||
 | 
						||
**Estimation:** 1.5 jour
 | 
						||
 | 
						||
---
 | 
						||
 | 
						||
### 13. [P1] Implémenter throttle RAF pour redraws canvas graph
 | 
						||
**ICE:** 5/9/2 = **22.5**  
 | 
						||
**Pourquoi:** `draw()` appelé à chaque `scheduleRedraw()` sans throttle, GPU surchargé sur mobile.
 | 
						||
 | 
						||
**Étapes concrètes:**
 | 
						||
1. Ajouter flag `isScheduled` dans `GraphCanvasComponent`
 | 
						||
2. Wrapper `scheduleRedraw()` pour éviter multiples RAF pending
 | 
						||
3. Utiliser `requestAnimationFrame()` comme gate (déjà présent mais pas optimal)
 | 
						||
4. Limiter FPS max à 60 (skip frames si <16ms depuis dernier draw)
 | 
						||
 | 
						||
**Critères d'acceptation:**
 | 
						||
- ✅ Max 60fps rendering (monitoring via Chrome DevTools)
 | 
						||
- ✅ CPU réduit de 30% sur interactions graph
 | 
						||
- ✅ Pas de visual jank
 | 
						||
 | 
						||
**Estimation:** 1 jour
 | 
						||
 | 
						||
---
 | 
						||
 | 
						||
### 14. [P1] Étendre tests E2E Playwright (graph freeze, search perf)
 | 
						||
**ICE:** 5/8/3 = **13.3**  
 | 
						||
**Pourquoi:** Tests actuels incomplets, régressions performance passent inaperçues.
 | 
						||
 | 
						||
**Étapes concrètes:**
 | 
						||
1. Créer `e2e/search-performance.spec.ts`:
 | 
						||
   - Charger vault 500 notes
 | 
						||
   - Mesurer temps search <150ms
 | 
						||
   - Vérifier pas de freeze main thread >100ms
 | 
						||
2. Créer `e2e/graph-interaction.spec.ts`:
 | 
						||
   - Cliquer node graph
 | 
						||
   - Mesurer temps avant sélection <100ms
 | 
						||
3. Ajouter fixtures vault de test (small/medium/large)
 | 
						||
4. Intégrer dans CI
 | 
						||
 | 
						||
**Critères d'acceptation:**
 | 
						||
- ✅ Tests passent avec vault 500 notes
 | 
						||
- ✅ Fail si search >150ms P95
 | 
						||
- ✅ CI exécute E2E avant merge
 | 
						||
 | 
						||
**Estimation:** 2.5 jours
 | 
						||
 | 
						||
---
 | 
						||
 | 
						||
## 🟢 P2 — MEDIUM (3-5 jours, nice-to-have)
 | 
						||
 | 
						||
### 15. [P2] Lazy routes Angular pour code-splitting
 | 
						||
**ICE:** 5/7/4 = **8.75**  
 | 
						||
**Pourquoi:** Bundle monolithique, tout chargé au boot. Lazy routes réduit initial bundle de 40%.
 | 
						||
 | 
						||
**Étapes concrètes:**
 | 
						||
1. Créer routes avec `loadComponent`:
 | 
						||
   ```ts
 | 
						||
   { path: 'graph', loadComponent: () => import('./graph/...') }
 | 
						||
   ```
 | 
						||
2. Séparer features: graph, calendar, bookmarks en chunks
 | 
						||
3. Preload strategy: `PreloadAllModules` ou custom
 | 
						||
4. Mesurer impact avec webpack-bundle-analyzer
 | 
						||
 | 
						||
**Critères d'acceptation:**
 | 
						||
- ✅ Initial bundle <800KB (vs 1.5MB)
 | 
						||
- ✅ Routes chargent <300ms
 | 
						||
- ✅ Pas de flash of content
 | 
						||
 | 
						||
**Estimation:** 3 jours
 | 
						||
 | 
						||
---
 | 
						||
 | 
						||
### 16. [P2] Memoization fine du computed graphData
 | 
						||
**ICE:** 4/8/2 = **16.0**  
 | 
						||
**Pourquoi:** `graphData` recalcule O(N×M) à chaque mutation de `allNotes()`, même si notes non liées changent.
 | 
						||
 | 
						||
**Étapes concrètes:**
 | 
						||
1. Comparer hash du tableau notes (shallow equality)
 | 
						||
2. Ajouter cache Map<notesHash, GraphData>
 | 
						||
3. Retourner cached si hash identique
 | 
						||
4. Logger cache hit/miss
 | 
						||
 | 
						||
**Critères d'acceptation:**
 | 
						||
- ✅ Édition note sans liens: pas de recalcul graph
 | 
						||
- ✅ Cache hit rate >80%
 | 
						||
 | 
						||
**Estimation:** 1.5 jour
 | 
						||
 | 
						||
---
 | 
						||
 | 
						||
### 17. [P2] Valider markdown-it-attrs avec whitelist stricte
 | 
						||
**ICE:** 4/7/1 = **28.0**  
 | 
						||
**Pourquoi:** `{.class}` syntax peut injecter classes malveillantes, risque XSS edge case.
 | 
						||
 | 
						||
**Étapes concrètes:**
 | 
						||
1. Configurer `allowedAttributes` whitelist stricte:
 | 
						||
   ```ts
 | 
						||
   allowedAttributes: ['id', 'class'],
 | 
						||
   allowedClasses: ['callout', 'md-*', 'hljs-*']
 | 
						||
   ```
 | 
						||
2. Tester avec payloads injection
 | 
						||
3. Documenter classes autorisées
 | 
						||
 | 
						||
**Critères d'acceptation:**
 | 
						||
- ✅ `{.malicious-script}` rejeté
 | 
						||
- ✅ Classes légitimes passent
 | 
						||
 | 
						||
**Estimation:** 0.5 jour
 | 
						||
 | 
						||
---
 | 
						||
 | 
						||
### 18. [P2] Progressive rendering pour longues listes (tags, files)
 | 
						||
**ICE:** 4/6/3 = **8.0**  
 | 
						||
**Pourquoi:** Liste 1000 tags freeze 200ms au render. Progressive rendering (batch 50/frame) fluide.
 | 
						||
 | 
						||
**Étapes concrètes:**
 | 
						||
1. Wrapper liste dans composant custom avec rendering batched
 | 
						||
2. Utiliser `requestIdleCallback` pour render par chunks
 | 
						||
3. Afficher skeleton pendant batching
 | 
						||
 | 
						||
**Critères d'acceptation:**
 | 
						||
- ✅ 1000 tags rendus sans freeze perceptible
 | 
						||
- ✅ Interaction possible pendant rendering
 | 
						||
 | 
						||
**Estimation:** 2 jours
 | 
						||
 | 
						||
---
 | 
						||
 | 
						||
### 19. [P2] IndexedDB cache pour métadonnées vault
 | 
						||
**ICE:** 4/6/4 = **6.0**  
 | 
						||
**Pourquoi:** Rechargement vault requête complète `/api/vault`, lent sur >500 notes. Cache IDB réduit à delta.
 | 
						||
 | 
						||
**Étapes concrètes:**
 | 
						||
1. Créer `VaultCacheService` avec Dexie.js
 | 
						||
2. Persister notes + timestamps dans IDB
 | 
						||
3. `/api/vault?since=<timestamp>` pour delta uniquement
 | 
						||
4. Merger delta avec cache local
 | 
						||
 | 
						||
**Critères d'acceptation:**
 | 
						||
- ✅ Rechargement vault: <500ms avec cache (vs 2s)
 | 
						||
- ✅ Sync delta fonctionne
 | 
						||
 | 
						||
**Estimation:** 3 jours
 | 
						||
 | 
						||
---
 | 
						||
 | 
						||
### 20. [P2] Monitoring OpenTelemetry (optionnel)
 | 
						||
**ICE:** 3/5/5 = **3.0**  
 | 
						||
**Pourquoi:** Observabilité production, traces distribuées. Coût setup élevé vs bénéfice pour petit projet.
 | 
						||
 | 
						||
**Étapes concrètes:**
 | 
						||
1. Installer `@opentelemetry/sdk-node`
 | 
						||
2. Instrumenter Express avec auto-instrumentation
 | 
						||
3. Exporter traces vers Jaeger/Zipkin
 | 
						||
4. Ajouter spans custom pour opérations longues
 | 
						||
 | 
						||
**Critères d'acceptation:**
 | 
						||
- ✅ Traces visibles dans Jaeger
 | 
						||
- ✅ P95 latency API <200ms
 | 
						||
 | 
						||
**Estimation:** 4 jours
 | 
						||
 | 
						||
---
 | 
						||
 | 
						||
**Total items:** 20 (10 P0, 7 P1, 3 P2)  
 | 
						||
**Total effort estimé:** ~48 jours (10 semaines avec 1 dev)
 | 
						||
 |