81 lines
7.0 KiB
Markdown
81 lines
7.0 KiB
Markdown
# 🔍 AUDIT STAFF ENGINEER — ObsiViewer
|
||
**Date:** 6 octobre 2025
|
||
**Auditeur:** Staff Engineer (Frontend Angular 20 + Node/DevOps)
|
||
**Scope:** Architecture, Performance, Sécurité, DX, Ops
|
||
|
||
---
|
||
|
||
## A) SYNTHÈSE EXÉCUTIVE (≤300 mots)
|
||
|
||
### État actuel
|
||
ObsiViewer est une application Angular 20 standalone, bien structurée mais souffrant de **problèmes critiques de performance** et d'absence d'architecture de recherche scalable. Le graph view utilise d3-force dans un Web Worker (bon), mais la recherche frontend synchrone bloque le thread principal.
|
||
|
||
### 🔴 Faiblesses majeures identifiées
|
||
|
||
1. **Recherche synchrone bloquante (P0)** — `SearchOrchestratorService.execute()` itère sur tous les contextes dans le main thread, gel UI sur voûtes >500 notes. Impact: UX critique.
|
||
|
||
2. **Pas de virtualisation des résultats (P0)** — Liste de résultats rendue intégralement dans le DOM, causant CLS et ralentissement sur 200+ résultats. Impact: performance.
|
||
|
||
3. **Parsing Markdown synchrone (P0)** — `MarkdownService.render()` bloque sur mermaid/highlight.js/MathJax, freeze de 500ms+ sur notes complexes. Impact: UX critique.
|
||
|
||
4. **Pas de sanitization XSS (P0 Sécurité)** — HTML brut rendu sans DOMPurify, vulnérabilité sur Markdown malveillant. Impact: sécurité critique.
|
||
|
||
5. **Indexation reconstruite à chaque effet (P0)** — `SearchIndexService.rebuildIndex()` et `GraphIndexService.rebuildIndex()` déclenchés sur mutation du signal `allNotes()`, coût O(N²). Impact: performance.
|
||
|
||
6. **Pas de lazy loading des bibliothèques lourdes (P1)** — Mermaid (1.2MB) et highlight.js chargés au démarrage, TTI >4s. Impact: chargement initial.
|
||
|
||
7. **Aucune stratégie de cache HTTP (P1)** — Pas d'ETag, Service Worker, ou cache IndexedDB pour les métadonnées. Impact: rechargements inutiles.
|
||
|
||
8. **Logs non structurés backend (P1)** — `/api/log` inexistant, pas de corrélation des événements, diagnostic impossible. Impact: observabilité.
|
||
|
||
### Opportunités majeures
|
||
- **Meilisearch** pour recherche côté serveur (typo-tolerance, highlights, facettes)
|
||
- **CDK Virtual Scroll** pour listes (réduction DOM de 95%)
|
||
- **Web Workers** pour parsing Markdown et indexation
|
||
- **Docker multi-stage** pour optimisation déploiement
|
||
|
||
### Métriques actuelles estimées
|
||
- **TTI:** ~4.2s (budget: <2.5s)
|
||
- **Search P95:** 800ms+ sur 500 notes (budget: <150ms)
|
||
- **Graph freeze:** 1.5s+ au clic (budget: <100ms)
|
||
- **Bundle size:** ~2.8MB (budget: <1.5MB)
|
||
|
||
---
|
||
|
||
## B) TABLEAU DES FAIBLESSES DÉTAILLÉES
|
||
|
||
| Catégorie | Fichier/Zone | Description | Symptôme | Cause racine | Evidence | Risque | Priorité |
|
||
|-----------|--------------|-------------|----------|--------------|----------|--------|----------|
|
||
| **Performance** | `search-orchestrator.service.ts:164-200` | Boucle synchrone sur tous les contextes | Gel UI 800ms+ sur 500 notes | Itération bloquante dans main thread | L.164 `for (const context of allContexts)` | Abandon utilisateur | **P0** |
|
||
| **Performance** | `app.component.ts:232-251` | Résultats de recherche non virtualisés | CLS, scroll janky sur 200+ résultats | Rendu DOM complet de la liste | Computed signal sans CDK Virtual Scroll | UX dégradée | **P0** |
|
||
| **Performance** | `markdown.service.ts:53-92` | Parsing Markdown synchrone | Freeze 500ms+ sur notes avec mermaid | `mermaid.render()` et `hljs.highlight()` dans main thread | L.53 render() bloquant | UX critique | **P0** |
|
||
| **Sécurité** | `markdown.service.ts:564-571` | Pas de sanitization HTML | Vulnérabilité XSS via Markdown malveillant | `escapeHtml()` custom au lieu de DOMPurify | Aucune lib sanitization trouvée | **CVE potentielle** | **P0** |
|
||
| **Performance** | `search-index.service.ts:80-146` | Rebuild index complet à chaque mutation | CPU spike 300ms+ sur edit | Effect déclenché sur `allNotes()` mutation | L.310 `effect(() => this.searchIndex.rebuildIndex())` | Gel éditeur | **P0** |
|
||
| **Performance** | `graph-index.service.ts` + `app.component.ts:304-307` | Rebuild graph index à chaque mutation | Calcul redondant O(N²) | Même pattern que search index | L.304 effect sans debounce | Gel éditeur | **P0** |
|
||
| **Architecture** | Pas de backend Meilisearch | Recherche frontend limitée | Pas de typo-tolerance, highlights serveur | Absence d'engine de recherche dédié | Opérateurs Obsidian mappés côté client | Scalabilité bloquée | **P0** |
|
||
| **Performance** | `package.json:44` | Mermaid chargé au démarrage (1.2MB) | TTI >4s | Import statique au lieu de lazy | L.44 dépendance non lazy | Slow initial load | **P1** |
|
||
| **Performance** | Aucun Virtual Scroll (CDK) | Liste tags, résultats non optimisées | Scroll janky sur 500+ items | Angular CDK non utilisé | `@angular/cdk` présent mais inutilisé | UX liste | **P1** |
|
||
| **Performance** | Pas de Service Worker | Rechargement complet à chaque visite | Pas de cache offline | Workbox non configuré | Aucun `ngsw-config.json` | Expérience offline nulle | **P1** |
|
||
| **Ops** | `Dockerfile:1-56` | Pas de multi-stage optimisé | Image 450MB+ | node:20-bullseye-slim pas assez slim | L.21 runtime inclut build tools | Déploiement lent | **P1** |
|
||
| **Ops** | Aucune variable d'env structurée | Config hardcodée | Pas de VAULT_ID, SEARCH_URL | `assets/config.local.js` non paramétré | Impossible multi-instance | **P1** |
|
||
| **DX** | Pas de budgets Lighthouse | Pas de garde-fou performance | Régressions non détectées | `angular.json` sans budgets | Aucun `budgets` configuré | Dégradation continue | **P1** |
|
||
| **Sécurité** | Aucun CSP header | XSS non mitigé | Pas de Content-Security-Policy | NGINX config absente | `nginx.conf` minimal | Défense en profondeur manquante | **P1** |
|
||
| **Observabilité** | `/api/log` non implémenté | Diagnostics impossibles | Pas de corrélation événements | Backend Express minimal | `server/index.mjs` sans routes log | Debug production impossible | **P1** |
|
||
| **Performance** | `vault.service.ts:96-142` | Graph data recalculé trop souvent | Computed sans memoization fine | Computed signal redéclenché | L.96-142 calcul O(N×M) | CPU gaspillé | **P2** |
|
||
| **Architecture** | Pas de lazy routes | Tout chargé au boot | TTI impacté par code inutile | Application standalone sans routing lazy | Pas de loadChildren | Bundle monolithique | **P2** |
|
||
| **DX** | Tests E2E partiels | Couverture <30% estimée | Pas de tests graph freeze | Playwright configuré mais incomplet | `e2e/*.spec.ts` limités | Régressions non catchées | **P2** |
|
||
| **Performance** | `graph-canvas.component.ts:373-403` | Redessins canvas non throttlés | GPU surchargé | `draw()` appelé à chaque tick sans RAF guard | L.364 scheduleRedraw sans throttle | Batterie mobile | **P2** |
|
||
| **Sécurité** | Markdown attrs non validés | Injection potentielle via `{.class}` | markdown-it-attrs sans whitelist | L.157 allowedAttributes minimal | XSS edge case | **P2** |
|
||
|
||
---
|
||
|
||
**Points forts à préserver:**
|
||
- ✅ Web Worker pour graph layout (d3-force offload)
|
||
- ✅ OnPush change detection (appliqué partout)
|
||
- ✅ Signals/Computed pour réactivité
|
||
- ✅ Architecture modulaire services/components
|
||
- ✅ Logging client structuré (base solide)
|
||
|
||
---
|
||
|