# ObsiGate
**Porte d'entrée web ultra-léger pour vos vaults Obsidian** — Accédez, naviguez et recherchez dans toutes vos notes Obsidian depuis n'importe quel appareil via une interface web moderne et responsive.
[]()
[](https://opensource.org/licenses/MIT)
[](https://www.docker.com/)
[](https://www.python.org/)
```
┌─────────────────────────────────────────────────────────┐
│ [🔍 Recherche...] [☀/🌙 Thème] ObsiGate │
├──────────────┬──────────────────────────────────────────┤
│ SIDEBAR │ CONTENT AREA │
│ ▼ Recettes │ 📄 Titre du fichier │
│ 📁 Soupes │ Tags: #recette #rapide │
│ 📄 Pizza │ [Contenu Markdown rendu] │
│ ▼ IT │ │
│ 📁 Docker │ │
│ Tags Cloud │ │
└──────────────┴──────────────────────────────────────────┘
```
---
## 📋 Table des matières
- [Fonctionnalités](#-fonctionnalités)
- [Architecture](#-architecture)
- [Prérequis](#-prérequis)
- [Installation rapide](#-installation-rapide)
- [Configuration détaillée](#-configuration-détaillée)
- [Variables d'environnement](#-variables-denvironnement)
- [Ajouter une nouvelle vault](#-ajouter-une-nouvelle-vault)
- [Build multi-platform](#-build-multi-platform)
- [Utilisation](#-utilisation)
- [API](#-api)
- [Performance](#-performance)
- [Dépannage](#-dépannage)
- [Stack technique](#-stack-technique)
- [Changelog](#-changelog)
---
## ✨ Fonctionnalités
- **🗂️ Multi-vault** : Visualisez plusieurs vaults Obsidian simultanément
- **🌳 Navigation arborescente** : Parcourez vos dossiers et fichiers dans la sidebar
- **🔍 Recherche avancée** : Moteur TF-IDF avec normalisation des accents, snippets surlignés, facettes, pagination et tri
- **💡 Autocomplétion intelligente** : Suggestions de fichiers, tags et historique avec navigation clavier
- **🧩 Syntaxe de requête** : Opérateurs `tag:`, `#`, `vault:`, `title:`, `path:` avec chips visuels
- **📜 Historique de recherche** : Persisté en localStorage (max 50 entrées, LIFO, dédupliqué)
- **🏷️ Tag cloud** : Filtrage par tags extraits des frontmatters YAML
- **🔗 Wikilinks** : Les `[[liens internes]]` Obsidian sont cliquables
- **🖼️ Images Obsidian** : Support complet des syntaxes d'images Obsidian avec résolution intelligente
- **🎨 Syntax highlight** : Coloration syntaxique des blocs de code
- **🌓 Thème clair/sombre** : Toggle persisté en localStorage
- **� Synchronisation temps réel** : Surveillance automatique des fichiers via watchdog avec mise à jour incrémentale de l'index
- **📡 Server-Sent Events** : Notifications SSE pour les changements d'index avec reconnexion automatique
- **➕ Gestion dynamique des vaults** : Ajout/suppression de vaults via API sans redémarrage
- **�🐳 Docker multi-platform** : linux/amd64, linux/arm64, linux/arm/v7, linux/386
- **🔒 Sécurité** : Protection contre le path traversal, utilisateur non-root dans Docker
- **❤️ Healthcheck** : Endpoint `/api/health` intégré pour Docker et monitoring
---
## 🚀 Prérequis
### Système requis
- **Docker** >= 20.10
- **docker-compose** >= 2.0
- **Espace disque** : ~200MB pour l'image Docker
### Systèmes supportés
- Linux (Ubuntu, Debian, CentOS, etc.)
- macOS (Intel et Apple Silicon)
- Windows (avec Docker Desktop)
- NAS compatibles Docker (Synology, QNAP, etc.)
---
## ⚡ Installation rapide
### 1. Cloner le dépôt
```bash
git clone https://git.dracodev.net/Projets/ObsiGate.git
cd ObsiGate
```
### 2. Configurer vos vaults
Éditez le fichier `docker-compose.yml` pour ajouter vos vaults Obsidian :
```yaml
volumes:
- /chemin/absolu/vers/votre/vault:/vaults/NomDeVotreVault:ro
```
> **Important** : Le chemin doit être absolu et le volume en lecture seule (`:ro`)
### 3. Lancer l'application
```bash
# Build local de l'image + démarrage
docker-compose up -d --build
# Vérifier les logs
docker-compose logs -f obsigate
```
> **Note** : ObsiGate est construit localement depuis le `Dockerfile` du projet. Sans build local, Docker essaiera de télécharger une image distante `obsigate:latest` qui n'existe pas forcément.
### 4. Accéder à l'interface
Ouvrez votre navigateur sur : **http://localhost:2020**
---
## ⚙️ Configuration détaillée
### Étape 1 : Préparation des vaults
1. **Localisez vos vaults Obsidian** sur votre système
2. **Notez les chemins absolus** vers chaque dossier `.obsidian`
3. **Vérifiez les permissions** : Docker doit pouvoir lire ces dossiers
### Étape 2 : Configuration docker-compose.yml
Voici un exemple complet :
```yaml
services:
obsigate:
build:
context: .
image: obsigate:latest
container_name: obsigate
restart: unless-stopped
ports:
- "2020:8080" # Port local 2020 → Port conteneur 8080
volumes:
# Exemples de vaults (adaptez à vos chemins)
- /home/user/Documents/Obsidian-Recettes:/vaults/Recettes:ro
- /home/user/Documents/Obsidian-IT:/vaults/IT:ro
- /home/user/Documents/Obsidian-Perso:/vaults/Perso:ro
environment:
# Configuration des vaults
- VAULT_1_NAME=Recettes
- VAULT_1_PATH=/vaults/Recettes
- VAULT_2_NAME=IT
- VAULT_2_PATH=/vaults/IT
- VAULT_3_NAME=Perso
- VAULT_3_PATH=/vaults/Perso
```
### Étape 3 : Construction de l'image
```bash
# Build l'image Docker
docker build -t obsigate:latest .
# Puis démarrer le service
docker-compose up -d --build
# Ou utilisez le script de build multi-platform
chmod +x build.sh
./build.sh
```
> **Compatibilité Docker** : l'image utilise la variante minimale de `uvicorn` et une version de `fastapi` compatible (`0.110.3`) afin d'éviter certaines dépendances optionnelles natives (`watchfiles`, `uvloop`, `httptools`, `fastapi-cli`, etc.) qui peuvent échouer au build sur certaines plateformes comme Alpine, ARM ou i386.
---
## 🌍 Variables d'environnement
Les vaults sont configurées par paires de variables `VAULT_N_NAME` / `VAULT_N_PATH` (N = 1, 2, 3…) :
| Variable | Description | Exemple |
|----------|-------------|---------|
| `VAULT_1_NAME` | Nom affiché de la vault | `Recettes` |
| `VAULT_1_PATH` | Chemin dans le conteneur | `/vaults/Obsidian-RECETTES` |
| `VAULT_1_ATTACHMENTS_PATH` | Chemin relatif vers le dossier d'attachements (optionnel) | `06_Boite_a_Outils/6.2_Attachments` |
| `VAULT_1_SCAN_ATTACHMENTS` | Activer le scan d'images au démarrage (optionnel, défaut: true) | `true` |
| `VAULT_2_NAME` | Nom affiché de la vault | `IT` |
| `VAULT_2_PATH` | Chemin dans le conteneur | `/vaults/Obsidian_IT` |
**Règles de nommage :**
- Utilisez uniquement des lettres, chiffres et tirets
- Pas d'espaces ou caractères spéciaux
- Le nom doit correspondre au chemin dans le conteneur
---
## ➕ Ajouter une nouvelle vault
### Méthode 1 : Édition directe
1. **Arrêtez le conteneur** :
```bash
docker-compose down
```
2. **Ajoutez un volume** dans `docker-compose.yml` :
```yaml
volumes:
- /nouveau/chemin/vault:/vaults/NouvelleVault:ro
```
3. **Ajoutez les variables d'environnement** :
```yaml
environment:
- VAULT_4_NAME=NouvelleVault
- VAULT_4_PATH=/vaults/NouvelleVault
```
4. **Redémarrez** :
```bash
docker-compose up -d --build
```
### Méthode 2 : Hot-reload (recommandé)
1. **Ajoutez le volume et les variables** comme ci-dessus
2. **Appliquez les changements** :
```bash
docker-compose up -d --build
```
3. **Rechargez l'index** via l'interface ou l'API :
```bash
curl http://localhost:2020/api/index/reload
```
### Méthode 3 : API dynamique (sans redémarrage)
Ajoutez une vault à chaud via l'API (le volume doit déjà être monté) :
```bash
curl -X POST http://localhost:2020/api/vaults/add \
-H "Content-Type: application/json" \
-d '{"name": "NouvelleVault", "path": "/vaults/NouvelleVault"}'
```
Supprimez une vault :
```bash
curl -X DELETE http://localhost:2020/api/vaults/NouvelleVault
```
---
## 🔨 Build multi-platform
Pour les architectures multiples (Raspberry Pi, NAS, etc.) :
```bash
# Rendre le script exécutable
chmod +x build.sh
# Lancer le build multi-platform
./build.sh
```
**Architectures supportées :**
- `linux/amd64` (PC, serveurs standards)
- `linux/arm64` (Raspberry Pi 4, Apple Silicon, NAS modernes)
- `linux/arm/v7` (Raspberry Pi 3, anciens NAS)
- `linux/386` (Systèmes 32-bit legacy)
**Pour un build local uniquement :**
```bash
docker buildx build --platform linux/amd64 --load -t obsigate:latest .
```
---
## �️ Rendu d'images Obsidian
ObsiGate supporte **toutes les syntaxes d'images Obsidian** avec un système de résolution intelligent multi-stratégies.
### Syntaxes supportées
1. **Standard Markdown avec attributs HTML** (compatible Obsidian) :
```markdown
[
](https://example.com)
```
2. **Wiki-link embed avec chemin complet** :
```markdown
![[06_Boite_a_Outils/6.2_Attachments/image.svg]]
```
3. **Wiki-link embed avec nom de fichier uniquement** :
```markdown
![[image.svg]]
```
4. **Markdown standard** :
```markdown

```
### Résolution intelligente des chemins
ObsiGate utilise 7 stratégies de résolution par ordre de priorité :
1. **Chemin absolu** : Si le chemin est absolu et existe
2. **Dossier d'attachements configuré** : Via `VAULT_N_ATTACHMENTS_PATH`
3. **Index de démarrage (match unique)** : Recherche par nom de fichier dans l'index
4. **Même répertoire** : Relatif au fichier markdown courant
5. **Racine du vault** : Relatif à la racine du vault
6. **Index de démarrage (match le plus proche)** : Si plusieurs fichiers portent le même nom
7. **Fallback** : Affiche un placeholder stylisé `[image not found: filename.ext]`
### Configuration
Pour optimiser la résolution, configurez le dossier d'attachements de chaque vault :
```yaml
environment:
- VAULT_1_NAME=MonVault
- VAULT_1_PATH=/vaults/MonVault
- VAULT_1_ATTACHMENTS_PATH=Assets/Images # Chemin relatif
- VAULT_1_SCAN_ATTACHMENTS=true # Activer le scan (défaut)
```
### Rescan manuel
Pour rescanner les images d'un vault après ajout/suppression :
```bash
curl -X POST http://localhost:2020/api/attachments/rescan/MonVault
```
---
## �📖 Utilisation
### Interface web
1. **Navigation** : Cliquez sur les vaults dans la sidebar pour les développer
2. **Recherche** : Utilisez la barre de recherche pour chercher dans toutes les vaults
3. **Tags** : Cliquez sur les tags pour filtrer les contenus
4. **Wikilinks** : Les liens `[[page]]` sont cliquables et navigables
5. **Images** : Toutes les syntaxes d'images Obsidian sont rendues automatiquement
6. **Thème** : Basculez entre thème clair/sombre avec l'icône 🌙/☀️
### Raccourcis clavier
| Action | Raccourci |
|--------|-----------|
| Recherche | `Ctrl + K` ou `/` |
| Toggle thème | `Ctrl + T` |
| Focus recherche | `Esc` |
---
## 🔌 API
ObsiGate expose une API REST complète :
| Endpoint | Description | Méthode |
|----------|-------------|---------|
| `/api/health` | Health check (status, version, stats) | GET |
| `/api/vaults` | Liste des vaults configurées | GET |
| `/api/browse/{vault}?path=` | Navigation dans les dossiers | GET |
| `/api/file/{vault}?path=` | Contenu rendu d'un fichier | GET |
| `/api/file/{vault}/raw?path=` | Contenu brut d'un fichier | GET |
| `/api/file/{vault}/download?path=` | Téléchargement d'un fichier | GET |
| `/api/file/{vault}/save?path=` | Sauvegarder un fichier | PUT |
| `/api/file/{vault}?path=` | Supprimer un fichier | DELETE |
| `/api/search?q=&vault=&tag=` | Recherche fulltext (legacy) | GET |
| `/api/search/advanced?q=&vault=&tag=&limit=&offset=&sort=` | Recherche avancée TF-IDF avec facettes et pagination | GET |
| `/api/suggest?q=&vault=&limit=` | Suggestions de titres de fichiers (autocomplétion) | GET |
| `/api/tags/suggest?q=&vault=&limit=` | Suggestions de tags (autocomplétion) | GET |
| `/api/tags?vault=` | Tags uniques avec compteurs | GET |
| `/api/index/reload` | Force un re-scan des vaults | GET |
| `/api/events` | Flux SSE temps réel (index updates, vault changes) | GET |
| `/api/vaults/add` | Ajouter une vault dynamiquement | POST |
| `/api/vaults/{name}` | Supprimer une vault | DELETE |
| `/api/vaults/status` | Statut détaillé des vaults (fichiers, watching) | GET |
| `/api/image/{vault}?path=` | Servir une image avec MIME type approprié | GET |
| `/api/attachments/rescan/{vault}` | Rescanner les images d'un vault | POST |
| `/api/attachments/stats?vault=` | Statistiques d'images indexées | GET |
| `/api/config` | Lire la configuration | GET |
| `/api/config` | Mettre à jour la configuration | POST |
| `/api/diagnostics` | Statistiques index, mémoire, moteur de recherche | GET |
> Tous les endpoints exposent des schémas Pydantic documentés. La doc interactive est disponible sur `/docs` (Swagger UI).
**Exemple d'utilisation :**
```bash
# Health check
curl http://localhost:2020/api/health
# Lister les vaults
curl http://localhost:2020/api/vaults
# Recherche simple (legacy)
curl "http://localhost:2020/api/search?q=recette&vault=all"
# Recherche avancée avec TF-IDF, facettes et pagination
curl "http://localhost:2020/api/search/advanced?q=recette%20tag:cuisine&vault=all&limit=20&offset=0&sort=relevance"
# Autocomplétion de titres
curl "http://localhost:2020/api/suggest?q=piz&vault=all"
# Autocomplétion de tags
curl "http://localhost:2020/api/tags/suggest?q=rec&vault=all"
# Obtenir un fichier
curl "http://localhost:2020/api/file/Recettes?path=pizza.md"
```
---
## 🔍 Recherche avancée
### Syntaxe de requête
| Opérateur | Description | Exemple |
|-----------|-------------|---------|
| `tag:` | Filtrer par tag | `tag:recette docker` |
| `#` | Raccourci tag | `#linux serveur` |
| `vault:` | Filtrer par vault | `vault:IT kubernetes` |
| `title:` | Filtrer par titre | `title:pizza` |
| `path:` | Filtrer par chemin | `path:recettes/soupes` |
| `"phrase exacte"` | Recherche de phrase | `tag:"multi mots"` |
Les opérateurs sont combinables : `tag:linux vault:IT serveur web` recherche "serveur web" dans le vault IT avec le tag linux.
### Raccourcis clavier
| Raccourci | Action |
|-----------|--------|
| `Ctrl+K` / `Cmd+K` | Focaliser la barre de recherche |
| `/` | Focaliser la recherche (hors champ texte) |
| `↑` / `↓` | Naviguer dans les suggestions |
| `Enter` | Sélectionner la suggestion active ou lancer la recherche |
| `Escape` | Fermer les suggestions / quitter la recherche |
### Fonctionnalités
- **TF-IDF** : Scoring basé sur la fréquence des termes pondérée par l'inverse de la fréquence documentaire
- **Boost titre** : Les correspondances dans le titre reçoivent un score 3× supérieur
- **Normalisation des accents** : `resume` trouve `résumé`, `elephant` trouve `éléphant`
- **Snippets surlignés** : Les termes trouvés sont encadrés par `` dans les extraits
- **Facettes** : Compteurs par vault et par tag dans les résultats
- **Pagination** : Navigation par pages de 50 résultats
- **Tri** : Par pertinence (TF-IDF) ou par date de modification
- **Chips visuels** : Les filtres actifs sont affichés comme des chips colorés supprimables
- **Historique** : Les 50 dernières recherches sont mémorisées en localStorage
---
## 🔧 Dépannage
### Problèmes courants
**Port déjà utilisé :**
```bash
# Vérifier qui utilise le port
sudo netstat -tulpn | grep 2020
# Changer le port dans docker-compose.yml
ports:
- "2021:8080"
```
**Vault non trouvée :**
- Vérifiez que les chemins sont absolus
- Assurez-vous que les permissions permettent la lecture
- Redémarrez le conteneur après modification
**Build échoue :**
```bash
# Nettoyer et rebuild
docker system prune -f
docker-compose down
docker build --no-cache -t obsigate:latest .
docker-compose up -d
```
**Logs pour debugging :**
```bash
# Logs en temps réel
docker-compose logs -f obsigate
# Logs détaillés
docker-compose logs --tail=100 obsigate
```
---
## ⚡ Performance
| Métrique | Estimation |
|----------|------------|
| **Indexation** | ~1–2s pour 1 000 fichiers markdown |
| **Recherche avancée** | < 10ms pour la plupart des requêtes (index inversé + TF-IDF) |
| **Résolution wikilinks** | O(1) via table de lookup |
| **Mémoire** | ~80–150MB par 1 000 fichiers (contenu capé à 100 KB/fichier) |
| **Image Docker** | ~180MB (multi-stage, sans outils de build) |
| **CPU** | Non-bloquant ; recherche offloadée sur thread pool dédié |
### Paramètres recommandés par taille de vault
| Taille | Fichiers | `search_workers` | `prefix_max_expansions` | `max_content_size` |
|--------|----------|-------------------|--------------------------|---------------------|
| Petit | < 500 | 1 | 50 | 100 000 |
| Moyen | 500–5 000 | 2 | 50 | 100 000 |
| Grand | 5 000+ | 4 | 30 | 50 000 |
Ces paramètres sont configurables via l'interface (Settings) ou l'API `/api/config`.
### Optimisations clés (v1.2.0)
- **Index inversé avec set-intersection** : la recherche utilise les posting lists pour un retrieval O(k × postings) au lieu de O(N) scan complet
- **Prefix matching par recherche binaire** : O(log V + k) au lieu de O(V) scan linéaire du vocabulaire
- **ThreadPoolExecutor** : les fonctions de recherche CPU-bound sont offloadées du event loop asyncio
- **Race condition guard** : `currentSearchId` + `AbortController` empêchent le rendu de résultats obsolètes
- **Progress bar** : barre de progression animée pendant la recherche
- **Search timeout** : abandon automatique après 30s (configurable)
- **Query time display** : temps serveur affiché dans les résultats (`query_time_ms`)
- **Staleness detection fix** : utilisation d'un compteur de génération au lieu de `id(index)` pour détecter les changements d'index
### Optimisations v1.1.0
- **Recherche sans I/O** : le contenu des fichiers est mis en cache dans l'index mémoire
- **Scoring multi-facteurs** : titre exact (+20), titre partiel (+10), chemin (+5), tag (+3), fréquence contenu (x1 par occurrence, capé à 10)
- **Rendu Markdown singleton** : le renderer mistune est instancié une seule fois
- **AbortController** : les requêtes de recherche obsolètes sont annulées côté client
- **Debounced icon rendering** : `lucide.createIcons()` est batché via `requestAnimationFrame`
---
## 🛡️ Sécurité
- **Path traversal** : tous les endpoints fichier valident que le chemin résolu reste dans la vault
- **Utilisateur non-root** : le conteneur Docker tourne sous l'utilisateur `obsigate`
- **Volumes read-only** : les vaults sont montées en `:ro` par défaut dans docker-compose
---
## 🏗️ Stack technique
- **Backend** : Python 3.11 + FastAPI 0.110 + Uvicorn
- **File Watcher** : watchdog 4.x (inotify natif + fallback polling)
- **Frontend** : Vanilla JS + HTML + CSS (zéro framework, zéro build)
- **Rendu Markdown** : mistune 3.x
- **Image Docker** : python:3.11-slim (multi-stage)
- **Base de données** : Aucune (index en mémoire uniquement)
- **Architecture** : SPA + API REST + SSE
---
## 🏠 Architecture
```
┌─────────────────┐ ┌─────────────────────────────────────────┐
│ Navigateur │◄───►│ FastAPI (backend/main.py) │
│ (SPA) │ REST │ │
│ │ │ ┌──────────────┐ ┌──────────────┐ │
│ app.js │ │ │ indexer.py │ │ search.py │ │
│ style.css │ │ │ (scan+cache)│ │ (in-memory) │ │
│ index.html │ │ └───────┬──────┘ └──────┬───────┘ │
└─────────────────┘ │ │ │ │
│ └──────┬───────┘ │
│ │ │
│ ┌────────┴─────────┐ │
│ │ Index en mémoire │ │
│ │ (fichiers, tags, │ │
│ │ contenu, lookup)│ │
│ └──────────────────┘ │
└─────────────────────────────────────────┘
┌───────────────────────────────┐
│ Filesystem (vaults montées) │
│ /vaults/Recettes (ro) │
│ /vaults/IT (ro) │
└───────────────────────────────┘
```
**Flux de données :**
1. Au démarrage, `indexer.py` scanne tous les vaults en parallèle (thread pool)
2. Le contenu, les tags (YAML + inline) et les métadonnées sont mis en cache en mémoire
3. Une table de lookup O(1) est construite pour la résolution des wikilinks
4. `watcher.py` démarre la surveillance des fichiers (watchdog natif ou polling)
5. Les modifications détectées déclenchent une mise à jour incrémentale de l'index
6. Les changements sont notifiés au frontend via Server-Sent Events (SSE)
7. Les requêtes de recherche utilisent l'index en mémoire (zéro I/O disque)
8. Le frontend SPA communique via REST + SSE et gère l'état côté client
---
## 📝 Développement
### Structure du projet
```
ObsiGate/
├── backend/ # API FastAPI
│ ├── main.py # Endpoints, Pydantic models, rendu markdown
│ ├── indexer.py # Scan des vaults, index en mémoire, lookup table
│ ├── search.py # Moteur de recherche fulltext avec scoring
│ ├── watcher.py # Surveillance fichiers (watchdog + debounce)
│ └── requirements.txt
├── frontend/ # Interface web (Vanilla JS, zéro framework)
│ ├── index.html # Page SPA + modales (aide, config, éditeur)
│ ├── app.js # Logique SPA, gestion d'état, API client
│ └── style.css # Styles (CSS variables, thèmes, responsive)
├── Dockerfile # Multi-stage, healthcheck, non-root
├── docker-compose.yml # Déploiement avec healthcheck
├── build.sh # Build multi-platform (amd64/arm64/arm/v7/i386)
└── CONTRIBUTING.md # Guide de contribution
```
### Contribuer
Voir [CONTRIBUTING.md](CONTRIBUTING.md) pour les détails.
---
## 📄 Licence
Ce projet est sous licence **MIT** - voir le fichier [LICENSE](LICENSE) pour les détails.
---
## 🤝 Support
- **Issues** : [git.dracodev.net/Projets/ObsiGate/issues](https://git.dracodev.net/Projets/ObsiGate/issues)
- **Documentation** : [git.dracodev.net/Projets/ObsiGate/wiki](https://git.dracodev.net/Projets/ObsiGate/wiki)
- **Auteur** : Bruno Beloeil
---
## 📝 Changelog
### v1.3.0 (2025)
**Synchronisation temps réel**
- Surveillance automatique des fichiers via `watchdog` avec fallback polling automatique
- Mise à jour incrémentale de l'index : ajout, modification, suppression et déplacement de fichiers sans rebuild complet
- Server-Sent Events (SSE) : endpoint `/api/events` pour notifications temps réel vers le frontend
- Badge de synchronisation dans le header avec indicateur visuel (connecté/déconnecté/syncing)
- Panel d'événements récents accessible en cliquant sur le badge
- Reconnexion SSE automatique avec backoff exponentiel
- Toast informatif à chaque mise à jour détectée
- Rafraîchissement automatique de la sidebar et du fichier courant si affecté
**Gestion dynamique des vaults**
- `POST /api/vaults/add` : ajouter une vault à chaud sans redémarrage
- `DELETE /api/vaults/{name}` : supprimer une vault de l'index
- `GET /api/vaults/status` : statut détaillé (fichiers, tags, état de surveillance)
- Les vaults ajoutées sont automatiquement surveillées par le watcher
**Configuration watcher**
- Nouveaux paramètres dans `config.json` : `watcher_enabled`, `watcher_use_polling`, `watcher_polling_interval`, `watcher_debounce`
- Section « Synchronisation automatique » dans la modal de configuration frontend
- Toggles pour activer/désactiver la surveillance et le mode polling
### v1.2.0 (2025)
**Performance (critique)**
- Réécriture du moteur `advanced_search()` : retrieval par set-intersection sur l'index inversé (O(k × postings) au lieu de O(N))
- Prefix matching par recherche binaire sur liste triée de tokens (O(log V + k) au lieu de O(V))
- Offload des fonctions de recherche CPU-bound via `ThreadPoolExecutor` (2 workers par défaut)
- Pré-calcul des expansions de préfixe pour éviter les recherches binaires répétées
- Fix du bug de staleness : `is_stale()` utilise un compteur de génération au lieu de `id(index)`
**Frontend**
- Guard contre les race conditions : `currentSearchId` vérifié après chaque `fetch` avant rendu
- Barre de progression animée pendant la recherche
- Timeout de recherche configurable (30s par défaut)
- Longueur minimale de requête configurable (2 caractères par défaut)
- Affichage du temps de requête serveur (`query_time_ms`) dans les résultats
- Pagination ajoutée sur l'endpoint legacy `/api/search` (params `limit`/`offset`)
**Configuration & Diagnostics**
- Nouveaux endpoints `GET/POST /api/config` pour la configuration persistante (`config.json`)
- Nouveau endpoint `GET /api/diagnostics` (stats index, mémoire, moteur de recherche)
- Page de configuration étendue : paramètres frontend (debounce, résultats/page, timeout) et backend (workers, boosts, expansions)
- Panel de diagnostics intégré dans la modal de configuration
- Boutons « Forcer réindexation » et « Réinitialiser » dans les paramètres
### v1.1.0 (2025)
**Sécurité**
- Protection path traversal sur tous les endpoints fichier
- Utilisateur non-root dans le conteneur Docker
- Dockerfile multi-stage (élimination des outils de build)
**Performance**
- Recherche fulltext en mémoire (zéro I/O disque par requête)
- Table de lookup O(1) pour la résolution des wikilinks
- Renderer mistune mis en cache (singleton)
- Scoring multi-facteurs (titre, chemin, tags, fréquence)
- `lucide.createIcons()` batché via `requestAnimationFrame`
- `AbortController` sur les requêtes de recherche
**Robustesse**
- Swap atomique de l'index (thread-safe) pendant le reload
- Extraction des tags inline (#tag) depuis le contenu markdown
- Modèles Pydantic sur tous les endpoints API
- Gestion d'erreurs avec toasts utilisateur (frontend)
- États de chargement pour la sidebar et le contenu
- Remplacement de `on_event` déprécié par `lifespan`
**Infrastructure**
- Endpoint `/api/health` pour monitoring
- Healthcheck Docker (Dockerfile + docker-compose)
- `build.sh` amélioré (variable version, checks, couleurs)
**Documentation**
- Docstrings complètes sur toutes les fonctions Python
- Schémas Pydantic documentés (Swagger UI auto-générée)
- README : sections Architecture, Performance, Sécurité, Changelog
- CONTRIBUTING.md ajouté
### v1.0.0 (2025)
- Version initiale
---
*Projet : ObsiGate | Version : 1.2.0 | Dernière mise à jour : 2025*