ObsiViewer/docs/AUDIT_CHECKLIST_AMELIORATIONS.md

14 KiB
Raw Blame History

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:
    "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:
    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:
    { 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:
    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)