14 KiB
✅ 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:
- Ajouter service Docker Meilisearch dans
docker-compose.yml
- Créer script d'indexation backend (
server/meilisearch-indexer.mjs
) - Définir schéma index (voir section Architecture)
- Créer
SearchMeilisearchService
Angular appelant/api/search
- Mapper opérateurs Obsidian → filtres Meilisearch
- 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:
npm install dompurify @types/dompurify
- Remplacer
escapeHtml()
parDOMPurify.sanitize()
dansMarkdownService
- Configurer whitelist tags/attributes Obsidian-safe
- Ajouter tests avec payloads XSS connus
- 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:
- Importer
ScrollingModule
depuis@angular/cdk/scrolling
- Wrapper liste résultats dans
<cdk-virtual-scroll-viewport>
- Définir
itemSize
fixe (80px) ou dynamique - Ajouter
trackBy
surnoteId
pour optimiser change detection - 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:
- Créer
markdown.worker.ts
avec MarkdownIt + plugins - Exposer API
parse(markdown: string, options) => html
- Créer
MarkdownWorkerService
Angular avec pool de workers (2-4) - Gérer communication async (Observable-based)
- Ajouter fallback synchrone pour SSR
- 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:
- Remplacer effect par
debounceTime(300)
surallNotes()
signal - Implémenter rebuild incrémental: détecter delta notes (added/updated/removed)
- Update index partiellement pour delta uniquement
- Ajouter flag
isIndexing
pour désactiver search pendant rebuild - 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:
- Convertir imports statiques en
import()
dynamiques - Dans
MarkdownService.renderFence()
, charger mermaid on-demand - Détecter présence
$$
avant charger MathJax - Wrapper imports dans
NgZone.runOutsideAngular()
pour éviter CD - 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:
- Créer route POST
/api/log
dansserver/index.mjs
- Parser batch d'événements (schéma LogRecord)
- Valider/sanitize données (éviter injection logs)
- Persister dans fichier JSON rotatif (max 10MB) ou stdout structuré
- Ajouter corrélation sessionId + requestId
- 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:
- Installer
@angular/service-worker
- 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
- Ajouter
ServiceWorkerModule.register()
dans app config - 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:
- Ajouter section
budgets
dansangular.json
:"budgets": [ { "type": "initial", "maximumWarning": "1.5mb", "maximumError": "2mb" }, { "type": "anyComponentStyle", "maximumWarning": "50kb" } ]
- Configurer CI pour fail si budgets dépassés
- 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:
- Stage 1 (builder):
FROM node:20-alpine
, build Angular + prune devDeps - Stage 2 (runtime):
FROM node:20-alpine
, copier dist + node_modules prod uniquement - Utiliser
.dockerignore
(node_modules, .git, .angular, tests) - Ajouter HEALTHCHECK curl
/api/health
- 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:
- Créer
.env.example
:VAULT_PATH=/app/vault VAULT_ID=primary MEILISEARCH_URL=http://meilisearch:7700 MEILISEARCH_KEY=masterKey LOG_LEVEL=info
- Charger avec
dotenv
dansserver/index.mjs
- Exposer config runtime via
/api/config
(non-sensitive seulement) - 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:
- Créer
docker/config/nginx.conf
avec: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;
- Tester avec CSP reporter
- Activer Brotli compression
- 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:
- Ajouter flag
isScheduled
dansGraphCanvasComponent
- Wrapper
scheduleRedraw()
pour éviter multiples RAF pending - Utiliser
requestAnimationFrame()
comme gate (déjà présent mais pas optimal) - 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:
- Créer
e2e/search-performance.spec.ts
:- Charger vault 500 notes
- Mesurer temps search <150ms
- Vérifier pas de freeze main thread >100ms
- Créer
e2e/graph-interaction.spec.ts
:- Cliquer node graph
- Mesurer temps avant sélection <100ms
- Ajouter fixtures vault de test (small/medium/large)
- 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:
- Créer routes avec
loadComponent
:{ path: 'graph', loadComponent: () => import('./graph/...') }
- Séparer features: graph, calendar, bookmarks en chunks
- Preload strategy:
PreloadAllModules
ou custom - 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:
- Comparer hash du tableau notes (shallow equality)
- Ajouter cache Map<notesHash, GraphData>
- Retourner cached si hash identique
- 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:
- Configurer
allowedAttributes
whitelist stricte:allowedAttributes: ['id', 'class'], allowedClasses: ['callout', 'md-*', 'hljs-*']
- Tester avec payloads injection
- 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:
- Wrapper liste dans composant custom avec rendering batched
- Utiliser
requestIdleCallback
pour render par chunks - 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:
- Créer
VaultCacheService
avec Dexie.js - Persister notes + timestamps dans IDB
/api/vault?since=<timestamp>
pour delta uniquement- 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:
- Installer
@opentelemetry/sdk-node
- Instrumenter Express avec auto-instrumentation
- Exporter traces vers Jaeger/Zipkin
- 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)