855 lines
34 KiB
Markdown
855 lines
34 KiB
Markdown
# 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)
|
||
- [🔒 Authentification](#-authentification)
|
||
- [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
|
||
- **🔒 Authentification** : JWT + Argon2id, sessions persistantes, contrôle d'accès par vault
|
||
- **🛡️ Sécurité** : Headers de sécurité (CSP, X-Frame-Options…), protection path traversal, utilisateur non-root
|
||
- **❤️ 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
|
||
|
||
---
|
||
|
||
## 🔒 Authentification
|
||
|
||
> **Désactivée par défaut** — Compatible avec toutes les installations existantes.
|
||
|
||
ObsiGate supporte un système d'authentification optionnel basé sur **JWT + Argon2id** avec contrôle d'accès par vault.
|
||
|
||
### Activer l'authentification
|
||
|
||
Dans `docker-compose.yml`, décommentez les variables d'auth :
|
||
|
||
```yaml
|
||
environment:
|
||
# ... vaults ...
|
||
- OBSIGATE_AUTH_ENABLED=true
|
||
# - OBSIGATE_ADMIN_USER=admin # Défaut: admin
|
||
# - OBSIGATE_ADMIN_PASSWORD= # Vide = mot de passe auto-généré (voir logs)
|
||
# - OBSIGATE_SECURE_COOKIES=false # true si derrière un reverse-proxy HTTPS
|
||
```
|
||
|
||
### Premier démarrage
|
||
|
||
Si aucun utilisateur n'existe, ObsiGate crée automatiquement un compte admin et **affiche le mot de passe dans les logs** :
|
||
|
||
```bash
|
||
docker-compose logs obsigate | grep -A4 "PREMIER"
|
||
```
|
||
|
||
```
|
||
============================================================
|
||
PREMIER DÉMARRAGE — Compte admin créé automatiquement
|
||
Utilisateur : admin
|
||
Mot de passe : xK9mQ3pLr7wN2jT5
|
||
CHANGEZ CE MOT DE PASSE dès la première connexion !
|
||
============================================================
|
||
```
|
||
|
||
### Gestion des utilisateurs via CLI
|
||
|
||
```bash
|
||
# Créer un utilisateur
|
||
docker exec obsigate python backend/create_admin.py create alice MonMotDePasse --role user --vaults Recettes IT
|
||
|
||
# Créer un admin avec accès total
|
||
docker exec obsigate python backend/create_admin.py create bob SecretPass --role admin --vaults "*"
|
||
|
||
# Lister les utilisateurs
|
||
docker exec obsigate python backend/create_admin.py list
|
||
|
||
# Supprimer un utilisateur
|
||
docker exec obsigate python backend/create_admin.py delete alice
|
||
```
|
||
|
||
### Interface d'administration
|
||
|
||
Lorsqu'un compte **admin** est connecté, une icône 🛡️ apparaît dans le header. Cliquer dessus ouvre le panneau d'administration permettant de :
|
||
|
||
- Lister tous les utilisateurs
|
||
- Créer / modifier / supprimer des utilisateurs
|
||
- Assigner les vaults accessibles par utilisateur
|
||
- Activer/désactiver des comptes
|
||
|
||
### Contrôle d'accès par vault
|
||
|
||
| Valeur vaults | Accès |
|
||
|---------------|-------|
|
||
| `["*"]` | Toutes les vaults (y compris futures) — défaut admin |
|
||
| `["Recettes", "IT"]` | Uniquement ces vaults |
|
||
| `[]` | Aucun accès |
|
||
|
||
### Variables d'environnement d'auth
|
||
|
||
| Variable | Description | Défaut |
|
||
|----------|-------------|--------|
|
||
| `OBSIGATE_AUTH_ENABLED` | Activer l'authentification | `false` |
|
||
| `OBSIGATE_ADMIN_USER` | Nom de l'admin auto-créé | `admin` |
|
||
| `OBSIGATE_ADMIN_PASSWORD` | Mot de passe admin (vide = auto-généré) | *(auto)* |
|
||
| `OBSIGATE_SECURE_COOKIES` | Cookie `Secure` (HTTPS uniquement) | `false` |
|
||
|
||
### Volume pour la persistance
|
||
|
||
Les données d'auth (`users.json`, `secret.key`) sont stockées dans `/app/data`. Ajoutez un volume pour les persister :
|
||
|
||
```yaml
|
||
volumes:
|
||
# vaults...
|
||
- ./data:/app/data # Persistance des utilisateurs et clé JWT
|
||
```
|
||
|
||
---
|
||
|
||
## ➕ 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 .
|
||
```
|
||
|
||
---
|
||
|
||
## <20>️ 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
|
||
[<img width="180" height="60" src="path/to/image.svg"/>](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
|
||
```
|
||
|
||
---
|
||
|
||
## <20>📖 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 | Auth |
|
||
|----------|-------------|---------|------|
|
||
| `/api/health` | Health check (status, version, stats) | GET | Non |
|
||
| `/api/auth/status` | Statut auth (activé, utilisateurs présents) | GET | Non |
|
||
| `/api/auth/login` | Connexion (retourne access token + cookie refresh) | POST | Non |
|
||
| `/api/auth/refresh` | Renouveler l'access token via cookie refresh | POST | Cookie |
|
||
| `/api/auth/logout` | Déconnexion + révocation refresh token | POST | Oui |
|
||
| `/api/auth/me` | Infos utilisateur courant | GET | Oui |
|
||
| `/api/auth/change-password` | Changer son mot de passe | POST | Oui |
|
||
| `/api/auth/admin/users` | Lister / créer des utilisateurs | GET/POST | Admin |
|
||
| `/api/auth/admin/users/{u}` | Modifier / supprimer un utilisateur | PATCH/DELETE | Admin |
|
||
| `/api/vaults` | Liste des vaults (filtrée par permissions) | GET | Oui |
|
||
| `/api/browse/{vault}?path=` | Navigation dans les dossiers | GET | Oui |
|
||
| `/api/file/{vault}?path=` | Contenu rendu d'un fichier | GET | Oui |
|
||
| `/api/file/{vault}/raw?path=` | Contenu brut d'un fichier | GET | Oui |
|
||
| `/api/file/{vault}/download?path=` | Téléchargement d'un fichier | GET | Oui |
|
||
| `/api/file/{vault}/save?path=` | Sauvegarder un fichier | PUT | Oui |
|
||
| `/api/file/{vault}?path=` | Supprimer un fichier | DELETE | Oui |
|
||
| `/api/search/advanced` | Recherche avancée TF-IDF | GET | Oui |
|
||
| `/api/suggest` / `/api/tags/suggest` | Autocomplétion | GET | Oui |
|
||
| `/api/tags?vault=` | Tags uniques avec compteurs | GET | Oui |
|
||
| `/api/index/reload` | Force un re-scan des vaults | GET | Admin |
|
||
| `/api/events` | Flux SSE temps réel | GET | Oui |
|
||
| `/api/vaults/add` / `/api/vaults/{name}` | Gestion dynamique des vaults | POST/DELETE | Admin |
|
||
| `/api/image/{vault}?path=` | Servir une image | GET | Oui |
|
||
| `/api/config` | Lire / écrire la configuration | GET/POST | Oui/Admin |
|
||
| `/api/diagnostics` | Statistiques index et mémoire | GET | Admin |
|
||
|
||
> Quand `OBSIGATE_AUTH_ENABLED=false`, tous les endpoints sont accessibles sans token.
|
||
|
||
> 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:<nom>` | Filtrer par tag | `tag:recette docker` |
|
||
| `#<nom>` | Raccourci tag | `#linux serveur` |
|
||
| `vault:<nom>` | Filtrer par vault | `vault:IT kubernetes` |
|
||
| `title:<texte>` | Filtrer par titre | `title:pizza` |
|
||
| `path:<texte>` | 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 `<mark>` 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
|
||
- **Auth** : python-jose (JWT HS256) + argon2-cffi (Argon2id)
|
||
- **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)
|
||
- **Stockage utilisateurs** : JSON local (`data/users.json`) — aucune base de données
|
||
- **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)
|
||
│ ├── auth/ # Module d'authentification
|
||
│ │ ├── password.py # Argon2id hashing
|
||
│ │ ├── jwt_handler.py # Tokens JWT + révocation
|
||
│ │ ├── user_store.py # CRUD users.json (atomique)
|
||
│ │ ├── middleware.py # Dépendances FastAPI auth
|
||
│ │ └── router.py # Endpoints /api/auth/*
|
||
│ ├── create_admin.py # CLI gestion utilisateurs
|
||
│ └── requirements.txt
|
||
├── frontend/ # Interface web (Vanilla JS, zéro framework)
|
||
│ ├── index.html # Page SPA + écran de login
|
||
│ ├── app.js # Logique SPA, AuthManager, AdminPanel
|
||
│ └── style.css # Styles (CSS variables, thèmes, responsive)
|
||
├── data/ # Données persistantes (créé au démarrage)
|
||
│ ├── users.json # Utilisateurs (hashés Argon2id)
|
||
│ └── secret.key # Clé secrète JWT (512 bits)
|
||
├── Dockerfile # Multi-stage, healthcheck, non-root
|
||
├── docker-compose.yml # Déploiement avec healthcheck et auth env vars
|
||
├── 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.4.0 (2026)
|
||
|
||
**Authentification & Contrôle d'accès**
|
||
- Système d'authentification JWT (HS256) optionnel — désactivé par défaut, activation via `OBSIGATE_AUTH_ENABLED=true`
|
||
- Hachage des mots de passe **Argon2id** (paramètres OWASP 2024 : time_cost=2, mem=64MB)
|
||
- Sessions avec **access token (1h)** + **refresh token (7j, HttpOnly cookie)** et révocation
|
||
- Contrôle d'accès **par vault** : chaque utilisateur accède uniquement à ses vaults assignées
|
||
- Bootstrap automatique du compte admin au premier démarrage (mot de passe généré et affiché dans les logs)
|
||
- CLI de gestion des utilisateurs : `python backend/create_admin.py create|list|delete`
|
||
- Interface d'administration dans la SPA : création/modification/suppression d'utilisateurs avec sélection de vaults
|
||
- Protection brute-force : verrouillage 15 min après 5 tentatives échouées
|
||
- Réponses en temps constant sur le login pour éviter l'énumération d'utilisateurs
|
||
- Token de rafraîchissement révocable (persisté dans `data/revoked_tokens.json`)
|
||
- Clé secrète JWT auto-générée (512 bits) et persistée dans `data/secret.key`
|
||
|
||
**Sécurité (headers)**
|
||
- `Content-Security-Policy` sur toutes les réponses
|
||
- `X-Frame-Options: SAMEORIGIN`
|
||
- `X-Content-Type-Options: nosniff`
|
||
- `X-XSS-Protection: 1; mode=block`
|
||
- `Referrer-Policy: strict-origin-when-cross-origin`
|
||
|
||
**Frontend**
|
||
- Écran de connexion avec glassmorphism (avant chargement de l'app si auth activée)
|
||
- Menu utilisateur dans le header (nom affiché, bouton admin, déconnexion)
|
||
- Rafraîchissement silencieux de l'access token (via cookie refresh)
|
||
- Compatibilité totale : `OBSIGATE_AUTH_ENABLED=false` → comportement identique à v1.3.x
|
||
|
||
### 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.4.0 | Dernière mise à jour : 2026*
|