ObsiGate/README.md

833 lines
33 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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.
[![Version](https://img.shields.io/badge/Version-1.7.0-blue.svg)]()
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Docker](https://img.shields.io/badge/Docker-Ready-blue.svg)](https://www.docker.com/)
[![Python](https://img.shields.io/badge/Python-3.11+-green.svg)](https://www.python.org/)
[![CI/CD](https://img.shields.io/badge/CI%2FCD-Gitea_Actions-green.svg)](https://git.dracodev.net/Projets/ObsiGate/actions)
```
┌─────────────────────────────────────────────────────────┐
│ [🔍 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 & déploiement avec build.sh](#-build--déploiement-avec-buildsh)
- [Utilisation](#-utilisation)
- [API](#-api)
- [Performance](#-performance)
- [Dépannage](#-dépannage)
- [Stack technique](#-stack-technique)
- [Changelog](#-changelog)
---
## ✨ Fonctionnalités
- **🤖 AI Editor intégré** — Éditeur CodeMirror 6 avec toolbar IA : amélioration, correction, traduction, génération, réécriture personnalisée, toolbox (liste, tableau, frontmatter, canvas) — multi-provider DeepSeek/OpenRouter/Gemini
- **🗺️ Vue graphe interactive** — Canvas force-directed avec Barnes-Hut O(n log n), filtres (tag, type), profondeur, mode focus, historique de navigation ←→↑, export PNG, aperçu au survol (Ctrl+click)
- **🗂️ 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 stemming français, 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:`, `ext:` 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é** : Rate limiting, audit log, backup automatique, redaction de secrets, headers CSP, protection path traversal, utilisateur non-root
- **⚡ Performance** : Compression GZip, Cache-Control immutable, index inversé incrémental, search sans I/O disque
- **❤️ 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 et vos secrets
É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`)
Créez votre fichier `.env` pour l'authentification et les secrets :
```bash
cp .env.example .env
# Éditez .env pour configurer vos mots de passe et options
```
> **Ne committez jamais `.env` !** Il est dans `.gitignore`. Utilisez `.env.example` comme référence.
### 3. Lancer l'application
```bash
# Rendre le script exécutable (une seule fois)
chmod +x build.sh
# Build + déploiement en une commande
./build.sh
```
> **C'est tout !** `build.sh` vérifie Docker, contrôle vos volumes, construit l'image et démarre le conteneur automatiquement.
>
> Options utiles : `./build.sh --help` pour l'aide, `./build.sh --cache` pour un rebuild plus rapide, `./build.sh --build-only` pour construire sans démarrer.
### 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
# Persistance des données d'auth
- ./data:/app/data
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
# Auth (les secrets sont dans .env)
- OBSIGATE_AUTH_ENABLED=true
- OBSIGATE_ADMIN_USER=admin
env_file:
- .env # Contient OBSIGATE_ADMIN_PASSWORD et autres secrets
```
### Étape 3 : Build & déploiement
```bash
# Utilisez le script de build automatisé (recommandé)
chmod +x build.sh
./build.sh
```
Le script `build.sh` gère tout : vérification des prérequis, validation des volumes, construction de l'image et démarrage.
**Alternative manuelle :**
```bash
docker compose build --no-cache
docker compose up -d
```
> **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
1. **Copiez le template `.env`** :
```bash
cp .env.example .env
```
2. **Éditez `.env`** et décommentez/configurez les variables :
```bash
OBSIGATE_AUTH_ENABLED=*** OBSIGATE_ADMIN_USER=admin
OBSIGATE_ADMIN_PASSWORD=votre_mot_de_passe # Laissez vide = auto-généré (voir logs)
# OBSIGATE_SECURE_COOKIES=false # true si derrière HTTPS
```
3. **Dans `docker-compose.yml`**, assurez-vous d'avoir :
```yaml
env_file:
- .env
```
> **Ne mettez jamais de mot de passe dans `docker-compose.yml` !** Utilisez toujours `.env`.
### 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` |
| `OBSIGATE_ACCESS_TOKEN_TTL` | Durée de vie token JWT (secondes) | `3600` |
| `OBSIGATE_REFRESH_TOKEN_TTL` | Durée de vie refresh token (secondes) | `2592000` |
| `OBSIGATE_LOGIN_MAX_ATTEMPTS` | Tentatives de login max par IP | `10` |
| `OBSIGATE_LOGIN_WINDOW_SECONDS` | Fenêtre de rate limiting (secondes) | `900` |
>Toutes ces variables sont documentées dans `.env.example`.
### 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
./build.sh
```
### Méthode 2 : Hot-reload (recommandé)
1. **Ajoutez le volume et les variables** comme ci-dessus
2. **Appliquez les changements** :
```bash
./build.sh
```
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 & déploiement avec build.sh
Le script `build.sh` est la méthode recommandée pour construire et déployer ObsiGate.
### Utilisation de base
```bash
chmod +x build.sh # une seule fois
./build.sh # build from scratch + démarrage
```
### Options disponibles
| Option | Description |
|--------|-------------|
| `--help`, `-h` | Affiche l'aide complète |
| `--build-only` | Construit l'image sans démarrer le conteneur |
| `--no-cache` | Rebuild complet sans cache Docker **(défaut)** |
| `--cache` | Utilise le cache Docker (plus rapide si peu de changements) |
| `--progress=plain` | Sortie verbeuse (recommandé pour le debug) |
| `--progress=tty` | Sortie interactive avec barres de progression |
### Ce que fait le script
1. **Vérifie** que Docker et Docker Compose sont installés (avec numéros de version)
2. **Valide** la présence et la syntaxe du fichier `docker-compose.yml`
3. **Vérifie chaque volume** monté : avertit si un répertoire source n'existe pas
4. **Construit** l'image Docker via `docker compose build` (multi-stage, ~180MB)
5. **Démarre** le conteneur via `docker compose up -d`
6. **Affiche** le statut du conteneur puis les logs en temps réel
### Exemples
```bash
# Build propre + démarrage (recommandé après changement de code)
./build.sh
# Rebuild rapide avec cache (changements mineurs frontend uniquement)
./build.sh --cache
# Vérifier le build sans démarrer (pratique pour tester avant mise en prod)
./build.sh --build-only
# Sortie verbeuse pour débugger un échec de build
./build.sh --progress=plain
```
### Arrêter / redémarrer
```bash
docker compose down # Arrêter le conteneur
docker compose up -d # Redémarrer sans rebuild
docker compose logs -f # Voir les logs
```
---
## <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
![alt text](path/to/image.png)
```
### 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` |
| `ext:<type>` | Filtrer par type de fichier | `ext:md kubernetes` |
| `"phrase exacte"` | Recherche de phrase | `tag:"multi mots"` |
Exemples de filtre par extension : `ext:sh` pour les scripts bash, `ext:py` pour les scripts Python, `ext:md` pour les fichiers Markdown.
Les opérateurs sont combinables : `tag:linux vault:IT ext:md serveur web` recherche "serveur web" dans les fichiers Markdown du 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 le cache Docker et rebuild
docker system prune -f
docker compose down
./build.sh --progress=plain
# Si l'échec persiste, vérifier les logs de build
./build.sh --progress=plain 2>&1 | tee build.log
```
**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** | ~12s 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** | ~80150MB 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 | 5005 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.5.0 (Quick Wins 2026-05-27)
- **Compression GZip** : middleware FastAPI compresse toutes les réponses >1KB (~70% de bande passante économisée)
- **Cache-Control immutable** : les assets statiques (`/static/*`) sont cachés 1 an par le navigateur
- **`.dockerignore`** : contexte de build Docker minimal (pas de `.git`, `docs/`, `*.md`, `__pycache__`)
- **InvertedIndex incrémental** : hooks `add_document`/`remove_document`, plus de rebuild O(N) à chaque mutation
### 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
- **Rate limiting** : 10 tentatives de login max par IP sur 15 minutes + lockout par compte (5 tentatives)
- **Audit log** : toutes les écritures, suppressions et changements de config sont journalisés dans `data/audit.log` (JSON lines, rotation 10 MB)
- **Backup automatique** : chaque modification ou suppression de fichier est sauvegardée dans `.obsigate-backup/` avec timestamp
- **Secret redaction** : masquage automatique des JWT, clés API, tokens et connection strings dans les aperçus
- **Utilisateur non-root** : le conteneur Docker tourne sous l'utilisateur `obsigate` (UID 1000)
- **Volumes read-only** : les vaults sont montées en `:ro` par défaut dans docker-compose
- **Secrets dans `.env`** : les mots de passe et tokens ne sont jamais dans `docker-compose.yml`
- **Atomic writes** : les fichiers de données (users.json, shares.json, webhooks.json) utilisent tmp+replace
---
## 🏗️ Stack technique
- **Backend** : Python 3.11 + FastAPI 0.110 + Uvicorn
- **Auth** : python-jose (JWT HS256) + argon2-cffi (Argon2id)
- **Security** : rate limiting (IP + account lockout), audit logging, secret redaction, atomic writes
- **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
- **PDF Export** : WeasyPrint 60+
- **Compression** : GZip middleware (FastAPI), Cache-Control immutable pour les assets
- **Image Docker** : python:3.11-slim (multi-stage), `.dockerignore` pour contexte minimal
- **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 & déploiement automatisé (docker compose build + up)
└── 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
Consultez le [CHANGELOG.md](./CHANGELOG.md) pour l'historique complet de toutes les versions (v1.0.0 → v1.7.0).
---
*Projet : ObsiGate | Version : 1.7.0 | Dernière mise à jour : Mai 2026*