ObsiGate/README.md

855 lines
34 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.4.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/)
```
┌─────────────────────────────────────────────────────────┐
│ [🔍 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
![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` |
| `"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** | ~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.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*