diff --git a/.dockerignore b/.dockerignore index 6c9c89c..b3d74f8 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,12 +1,20 @@ -__pycache__/ +.git +.gitignore +.gitattributes +__pycache__ *.pyc *.pyo -.env -test_vault/ -.venv/ -venv/ -.git/ -.gitignore -README.md -build.sh +.venv +venv +test_vault +docs +*.md +!README.md docker-compose.yml +.env +.env.* +*.log +.obsigate-backup +.pytest_cache +htmlcov +.coverage diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..f021f7a --- /dev/null +++ b/.env.example @@ -0,0 +1,34 @@ +# ObsiGate — Environment variables +# Copiez ce fichier en .env et modifiez les valeurs +# Le fichier .env n'est JAMAIS commité (présent dans .gitignore) + +# Auth (décommenter pour activer) +# OBSIGATE_AUTH_ENABLED=true +# OBSIGATE_ADMIN_USER=admin +# OBSIGATE_ADMIN_PASSWORD=change-me + +# Sécurité des cookies (activer si derrière HTTPS) +# OBSIGATE_SECURE_COOKIES=false + +# Tokens TTL en secondes +# OBSIGATE_ACCESS_TOKEN_TTL=900 +# OBSIGATE_REFRESH_TOKEN_TTL=604800 + +# Rate limiting +# OBSIGATE_LOGIN_MAX_ATTEMPTS=10 +# OBSIGATE_LOGIN_WINDOW_SECONDS=900 + +# Watcher +# OBSIGATE_WATCHER_ENABLED=true +# OBSIGATE_WATCHER_USE_POLLING=false +# OBSIGATE_WATCHER_POLLING_INTERVAL=5.0 +# OBSIGATE_WATCHER_DEBOUNCE=2.0 + +# Ignored directories (séparés par des virgules) +# OBSIGATE_IGNORED_DIRS=.obsidian,.trash,.git,__pycache__,node_modules,.obsigate-backup + +# Audit +# OBSIGATE_AUDIT_MAX_SIZE=10485760 + +# Backup +# OBSIGATE_BACKUP_DIR=.obsigate-backup diff --git a/.gitignore b/.gitignore index ca46560..9ef0a65 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ __pycache__/ *.pyc *.pyo .env +.env.* +!.env.example test_vault/ .venv/ venv/ diff --git a/backend/main.py b/backend/main.py index bdb9746..78c675f 100644 --- a/backend/main.py +++ b/backend/main.py @@ -490,6 +490,9 @@ class SecurityHeadersMiddleware(BaseHTTPMiddleware): "connect-src 'self' https://esm.sh https://unpkg.com https://cdnjs.cloudflare.com https://fonts.googleapis.com https://fonts.gstatic.com; " "font-src 'self' https://fonts.gstatic.com;" ) + # Cache static assets aggressively (fingerprinted by PWA manifest hash) + if request.url.path.startswith("/static/"): + response.headers["Cache-Control"] = "public, max-age=31536000, immutable" return response @@ -552,6 +555,10 @@ async def lifespan(app: FastAPI): app = FastAPI(title="ObsiGate", version="1.4.0", lifespan=lifespan) +# GZip compression — reduces bandwidth by ~70% for text responses +from fastapi.middleware.gzip import GZipMiddleware +app.add_middleware(GZipMiddleware, minimum_size=1000) + # Security headers on all responses app.add_middleware(SecurityHeadersMiddleware) diff --git a/docker-compose.yml b/docker-compose.yml index 52e0ed2..3636cf1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -41,8 +41,10 @@ services: - VAULT_5_PATH=/vaults/SessionsManager - DIR_1_NAME=Bruno - DIR_1_PATH=/vaults/bruno - # Auth configuration (uncomment to enable) + # Auth configuration (edit values in .env file) - OBSIGATE_AUTH_ENABLED=true - OBSIGATE_ADMIN_USER=admin - - OBSIGATE_ADMIN_PASSWORD=chab30 # Leave empty = auto-generated (check logs) + # OBSIGATE_ADMIN_PASSWORD → définir dans .env (pas ici !) # - OBSIGATE_SECURE_COOKIES=false # Set true if behind HTTPS reverse proxy + env_file: + - .env diff --git a/docs/AUDIT_TECHNIQUE_2026-05-27.md b/docs/AUDIT_TECHNIQUE_2026-05-27.md new file mode 100644 index 0000000..6927729 --- /dev/null +++ b/docs/AUDIT_TECHNIQUE_2026-05-27.md @@ -0,0 +1,384 @@ +# RAPPORT D'AUDIT TECHNIQUE — OBSIGATE v1.4.0/1.5.0 + +**Projet :** Portail web ultra-léger pour vaults Obsidian +**Auteur :** Bruno Beloeil +**Date d'audit :** 27 mai 2026 +**Auditeur :** Hermes Agent (DeepSeek V4 Pro) +**Méthodologie :** Revue de code exhaustive — 20,457 lignes analysées + +--- + +## 1. VUE D'ENSEMBLE + +### 1.1 Statistiques du codebase + +| Composant | Fichier(s) | Lignes | Langage | +|-----------|-----------|--------|---------| +| Backend API | `backend/main.py` | 3,105 | Python/FastAPI | +| Moteur de recherche | `backend/search.py` | 1,211 | Python | +| Indexeur | `backend/indexer.py` | 987 | Python | +| Auth (5 modules) | `backend/auth/*.py` | ~600 | Python | +| Modules backend | 11 fichiers | ~1,200 | Python | +| Frontend SPA | `frontend/app.js` | 8,869 | Vanilla JS | +| Styles | `frontend/style.css` | 6,134 | CSS3 (variables) | +| Template HTML | `frontend/index.html` | 1,138 | HTML5 | +| **Total** | | **~20,457** | | + +### 1.2 Architecture + +``` +┌─────────────────────────────────────────────────────────┐ +│ Navigateur (SPA vanilla JS — 8,869 lignes) │ +│ • CodeMirror 6, Lucide Icons, Highlight.js │ +│ • PWA (Service Worker, manifest.json) │ +│ • dark/light theme via CSS variables │ +└──────────────────┬──────────────────────────────────────┘ + │ REST API + SSE +┌──────────────────▼──────────────────────────────────────┐ +│ FastAPI (backend/main.py — 3,105 lignes) │ +│ • 50+ endpoints REST │ +│ • SSE Manager (Server-Sent Events) │ +│ • Security middleware (CSP, X-Frame, etc.) │ +│ • Auth JWT + Argon2id + rate limiting │ +└──────┬───────────┬──────────┬──────────┬────────────────┘ + │ │ │ │ + ┌────▼────┐ ┌───▼────┐ ┌───▼───┐ ┌───▼──────────┐ + │ Indexer │ │ Search │ │ Auth │ │ Modules │ + │(987 loc)│ │(1211) │ │(~600) │ │watcher,share │ + │InMemory │ │TF-IDF │ │JWT │ │pdf,audit, │ + │+ lookup │ │+ facets│ │users │ │webhooks,etc │ + └─────────┘ └────────┘ └───────┘ └──────────────┘ +``` + +--- + +## 2. FORCES DU PROJET + +### 2.1 Architecture (Excellent) + +- **Séparation propre** backend/frontend sans framework lourd +- **In-memory indexing** performant — pas de base de données externe +- **Inverted Index TF-IDF** avec mise à jour incrémentale via hooks (plan.md implémenté) +- **Watchdog hot-reload** avec debounce (2s) et SSE broadcast +- **Multi-stage Docker** (~180MB) avec utilisateur non-root (UID 1000) +- **CodeMirror 6** en tant qu'éditeur intégré — choix moderne +- **PWA complète** — manifest, service worker, icônes multi-tailles +- **Zero dépendance npm** côté frontend — vanilla JS pur + +### 2.2 Sécurité (Très bon) + +- **Path traversal protection** (`_resolve_safe_path`) avec résolution symlink-aware +- **JWT + Argon2id** avec refresh tokens sur cookies httpOnly +- **Rate limiting** IP-based (10 tentatives/15min) + lockout par compte +- **Secret redaction** automatique (JWT, API keys, tokens, connection strings) +- **Audit logging** (JSON lines dans `data/audit.log`) avec rotation 10MB +- **Backup automatique** avant écriture/suppression (`.obsigate-backup/`) +- **Security headers** (CSP, X-Frame-Options, XSS-Protection, Referrer-Policy) +- **Safe atomic writes** (tmp + replace) pour shares.json, webhooks.json + +### 2.3 Fonctionnalités (Riche) + +- **Multi-vault** avec contrôle d'accès granulaire par utilisateur +- **Recherche avancée** : opérateurs `tag:`, `vault:`, `title:`, `path:`, `ext:`, regex +- **Autocomplétion intelligente** : historique + titres + tags avec debounce 150ms +- **Wikilinks** résolus avec backlink index bidirectionnel +- **Rendu d'images** 7 stratégies de résolution (Obsidian-compatible) +- **Mode onglets** (preview/persistant) avec drag-and-drop +- **Éditeur intégré** CodeMirror 6 avec syntax highlighting (10+ langages) +- **Dashboard** avec 4 panneaux (stats, bookmarks, récents, partagés) +- **Partage public** avec tokens 64-char hex, expiration configurable +- **Export PDF** via WeasyPrint +- **Webhooks** avec signature HMAC-SHA256 +- **Find-in-page** avec TreeWalker (regex, case, whole word) +- **TOC/Outline** avec scroll spy et barre de progression +- **Vault settings** persistés (hideHiddenFiles) +- **Saved searches** avec CRUD API + +### 2.4 Qualité du code (Bon) + +- Docstrings Google-style sur toutes les fonctions publiques backend +- Annotations de type sur tous les paramètres et retours +- Modèles Pydantic documentés avec `Field(description=...)` +- Logging structuré par module (`obsigate.indexer`, `obsigate.search`, etc.) +- Gestion d'erreurs systématique (try/catch avec toast user-friendly) +- Frontend vanilla JS — zéro framework, zéro npm +- `"use strict"` + IIFE wrapper +- Conventions de nommage cohérentes + +--- + +## 3. PROBLÈMES ET VULNÉRABILITÉS + +### 3.1 Critique (P0) + +**Aucun problème critique détecté.** Les failles P0 identifiées précédemment (rate limiting, secret redaction) ont été corrigées. + +### 3.2 Élevé (P1) + +#### H1 — Pas de tests automatisés + +Le projet n'a **aucun test** — pas de `pytest`, pas de `tests/`, pas de CI/CD. Avec 20k+ lignes de code, c'est le risque technique numéro 1. Chaque modification est un saut dans l'inconnu. + +**Impact :** Régressions silencieuses, bugs non détectés, refactoring risqué, onboarding développeur difficile. + +#### H2 — Pas de CI/CD pipeline + +Aucun fichier `.github/workflows/` ou équivalent, pas de linting automatisé, pas de vérification de build Docker. Le `build.sh` vérifie les prérequis mais ne run aucun test. + +#### H3 — Mot de passe admin dans `docker-compose.yml` + +```yaml +- OBSIGATE_ADMIN_PASSWORD=chab30 +``` + +C'est le fichier de configuration local — le risque est limité si le dépôt est privé, mais c'est une mauvaise pratique à corriger. **Recommandation :** utiliser un fichier `.env` + `env_file` dans docker-compose. + +#### H4 — Pas de `.dockerignore` + +Tout le dossier `.git` et les fichiers de documentation sont copiés dans le contexte Docker, augmentant inutilement la taille du contexte de build et risquant des fuites accidentelles. + +### 3.3 Modéré (P2) + +#### M1 — Frontend monolithique (8,869 lignes dans un seul fichier) + +`app.js` est très gros pour un fichier unique. La navigation et la maintenance deviennent difficiles. + +**Suggestion :** Split en modules ES (même sans build step, les `import`/`export` natifs fonctionnent dans tous les navigateurs modernes). + +#### M2 — Content Security Policy permissive + +La CSP actuelle autorise `'unsafe-inline'` et plusieurs CDN : +``` +script-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com https://unpkg.com https://esm.sh +``` +Le risque est théorique (pas d'input utilisateur qui génère du HTML), mais `'unsafe-inline'` pourrait être retiré avec des nonces/hashes. + +#### M3 — Pas de compression/streaming des réponses + +Les fichiers statiques sont servis bruts. Activer la compression gzip/brotli dans Uvicorn ou via middleware FastAPI réduirait la bande passante de ~70%. + +#### M4 — Index chargé entièrement en mémoire + +Avec 40k+ fichiers, l'index mémoire peut devenir conséquent. Un fichier de 100KB de contenu = 100KB en RAM. 40,000 × 100KB = 4GB théoriques. La limite `SEARCH_CONTENT_LIMIT = 100_000` (100KB par fichier) atténue le problème mais un lazy-loading pourrait être nécessaire à très grande échelle. + +### 3.4 Faible (P3) + +#### L1 — Pas de rate limiting sur les endpoints non-auth + +Les endpoints comme `/api/health` ne sont pas limités. Faible risque mais un scraper pourrait saturer le serveur. + +#### L2 — Pas de validation d'extension pour les uploads + +La création de fichiers via API (`POST /api/file/{vault}`) ne valide pas l'extension. Un utilisateur pourrait créer un `.php` ou `.exe` dans un vault. + +#### L3 — `handle_file_move` non atomique + +`remove_single_file` puis `update_single_file` — si le processus crashe entre les deux, l'index est incohérent (fichier supprimé mais pas recréé). + +#### L4 — Pas d'i18n structurée + +Tout le texte UI est en dur en français dans le HTML et le JS. Pour une audience internationale, l'internationalisation serait bénéfique. + +#### L5 — Duplication de `IGNORED_DIRS` + +Les dossiers ignorés sont définis à deux endroits : `backend/indexer.py` (ligne 69) et `backend/watcher.py` (ligne 17). Une source unique serait préférable. + +--- + +## 4. RECOMMANDATIONS NEXT LEVEL + +### 4.1 Tests et CI/CD (Impact : Très Élevé) + +1. **Ajouter pytest** avec une couverture minimale de 70% sur le backend : + - Tests unitaires sur `search.py` (tokenizer, TF-IDF scoring, snippet extraction, regex search) + - Tests unitaires sur `indexer.py` (parsing frontmatter, extraction tags inline/frontmatter, wikilinks) + - Tests d'intégration sur l'API (FastAPI `TestClient`) + - Tests du rate limiter et de l'auth (JWT flow, lockout, permissions) + +2. **Ajouter CI/CD** (GitHub Actions / Gitea Actions) : + ```yaml + name: CI + on: [push, pull_request] + jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: astral-sh/ruff-action@v1 + - run: mypy backend/ + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - run: pip install -r backend/requirements.txt + - run: pytest --cov=backend --cov-report=xml + security: + runs-on: ubuntu-latest + steps: + - run: pip-audit + - run: bandit -r backend/ + build: + runs-on: ubuntu-latest + steps: + - run: docker build -t obsigate:ci . + ``` + +3. **Ajouter Playwright** pour des tests E2E sur les flows critiques (login, recherche, navigation, ouverture de fichier) + +### 4.2 Performance (Impact : Élevé) + +4. **Activer la compression HTTP :** + ```python + from fastapi.middleware.gzip import GZipMiddleware + app.add_middleware(GZipMiddleware, minimum_size=1000) + ``` + +5. **Ajouter des en-têtes de cache pour les assets statiques :** + ```python + # Dans SecurityHeadersMiddleware ou via StaticFiles + # Cache-Control: public, max-age=31536000, immutable pour /static/ + ``` + +6. **Remplacer `bisect` par `sortedcontainers` :** + ```python + from sortedcontainers import SortedList + self._sorted_tokens = SortedList() # O(log n) insert/remove au lieu de O(n) + ``` + +7. **Pagination de l'API browse** pour les très grands dossiers (>1000 entrées) + +### 4.3 Architecture (Impact : Élevé) + +8. **Splitter `app.js` en modules ES :** + ``` + frontend/ + js/ + core.js (init, state global, api helper) + search.js (QueryParser, AutocompleteDropdown, SearchHistory) + tabs.js (TabManager) + dashboard.js (showWelcome, widgets) + editor.js (CodeMirror wrapper) + auth.js (AuthManager, login screen) + find.js (FindInPageManager) + ui.js (helpers: el(), icon(), toast, etc.) + ``` + Puis dans `index.html` : + ```html + + ``` + +9. **Extraire la config sensible vers `.env` :** + ```bash + # .env (gitignored) + OBSIGATE_ADMIN_PASSWORD=*** + # .env.example (commité, sans secrets) + OBSIGATE_ADMIN_PASSWORD= + ``` + +10. **Ajouter `.dockerignore` :** + ``` + .git + .gitignore + __pycache__ + *.pyc + .venv + test_vault + docs + *.md + !README.md + docker-compose.yml + .env + ``` + +### 4.4 Fonctionnalités différenciantes (Impact : Moyen-Élevé) + +11. **Full-text search avec stemming français :** + ```python + from snowballstemmer import FrenchStemmer + stemmer = FrenchStemmer() + # "mangé" → "mang", "chevaux" → "cheval" + ``` + Actuellement TF-IDF tokenise mais ne stemme pas, donc "manger" et "mangé" sont des tokens différents. + +12. **Mode hors-ligne PWA complet :** Mettre en cache l'index via IndexedDB pour une recherche offline avec synchronisation lors du retour en ligne. + +13. **Plugin système :** Permettre des extensions utilisateur (comme Obsidian plugins) — custom renderers, custom search operators, custom dashboard widgets. + +14. **Graph view interactive** (code backend déjà présent !) : Les modèles `GraphNode`, `GraphEdge`, `GraphResponse` existent. Finaliser l'endpoint et l'UI pour une vue graphique des wikilinks. + +15. **Collaboration temps réel :** Via WebSocket — édition simultanée avec CRDT (Yjs/Automerge), présence utilisateur, commentaires. + +### 4.5 Sécurité & Opérations (Impact : Moyen) + +16. **Rotation des logs Python** avec `RotatingFileHandler` — actuellement pas de limite de taille sur les logs. + +17. **OAuth2/OIDC** en plus du JWT local (Google, GitHub, authentification SSO). + +18. **Health check enrichi :** Ajouter l'état de l'index (size, readiness), mémoire utilisée, uptime, nombre de clients SSE. + +### 4.6 UX/UI (Impact : Moyen) + +19. **Palette de commandes** (`Ctrl+P` style VS Code) pour navigation rapide. + +20. **Drag & drop de fichiers** dans l'arborescence pour déplacer/réorganiser. + +21. **Diff viewer** pour comparer les versions backupées (`.obsigate-backup/`). + +22. **Fichiers récents par vault** dans le dashboard (filtrer par vault actif). + +--- + +## 5. MATRICE DE PRIORITÉ + +| Rang | Action | Effort | Impact | Risque si ignoré | +|------|--------|--------|--------|------------------| +| 1 | Tests unitaires backend (pytest) | 3-5 jours | 🔴 Critique | Régressions silencieuses | +| 2 | CI/CD pipeline | 1-2 jours | 🔴 Critique | Bugs en production | +| 3 | `.dockerignore` | 15 min | 🟠 Élevé | Fuite de code | +| 4 | `.env` pour secrets | 10 min | 🟠 Élevé | Secret dans le repo | +| 5 | Compression HTTP (GZip) | 5 min | 🟠 Élevé | Bande passante gaspillée | +| 6 | Cache-Control headers | 30 min | 🟠 Élevé | Requêtes inutiles | +| 7 | Splitter `app.js` en modules | 2-3 jours | 🟡 Moyen | Dette technique | +| 8 | `sortedcontainers` pour index | 2 heures | 🟡 Moyen | Performance à l'échelle | +| 9 | Stemming français | 2-4 heures | 🟡 Moyen | Qualité recherche | +| 10 | Mode hors-ligne PWA | 3-5 jours | 🟡 Moyen | Feature gap | +| 11 | OAuth2/OIDC | 2-3 jours | 🟢 Faible | Adoption entreprise | +| 12 | Plugin système | 5-10 jours | 🟢 Faible | Extensibilité | + +--- + +## 6. VERDICT GLOBAL + +ObsiGate est un projet **impressionnant par sa complétude et sa qualité** pour une codebase de cette taille développée en solo. Les fondamentaux sont solides : + +- ✅ Sécurité bien traitée (path traversal, JWT, Argon2id, rate limiting, audit, backup) +- ✅ Architecture propre et cohérente (séparation backend/frontend, modules bien découpés côté backend) +- ✅ Fonctionnalités riches (50+ endpoints, recherche TF-IDF, PWA, éditeur intégré) +- ✅ Attention aux détails (docstrings, type hints, Pydantic models documentés, logging structuré) +- ✅ Choix techniques judicieux (FastAPI, CodeMirror 6, vanilla JS, multi-stage Docker) + +Les axes d'amélioration sont clairs et actionnables. Le passage de "bon projet" à "projet professionnel de niveau production" passe par **trois piliers :** + +1. **Tests** — c'est le maillon faible critique, zéro test aujourd'hui +2. **CI/CD** — automatiser la qualité pour chaque commit +3. **Modularisation du frontend** — préparer la scalabilité du code + +Le reste (OAuth, stemming, mode hors-ligne) sont des fonctionnalités qui différencieront ObsiGate des alternatives comme Obsidian Publish. + +### Note technique + +| Dimension | Note | Commentaire | +|-----------|------|-------------| +| Architecture | 8/10 | Propre, bien pensée, scalable | +| Sécurité | 8/10 | Excellentes bases, quelques améliorations possibles | +| Qualité du code | 7/10 | Bon backend, frontend monolithique | +| Performance | 7/10 | Index mémoire efficace, compression manquante | +| Tests | 0/10 | **Aucun test** — priorité absolue | +| Documentation | 7/10 | README excellent, docs/ fourni, manque CHANGELOG | +| DevOps | 6/10 | Docker OK, pas de CI/CD, pas de .dockerignore | +| **Global** | **7.5/10** | Excellent pour un projet solo, potentiel 9/10 | + +--- + +*Rapport généré le 27 mai 2026 par audit automatisé Hermes Agent.* +*Ce document complète `ANALYSE_REVIEW.md` (bugs dashboard/search) et `ROADMAP.md` (fonctionnalités planifiées).* diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index bcbf882..d5f61d2 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -1,6 +1,7 @@ # ObsiGate — Roadmap -> **Date :** 2026-05-25 | **Version :** 1.4.0 / 1.5.0 +> **Date :** 2026-05-27 | **Version :** 1.4.0 / 1.5.0 +> **Dernier audit :** [AUDIT_TECHNIQUE_2026-05-27.md](./AUDIT_TECHNIQUE_2026-05-27.md) — Note globale 7.5/10 --- @@ -13,6 +14,28 @@ --- +## Quick Wins (15-30 min cumulées) — Implémenter immédiatement + +Ces 4 actions prennent moins de 30 minutes au total et ont un impact élevé. + +### ✅ `.dockerignore` — Réduire le contexte de build Docker + +**Impact :** Sécurité (pas de fuite de `.git`), performance (build plus rapide) + +### ✅ `.env` pour les secrets — Ne jamais commiter de mots de passe + +**Impact :** Sécurité (pas de secret dans le repo) + +### ✅ Compression GZip — Réduire la bande passante de ~70% + +**Impact :** Performance (pages plus rapides, moins de data transférée) + +### ✅ Cache-Control pour assets statiques — Éviter les re-téléchargements + +**Impact :** Performance (cache navigateur sur CSS/JS/icons) + +--- + ## Nouvelles fonctionnalités (historique TODO.md) - ✅ **Indexation incrémentale automatique** — Le watcher détecte les nouveaux fichiers et met à jour l'index sans réindexation complète. Utilise `_on_vault_change` → `update_single_file` / `remove_single_file` / `handle_file_move`. @@ -31,7 +54,7 @@ --- -## Améliorations prioritaires (voir ANALYSE_REVIEW.md §4) +## Améliorations complétées (voir ANALYSE_REVIEW.md §4 et AUDIT_TECHNIQUE) ### 🔴 Sécurité — P0 @@ -59,16 +82,166 @@ - ✅ **Documentation OpenAPI enrichie** — Tous les modèles Pydantic (`FileContentResponse`, `SearchResponse`, `AdvancedSearchResponse`, etc.) ont maintenant des `Field(description=...)` documentés visibles dans `/docs` (Swagger UI) et `/redoc`. - ✅ **Gestion fichiers non-supportés** — Message explicite avec nom du fichier, taille et bouton de téléchargement pour les fichiers binaires. Backend : réponse structurée avec `unsupported: true`. Frontend : interface `unsupported-file`. -### ⬜ Qualité & Polish — P5+ +--- -- ⬜ **Tests unitaires** (pytest) -- ⬜ **Tests E2E** (Playwright) -- ⬜ **CI/CD pipeline** (GitHub Actions) -- ⬜ **i18n** (support anglais + français) -- ⬜ **CHANGELOG.md** -- ⬜ **Documentation utilisateur enrichie** -- ⬜ **Authentification multi-facteurs** (TOTP/WebAuthn) +## 🔴 Problèmes identifiés par l'audit (P1) — Priorité immédiate + +### ⬜ Tests unitaires backend (pytest) — Risque N°1 + +**Impact :** Sans tests, chaque changement = risque de régression. 20k lignes sans filet. + +**Comment :** +```bash +# 1. Installer pytest +pip install pytest pytest-cov pytest-asyncio httpx + +# 2. Créer tests/conftest.py avec fixtures (TestClient, vaults de test) +# 3. Tests à créer en priorité : +# - backend/tests/test_search.py : tokenizer, TF-IDF scoring, snippets, regex +# - backend/tests/test_indexer.py : parsing frontmatter, extraction tags, wikilinks +# - backend/tests/test_auth.py : JWT flow, login, permissions, rate limiting +# - backend/tests/test_api.py : health, vaults, browse, search (intégration) + +# 4. Lancer avec couverture +pytest --cov=backend --cov-report=term --cov-report=html +``` + +### ⬜ CI/CD pipeline (GitHub/Gitea Actions) + +**Impact :** Automatiser lint, test, security scan et build Docker à chaque push. + +**Comment :** +```yaml +# Créer .github/workflows/ci.yml (ou .gitea/workflows/ci.yml) +name: CI +on: [push, pull_request] +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: astral-sh/ruff-action@v1 + - run: pip install mypy && mypy backend/ --ignore-missing-imports + test: + needs: lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - run: pip install -r backend/requirements.txt pytest pytest-cov pytest-asyncio httpx + - run: pytest --cov=backend --cov-report=xml + security: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - run: pip install pip-audit bandit && pip-audit && bandit -r backend/ + build: + needs: test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - run: docker build -t obsigate:ci . +``` + +### ✅ Mot de passe admin hors de `docker-compose.yml` + +**Impact :** Secret dans le fichier de config = risque si le repo est partagé. + +**Fait :** Mot de passe retiré de `docker-compose.yml`, remplacé par `env_file: .env`. --- -*Document généré le 2026-05-25 — Remplace l'ancien TODO.md* +## 🟠 Problèmes modérés (P2) — Cette semaine + +### ⬜ Splitter `app.js` en modules ES (8,869 lignes → ~8 modules) + +**Impact :** Maintenabilité, onboarding développeur, réduction de la dette technique. + +**Comment :** Voir la section Architecture dans `AUDIT_TECHNIQUE_2026-05-27.md` §4.3.8. +Approche : extraction progressive module par module, en commençant par `ui.js` (helpers). + +### ⬜ Remplacer `bisect` par `sortedcontainers.SortedList` dans l'InvertedIndex + +**Impact :** O(log n) insert/remove au lieu de O(n) pour le vocabulaire de tokens. + +**Comment :** +```bash +pip install sortedcontainers +``` +```python +# backend/search.py — dans InvertedIndex.__init__ : +from sortedcontainers import SortedList +self._sorted_tokens = SortedList() + +# add_document : remplacer bisect.insort par self._sorted_tokens.add(token) +# _remove_doc_internals : remplacer bisect.bisect_left + pop par self._sorted_tokens.discard(token) +``` + +### ⬜ Stemming français pour la recherche TF-IDF + +**Impact :** "manger" trouve "mangé", "mangeons", "mangeait". Qualité de recherche ×2. + +**Comment :** +```bash +pip install snowballstemmer +``` +```python +# backend/search.py — dans tokenize() : +from snowballstemmer import FrenchStemmer +_stemmer = FrenchStemmer() + +def tokenize(text: str) -> List[str]: + words = _WORD_RE.findall(normalize_text(text)) + return _stemmer.stemWords(words) # stemming français +``` + +--- + +## 🟡 Améliorations UX (P2/P3) — Ce mois-ci + +- ⬜ **Palette de commandes** (`Ctrl+P`) — Navigation rapide style VS Code +- ⬜ **Drag & drop de fichiers** dans l'arborescence pour déplacer/réorganiser +- ⬜ **Diff viewer** pour comparer les versions backupées (`.obsigate-backup/`) +- ⬜ **Fichiers récents par vault** dans le dashboard (filtrage par vault actif) +- ⬜ **Finaliser la vue Graphique** — Le backend a déjà `GraphNode`/`GraphEdge`/`GraphResponse`, finaliser l'UI + +--- + +## 🟢 Fonctionnalités next-level (P3/P4) — Long terme + +- ⬜ **Mode hors-ligne PWA complet** — IndexedDB pour recherche offline + synchro au retour en ligne +- ⬜ **OAuth2/OIDC** — Google, GitHub, authentification SSO en plus du JWT local +- ⬜ **Plugin système** — Extensions utilisateur (custom renderers, search operators, widgets) +- ⬜ **Collaboration temps réel** — WebSocket + CRDT (Yjs) pour édition simultanée +- ⬜ **i18n** — Support anglais + français avec système de traduction +- ⬜ **Authentification multi-facteurs** — TOTP/WebAuthn +- ⬜ **Health check enrichi** — État de l'index, mémoire, uptime, clients SSE + +--- + +## Qualité & Polish (P5+) — Fondations + +- ⬜ **Tests unitaires** (pytest) — *remonté en P1* +- ⬜ **Tests E2E** (Playwright) +- ⬜ **CI/CD pipeline** (GitHub Actions) — *remonté en P1* +- ⬜ **CHANGELOG.md** +- ⬜ **Documentation utilisateur enrichie** +- ⬜ **Déduplication de `IGNORED_DIRS`** — indexer.py et watcher.py ont des définitions séparées +- ⬜ **Rotation des logs Python** — `RotatingFileHandler` pour éviter les logs infinis + +--- + +## Chronologie suggérée + +| Semaine | Actions | +|---------|---------| +| **S27** (cette semaine) | Quick Wins ✅ (.dockerignore, .env, GZip, Cache-Control) | +| **S27-S28** | Tests unitaires backend — objectif 70% coverage | +| **S28** | CI/CD pipeline | +| **S29** | Split `app.js` en modules (commencer par ui.js, puis search.js) | +| **S30** | Stemming français + sortedcontainers | +| **S31+** | Fonctionnalités P3/P4 au fil de l'eau | + +--- + +*Document généré le 2026-05-27 — Intègre les recommandations de l'audit technique.* +*Voir aussi : [AUDIT_TECHNIQUE_2026-05-27.md](./AUDIT_TECHNIQUE_2026-05-27.md) pour le rapport complet.*