ObsiViewer/docs/AUDIT_PLAN_EXECUTION.md

552 lines
15 KiB
Markdown

# 🚀 PLAN D'EXÉCUTION & MÉTRIQUES
## 1. RÉSUMÉ EXÉCUTABLE — Ordre d'Attaque des P0 (Semaine 1)
### Jour 1: Sécurité Critique
**Objectif:** Éliminer vulnérabilité XSS
**Tasks:**
- [ ] Installer DOMPurify: `npm install dompurify @types/dompurify`
- [ ] Remplacer `escapeHtml()` par `DOMPurify.sanitize()` dans `MarkdownService`
- [ ] Configurer whitelist: `ALLOWED_TAGS`, `ALLOWED_ATTR`
- [ ] Tests avec payloads XSS (OWASP Top 10)
- [ ] Commit + merge
**Critère de succès:** Payload `<img src=x onerror=alert(1)>` neutralisé
---
### Jour 2-3: Performance UI Immédiate
**Objectif:** Éliminer gels perceptibles
**Tasks:**
- [ ] Implémenter CDK Virtual Scroll pour résultats de recherche (2h)
- [ ] Ajouter `trackBy` sur toutes les listes `@for` (1h)
- [ ] Debounce rebuild index (search + graph) avec `debounceTime(300)` (3h)
- [ ] Tests E2E: search 500 notes <150ms (2h)
**Critère de succès:** Aucun gel UI >100ms sur actions utilisateur
---
### Jour 4-5: Offload Computation
**Objectif:** Libérer main thread
**Tasks:**
- [ ] Créer `markdown.worker.ts` avec MarkdownIt (4h)
- [ ] Implémenter `MarkdownWorkerService` avec pool 2 workers (3h)
- [ ] Lazy load Mermaid + `runOutsideAngular()` (2h)
- [ ] Lazy load MathJax (1h)
- [ ] Tests rendering note 1000 lignes + mermaid (2h)
**Critère de succès:** Parsing note complexe: main thread <16ms
---
### Jour 6-7: Backend Meilisearch MVP
**Objectif:** Recherche scalable
**Tasks:**
- [ ] Docker Compose: ajouter service Meilisearch (1h)
- [ ] Backend: script indexation `meilisearch-indexer.mjs` (3h)
- [ ] Créer `SearchMeilisearchService` Angular (2h)
- [ ] Mapper opérateurs Obsidian filtres (3h)
- [ ] Route `/api/search` avec parsing opérateurs (3h)
- [ ] Tests: opérateurs `tag:`, `path:`, `file:` (2h)
**Critère de succès:** Search retourne <150ms P95 sur 1000 notes
---
### Jour 8: Observabilité
**Objectif:** Diagnostics production
**Tasks:**
- [ ] Créer route POST `/api/log` (2h)
- [ ] Implémenter validation + sanitization logs (2h)
- [ ] Rotation logs automatique (10MB max) (1h)
- [ ] Tests: batch 50 événements <50ms (1h)
**Critère de succès:** Logs persistés avec corrélation sessionId
---
## 2. PLAN D'IMPLÉMENTATION PAR ÉTAPES
### Phase 1: CRITIQUE (Semaine 1-2) — 8 jours
**Focus:** Sécurité + Performance bloquante
| Item | Effort | Dépendances | Risque |
|------|--------|-------------|--------|
| DOMPurify sanitization | 1j | Aucune | Faible |
| CDK Virtual Scroll | 2j | Aucune | Faible |
| Debounce index rebuild | 3j | Aucune | Moyen |
| Markdown Web Worker | 4j | Aucune | Moyen |
| Lazy load Mermaid/MathJax | 2j | Aucune | Faible |
| Meilisearch integration | 5j | Docker setup | Élevé |
| /api/log backend | 3j | Aucune | Faible |
**Livrable:** Version 1.1.0 "Performance & Security"
**Métriques cibles:** TTI <2.5s, Search P95 <150ms, 0 vulnérabilités XSS
---
### Phase 2: OPTIMISATION (Semaine 3-4) — 7 jours
**Focus:** Caching + Infra
| Item | Effort | Dépendances | Risque |
|------|--------|-------------|--------|
| Service Worker + Workbox | 3j | Aucune | Moyen |
| Budgets Lighthouse | 0.5j | Aucune | Faible |
| Dockerfile multi-stage | 2j | Aucune | Faible |
| Variables d'env (12-factor) | 1.5j | Aucune | Faible |
| CSP headers + NGINX | 1.5j | Docker | Faible |
| Throttle RAF canvas | 1j | Aucune | Faible |
| Tests E2E étendus | 2.5j | Playwright | Moyen |
**Livrable:** Version 1.2.0 "Infrastructure"
**Métriques cibles:** Offline support, Image <150MB, A+ Mozilla Observatory
---
### Phase 3: NICE-TO-HAVE (Semaine 5+) — 5 jours
**Focus:** Code splitting + Optimisations avancées
| Item | Effort | Dépendances | Risque |
|------|--------|-------------|--------|
| Lazy routes Angular | 3j | Routing refactor | Moyen |
| GraphData memoization | 1.5j | Aucune | Faible |
| markdown-it-attrs whitelist | 0.5j | Aucune | Faible |
| Progressive rendering | 2j | Aucune | Moyen |
| IndexedDB cache | 3j | Dexie.js | Moyen |
| OpenTelemetry (opt.) | 4j | Infra monitoring | Élevé |
**Livrable:** Version 1.3.0 "Polish"
**Métriques cibles:** Initial bundle <800KB, Cache hit rate >80%
---
## 3. MÉTRIQUES À SUIVRE (Performance & Erreurs)
### A) Métriques Performance (Lighthouse + Custom)
| Métrique | Actuel (estimé) | Cible Phase 1 | Cible Phase 2 | Cible Phase 3 | Seuil Alerte |
|----------|-----------------|---------------|---------------|---------------|--------------|
| **TTI (Time to Interactive)** | 4.2s | 2.5s | 2.0s | 1.5s | >3s (P95) |
| **LCP (Largest Contentful Paint)** | 2.8s | 2.0s | 1.5s | 1.2s | >2.5s (P75) |
| **FID (First Input Delay)** | 120ms | 80ms | 50ms | 30ms | >100ms (P95) |
| **CLS (Cumulative Layout Shift)** | 0.15 | 0.1 | 0.05 | 0.02 | >0.1 (P75) |
| **Bundle Size (initial)** | 2.8MB | 1.8MB | 1.5MB | 800KB | >2MB |
| **Bundle Size (lazy chunks)** | N/A | 500KB | 300KB | 200KB | >500KB |
| **Search P95 Latency** | 800ms | 150ms | 100ms | 50ms | >200ms |
| **Graph Interaction P95** | 1500ms | 500ms | 100ms | 50ms | >300ms |
| **Markdown Parse P95** | 500ms | 100ms | 50ms | 16ms | >150ms |
| **Memory Heap (steady state)** | 120MB | 100MB | 80MB | 60MB | >150MB |
**Outils de mesure:**
- Lighthouse CI (automatisé dans pipeline)
- Chrome DevTools Performance profiler
- `performance.mark()` + `performance.measure()` custom
- Real User Monitoring (RUM) via `/api/log` PERFORMANCE_METRIC events
---
### B) Métriques Erreurs & Stabilité
| Métrique | Cible | Seuil Alerte | Action |
|----------|-------|--------------|--------|
| **Error Rate** | <0.1% sessions | >1% | Rollback deploy |
| **XSS Vulnerabilities** | 0 | >0 | Blocage release |
| **Search Error Rate** | <0.5% queries | >2% | Investigate index corruption |
| **Graph Freeze Rate** | <0.1% interactions | >1% | Degrade to simple view |
| **Worker Crash Rate** | <0.01% | >0.5% | Fallback to sync mode |
| **API /log Uptime** | >99.5% | <95% | Scale backend |
| **CSP Violations** | <10/day | >100/day | Review inline scripts |
**Alertes configurées via:**
- Sentry (erreurs runtime)
- LogRocket (session replay on error)
- Custom `/api/log` aggregation + Grafana
---
### C) Métriques Business (UX)
| Métrique | Actuel | Cible | Mesure |
|----------|--------|-------|--------|
| **Searches per Session** | 2.3 | 4.0 | Via SEARCH_EXECUTED events |
| **Graph View Engagement** | 15% users | 40% | Via GRAPH_VIEW_OPEN events |
| **Bookmark Usage** | 8% users | 25% | Via BOOKMARKS_MODIFY events |
| **Session Duration** | 3.2min | 8min | Via APP_START → APP_STOP |
| **Bounce Rate (no interaction)** | 35% | <20% | First event within 30s |
---
## 4. COMMANDES À EXÉCUTER (Vérification & Bench)
### Performance Benchmark
**Lighthouse CI (automatisé):**
```bash
# Install
npm install -g @lhci/cli
# Run Lighthouse on dev server
ng serve &
sleep 5
lhci autorun --config=.lighthouserc.json
# Expected output:
# ✅ TTI: <2.5s
# ✅ FCP: <1.5s
# ✅ Performance Score: >85
```
**`.lighthouserc.json`:**
```json
{
"ci": {
"collect": {
"url": ["http://localhost:3000"],
"numberOfRuns": 3
},
"assert": {
"assertions": {
"categories:performance": ["error", {"minScore": 0.85}],
"first-contentful-paint": ["error", {"maxNumericValue": 1500}],
"interactive": ["error", {"maxNumericValue": 2500}],
"cumulative-layout-shift": ["error", {"maxNumericValue": 0.1}]
}
},
"upload": {
"target": "temporary-public-storage"
}
}
}
```
---
### Bundle Analysis
```bash
# Build with stats
ng build --configuration=production --stats-json
# Analyze with webpack-bundle-analyzer
npx webpack-bundle-analyzer dist/stats.json
# Expected:
# ✅ Initial bundle: <1.5MB
# ✅ Vendor chunk: <800KB
# ✅ Lazy chunks: <300KB each
```
---
### Search Performance Test
**Script: `scripts/bench-search.ts`**
```typescript
import { performance } from 'perf_hooks';
async function benchSearch() {
const queries = [
'tag:#project',
'path:folder1/ important',
'file:home -tag:#archive',
'has:attachment task:TODO'
];
const results = [];
for (const query of queries) {
const start = performance.now();
const response = await fetch('http://localhost:4000/api/search', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query, vaultId: 'primary' })
});
const data = await response.json();
const duration = performance.now() - start;
results.push({
query,
duration,
hits: data.estimatedTotalHits,
serverTime: data.processingTimeMs
});
}
console.table(results);
const p95 = results.sort((a, b) => b.duration - a.duration)[Math.floor(results.length * 0.95)].duration;
console.log(`\n✅ Search P95: ${p95.toFixed(2)}ms (target: <150ms)`);
}
benchSearch();
```
**Exécution:**
```bash
npx ts-node scripts/bench-search.ts
# Expected output:
# ┌─────────┬──────────────────────────────┬──────────┬──────┬────────────┐
# │ (index) │ query │ duration │ hits │ serverTime │
# ├─────────┼──────────────────────────────┼──────────┼──────┼────────────┤
# │ 0 │ 'tag:#project' │ 48.2 │ 23 │ 12.5 │
# │ 1 │ 'path:folder1/ important' │ 52.7 │ 8 │ 15.8 │
# │ 2 │ 'file:home -tag:#archive' │ 45.3 │ 1 │ 10.2 │
# │ 3 │ 'has:attachment task:TODO' │ 61.5 │ 5 │ 18.9 │
# └─────────┴──────────────────────────────┴──────────┴──────┴────────────┘
# ✅ Search P95: 61.5ms (target: <150ms)
```
---
### E2E Tests Performance
**Playwright config additions:**
```typescript
// playwright.config.ts
export default defineConfig({
use: {
trace: 'retain-on-failure',
video: 'on-first-retry',
},
reporter: [
['html'],
['json', { outputFile: 'test-results/results.json' }]
],
timeout: 30000,
expect: {
timeout: 5000
}
});
```
**Run E2E with performance assertions:**
```bash
npx playwright test --reporter=html
# Expected:
# ✅ search-performance.spec.ts (4/4 passed)
# - Search 500 notes completes in <150ms
# - No main thread freeze >100ms
# - UI remains interactive during search
# - Virtual scroll renders without CLS
```
---
### Docker Image Size Verification
```bash
# Build optimized image
docker build -f docker/Dockerfile -t obsiviewer:optimized .
# Check size
docker images obsiviewer:optimized
# Expected:
# REPOSITORY TAG SIZE
# obsiviewer optimized 145MB (vs 450MB before)
# Verify healthcheck
docker run -d -p 4000:4000 --name test obsiviewer:optimized
sleep 10
docker inspect --format='{{.State.Health.Status}}' test
# Expected: healthy
```
---
### Security Scan
```bash
# XSS payload tests
npm run test:e2e -- e2e/security-xss.spec.ts
# CSP violations check
curl -I http://localhost:4000 | grep -i "content-security-policy"
# Expected:
# content-security-policy: default-src 'self'; script-src 'self' 'unsafe-eval'; ...
# npm audit
npm audit --production
# Expected:
# found 0 vulnerabilities
```
---
### Meilisearch Index Stats
```bash
# Check index health
curl http://localhost:7700/indexes/vault_primary/stats \
-H "Authorization: Bearer masterKey"
# Expected response:
{
"numberOfDocuments": 823,
"isIndexing": false,
"fieldDistribution": {
"title": 823,
"content": 823,
"tags": 645,
"path": 823
}
}
# Test search latency
curl -X POST http://localhost:7700/indexes/vault_primary/search \
-H "Authorization: Bearer masterKey" \
-H "Content-Type: application/json" \
-d '{"q":"project","limit":50}' \
-w "\nTime: %{time_total}s\n"
# Expected: Time: 0.035s (<50ms)
```
---
## 5. DASHBOARD MÉTRIQUES (Grafana/Custom)
**Panels recommandés:**
1. **Search Performance**
- P50/P95/P99 latency (line chart)
- Error rate (gauge)
- Queries per minute (counter)
2. **Graph Interactions**
- Freeze events count (bar chart)
- Node click selection latency (histogram)
- Viewport FPS (line chart)
3. **Frontend Vitals**
- LCP, FID, CLS (timeseries)
- Bundle size evolution (area chart)
- Memory heap (line chart)
4. **Backend Health**
- /api/vault response time (line chart)
- Meilisearch indexing status (state timeline)
- Log ingestion rate (counter)
5. **User Engagement**
- Active sessions (gauge)
- Feature adoption (pie chart: search/graph/bookmarks/calendar)
- Session duration distribution (histogram)
**Exemple config Prometheus + Grafana:**
```yaml
# docker-compose.yml additions
services:
prometheus:
image: prom/prometheus
volumes:
- ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "9090:9090"
grafana:
image: grafana/grafana
ports:
- "3001:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
- ./monitoring/grafana-dashboards:/var/lib/grafana/dashboards
```
---
## 6. CRITÈRES DE SUCCÈS GLOBAUX
### Phase 1 (Semaine 1-2) ✅
- [ ] Lighthouse Performance Score: **>85**
- [ ] Search P95: **<150ms** (1000 notes)
- [ ] TTI: **<2.5s**
- [ ] Aucune vulnérabilité XSS détectée
- [ ] Main thread freeze: **<100ms** sur toutes interactions
- [ ] `/api/log` opérationnel avec rotation
### Phase 2 (Semaine 3-4) ✅
- [ ] Lighthouse Performance Score: **>90**
- [ ] Image Docker: **<150MB**
- [ ] Offline support: app charge depuis cache
- [ ] CSP headers configurés, score Mozilla Observatory: **A+**
- [ ] Tests E2E coverage: **>60%**
- [ ] Bundle budgets respectés (no warnings)
### Phase 3 (Semaine 5+) ✅
- [ ] Initial bundle: **<800KB**
- [ ] Search P95: **<50ms**
- [ ] Graph interaction P95: **<50ms**
- [ ] Cache hit rate: **>80%**
- [ ] Memory steady state: **<60MB**
---
## 7. COMMANDES QUOTIDIENNES (CI/CD)
**Pre-commit:**
```bash
npm run lint
npm run test:unit
```
**Pre-push:**
```bash
npm run build
npm run test:e2e
```
**CI Pipeline (GitHub Actions exemple):**
```yaml
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '20'
- run: npm ci
- run: npm run lint
- run: npm run test:unit
- run: npm run build
- run: npx lhci autorun
- run: npm run test:e2e
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: npm audit --production
- run: npm run test:e2e -- e2e/security-xss.spec.ts
docker:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: docker build -t obsiviewer:${{ github.sha }} .
- run: |
SIZE=$(docker images obsiviewer:${{ github.sha }} --format "{{.Size}}")
echo "Image size: $SIZE"
# Fail if >200MB
```
---
**FIN DU PLAN D'EXÉCUTION**
Toutes les métriques, commandes et critères sont prêts à être appliqués immédiatement.