pip-audit bloquait sur PYSEC-2026-3910 (outlines) et PYSEC-2026-3911 (XForm), toutes deux atteignables via backend/pdf_reader.py. Le plancher pypdf>=4.0 ne protégeait rien : l'image Act du runner embarque 6.16.0 dans sa toolcache Python, donc pip répondait « already satisfied » sans jamais aligner. Au passage, le garde-fou TestSemgrepStep était en régression depuis la désactivation de semgrep (v2.39.9) et aurait rougi le job `test` : il vérifie désormais que l'étape n'exécute que son avertissement et que bandit et pip-audit restent bloquants. Nouveau TestDependencySecurityFloors pour verrouiller les planchers de sécurité (contre-preuve : pypdf remis à >=4.0). 🤖 Generated with Codebuff Co-Authored-By: Codebuff <[email protected]>
984 lines
45 KiB
Markdown
984 lines
45 KiB
Markdown
# ObsiGate
|
||
|
||
> **Version française** — ce document est le miroir synchronisé de [README.md](README.md) (référence complète). Dernière synchronisation : septembre 2026.
|
||
|
||
**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/)
|
||
[](https://git.dracodev.net/Projets/ObsiGate/actions)
|
||
|
||

|
||
|
||
> Interface web d'ObsiGate : sidebar multi-vault, recherche globale, statistiques et raccourcis.
|
||
|
||
---
|
||
|
||
## 📚 Guides
|
||
|
||
Les **guides d'utilisation** pas à pas se trouvent dans [`docs/GUIDES/`](docs/GUIDES/) :
|
||
|
||
| Guide | Contenu |
|
||
|---|---|
|
||
| 🚀 [Prise en main](docs/GUIDES/PRISE_EN_MAIN.md) | Premier lancement, interface, navigation, vaults, raccourcis |
|
||
| 🔍 [Recherche, PDF, Excel & Excalidraw](docs/GUIDES/RECHERCHE_PDF_EXCALIDRAW.md) | Syntaxe de requête, recherche sémantique, lecteurs PDF/Excel, diagrammes |
|
||
| 🤖 [Assistant IA & Forge](docs/GUIDES/ASSISTANT_IA_FORGE.md) | Fournisseurs, éditeur IA, BooksLM, Forge, commandes `@` / `/` |
|
||
| 📝 [Édition & collaboration](docs/GUIDES/COLLABORATION.md) | Édition simultanée, curseurs distants, persistance |
|
||
| 📱 [PWA & hors-ligne](docs/GUIDES/PWA_HORS_LIGNE.md) | Installation, cache hors-ligne, file de synchro, notifications |
|
||
| 🔌 [API REST](docs/GUIDES/API_REST.md) | Authentification, clés API, endpoints, exemples `curl`, SSE |
|
||
| 🧩 [Serveur MCP](docs/GUIDES/MCP.md) | Brancher Claude Desktop, Cursor, Cline… sur vos vaults |
|
||
| 🔒 [Authentification & sécurité](docs/GUIDES/AUTHENTIFICATION_SECURITE.md) | Utilisateurs, MFA, permissions par vault, durcissement |
|
||
| 🐳 [Déploiement Docker](docs/GUIDES/DEPLOIEMENT_DOCKER.md) | `docker-compose`, volumes, reverse proxy, mises à jour |
|
||
| 🖥️ [Desktop (Tauri)](docs/GUIDES/DESKTOP.md) | Installation, premier lancement, build depuis les sources, dépannage |
|
||
|
||
> Index complet : [`docs/GUIDES/README.md`](docs/GUIDES/README.md).
|
||
|
||
---
|
||
|
||
## 📋 Table des matières
|
||
|
||
- ✨ [Fonctionnalités](#fonctionnalites)
|
||
- 📚 [Guides](#guides)
|
||
- 🚀 [Prérequis](#prerequis)
|
||
- ⚡ [Installation rapide](#installation-rapide)
|
||
- ⚙️ [Configuration détaillée](#configuration-detaillee)
|
||
- 🌍 [Variables d'environnement](#variables-denvironnement)
|
||
- 🔒 [Authentification](#authentification)
|
||
- ➕ [Ajouter une nouvelle vault](#ajouter-une-nouvelle-vault)
|
||
- 🔨 [Build & déploiement avec build.sh](#build-deploiement-avec-buildsh)
|
||
- 🖼️ [Rendu d'images Obsidian](#rendu-dimages-obsidian)
|
||
- 🖥️ [Desktop (Tauri) — Application native](#desktop-tauri-application-native)
|
||
- 📖 [Utilisation](#utilisation)
|
||
- 👥 [Collaboration temps réel](#collaboration-temps-reel)
|
||
- 🔌 [API](#api)
|
||
- 🔍 [Recherche avancée](#recherche-avancee)
|
||
- 🔧 [Dépannage](#depannage)
|
||
- ⚡ [Performance](#performance)
|
||
- 🛡️ [Sécurité](#securite)
|
||
- 🏗️ [Stack technique](#stack-technique)
|
||
- 🏠 [Architecture](#architecture)
|
||
- 📝 [Développement](#developpement)
|
||
- 📄 [Licence](#licence)
|
||
- 🤝 [Support](#support)
|
||
- 📝 [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
|
||
- **🧩 Serveur MCP & agent IA** — Serveur Model Context Protocol intégré (`/mcp`) et assistant avec function calling : lisez, cherchez et modifiez vos vaults depuis Claude Desktop, Cursor… avec confirmations two-step, permissions par vault, rate limiting et redaction des secrets ([guide](docs/GUIDES/MCP.md))
|
||
- **👥 Collaboration temps réel** — Édition simultanée d'un même document (Yjs/CRDT) : curseurs distants colorés, indicateur de présence, fusion sans conflit, reconnexion automatique et persistance serveur ([détail](docs/features/collaboration.md))
|
||
- **📖 Guide d'utilisation intégré** — Aide complète en FR/EN accessible depuis le menu Options : interface, navigation, recherche, fichiers, IA, sécurité, API & intégrations (OpenAPI, MCP), hors-ligne, collaboration, desktop, plus une section **Architecture** avec diagramme Mermaid ; téléchargeable en **Markdown** et **PDF** dans la langue courante ([détail](docs/features/guide-coverage-105.md))
|
||
- **📱 Éditeur mobile natif** — Édition optimisée pour le tactile : barre d'outils Markdown flottante (gras/italique/code/liste/lien), bouton « Coller » persistant (contournement iOS), zoom par pincement et hauteur ajustable, raccourcis swipe (liens entrants / table des matières) et mode lecture plein écran avec navigation entre fichiers ([détail](docs/features/mobile-editor.md))
|
||
- **🗺️ 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 — plus une **recherche sémantique** optionnelle (embeddings `all-MiniLM-L6-v2`, fusion hybride TF-IDF + RRF) activable via le toggle `~` ([détail](docs/features/semantic-search.md))
|
||
- **💡 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
|
||
- **🎬 Audio & vidéo** : Lecteurs HTML5 intégrés (`.mp3 .wav .flac .mp4 .webm`…) avec streaming HTTP Range (lecture, déplacement, plein écran) et **lecture persistante** (mini-lecteur flottant / mini-fenêtre vidéo, retour au média ou arrêt à tout moment, contrôles écran verrouillé via Media Session), repli téléchargement si le format n'est pas lisible par le navigateur
|
||
- **🎨 Diagrammes Excalidraw** : Visualiseur/éditeur natif des fichiers `.excalidraw` et `.excalidraw.md` (iframe sandboxée, auto-save, thème clair/sombre, texte des diagrammes indexé pour la recherche)
|
||
- **📊 Tableurs Excel** : les fichiers `.xlsx` et `.xlsm` s'ouvrent dans un visualiseur dédié — un tableau par feuille avec onglets, en-têtes A1 et édition directe des cellules (`PUT /api/file/{vault}/xlsx/save`, backup automatique, écriture atomique), plus le téléchargement du fichier d'origine. Le visualiseur rend polices, couleurs, cellules fusionnées et volets figés, et offre navigation clavier, barre de formule, tri/filtre/recherche, export CSV, édition de la structure (feuilles, lignes, colonnes) et un tableau de bord du classeur (plages nommées, détection graphiques/TCD, stats par feuille) ; un `.csv` s'édite dans la même grille (RFC 4180) tandis que `.xls` et `.ods` s'ouvrent en lecture seule. Les classeurs contenant des éléments qu'ObsiGate ne peut pas conserver (valeurs calculées, segments, contrôles de formulaire, signature…) affichent un **avertissement** et demandent confirmation avant l'enregistrement ; une saisie commençant par `=` ou `@` est stockée comme texte sauf activation du bouton `f(x)`. L'assistant IA peut lister les feuilles, injecter un tableau borné dans son contexte, modifier des cellules et ajouter des lignes
|
||
- **🎨 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
|
||
- **🖥️ Application desktop native** : Tauri (Rust) + backend Python embarqué, sans Docker ni navigateur
|
||
- **🐳 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 `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
|
||
chmod +x build.sh # une seule fois
|
||
./build.sh # build + déploiement en une commande
|
||
```
|
||
|
||
> Options utiles : `./build.sh --help`, `./build.sh --cache` (rebuild rapide), `./build.sh --build-only` (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
|
||
3. **Vérifiez les permissions** : Docker doit pouvoir lire ces dossiers
|
||
|
||
### Étape 2 : Configuration docker-compose.yml
|
||
|
||
```yaml
|
||
services:
|
||
obsigate:
|
||
build:
|
||
context: .
|
||
image: obsigate:latest
|
||
container_name: obsigate
|
||
restart: unless-stopped
|
||
ports:
|
||
- "2020:8080" # Port local 2020 → Port conteneur 8080
|
||
volumes:
|
||
- /home/user/Documents/Obsidian-Recettes:/vaults/Recettes:ro
|
||
- /home/user/Documents/Obsidian-IT:/vaults/IT:ro
|
||
- ./data:/app/data # Persistance des données d'auth
|
||
environment:
|
||
- VAULT_1_NAME=Recettes
|
||
- VAULT_1_PATH=/vaults/Recettes
|
||
- VAULT_2_NAME=IT
|
||
- VAULT_2_PATH=/vaults/IT
|
||
- OBSIGATE_AUTH_ENABLED=true
|
||
- OBSIGATE_ADMIN_USER=admin
|
||
env_file:
|
||
- .env # Contient OBSIGATE_ADMIN_PASSWORD et autres secrets
|
||
```
|
||
|
||
### Étape 3 : Build & déploiement
|
||
|
||
```bash
|
||
chmod +x build.sh
|
||
./build.sh
|
||
```
|
||
|
||
**Alternative manuelle :**
|
||
|
||
```bash
|
||
docker compose build --no-cache
|
||
docker compose up -d
|
||
```
|
||
|
||
> **Compatibilité Docker** : l'image utilise une variante minimale d'`uvicorn` et `fastapi 0.110.3` afin d'éviter les dépendances optionnelles natives (`watchfiles`, `uvloop`, …) qui peuvent échouer au build sur Alpine, ARM ou i386.
|
||
|
||
---
|
||
|
||
## 🌍 Variables d'environnement
|
||
|
||
Les vaults sont configurées par paires `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` | Dossier d'attachements (optionnel) | `06_Boite_a_Outils/6.2_Attachments` |
|
||
| `VAULT_1_SCAN_ATTACHMENTS` | Scan d'images au démarrage (défaut : true) | `true` |
|
||
|
||
**Règles de nommage :** lettres, chiffres et tirets uniquement ; pas d'espaces ; le nom doit correspondre au chemin dans le conteneur.
|
||
|
||
---
|
||
|
||
## 🔒 Authentification
|
||
|
||
> **Désactivée par défaut** — Compatible avec toutes les installations existantes.
|
||
|
||
Système optionnel basé sur **JWT + Argon2id** avec contrôle d'accès par vault.
|
||
|
||
### Activer l'authentification
|
||
|
||
1. `cp .env.example .env`
|
||
2. Éditez `.env` :
|
||
```bash
|
||
OBSIGATE_AUTH_ENABLED=true
|
||
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` : `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 une seule fois dans les logs** :
|
||
|
||
```bash
|
||
docker-compose logs obsigate | grep -A4 "PREMIER"
|
||
```
|
||
|
||
### 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 / supprimer
|
||
docker exec obsigate python backend/create_admin.py list
|
||
docker exec obsigate python backend/create_admin.py delete alice
|
||
```
|
||
|
||
### Interface d'administration
|
||
|
||
Un compte **admin** connecté voit une icône 🛡️ dans le header : liste, création/édition/suppression d'utilisateurs, assignation des vaults, activation/désactivation de 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_ACCOUNT_MAX_ATTEMPTS` | Tentatives de login max par compte | `10` |
|
||
| `OBSIGATE_LOGIN_WINDOW_SECONDS` | Fenêtre de rate limiting (secondes) | `900` |
|
||
| `OBSIGATE_TRUST_PROXY` | Faire confiance à `X-Forwarded-For` pour l'IP client (reverse proxy) | `false` |
|
||
| `OBSIGATE_WEBHOOK_ALLOW_HTTP` | Autoriser les webhooks non HTTPS | `false` |
|
||
| `OBSIGATE_WEBHOOK_ALLOW_PRIVATE` | Autoriser les webhooks vers des adresses privées/boucle | `false` |
|
||
| `OBSIGATE_PDF_MAX_SIZE_MB` | Taille max des PDF extraits (text indexation) | `50` |
|
||
| `OBSIGATE_MEDIA_MAX_INLINE_MB` | Taille max pour la lecture audio/vidéo intégrée (au-delà : téléchargement) | `500` |
|
||
| `OBSIGATE_PDF_EXTRACT_TIMEOUT` | Timeout extraction PDF (secondes) | `30` |
|
||
| `OBSIGATE_TAVILY_API_KEY` / `OBSIGATE_BRAVE_API_KEY` / `OBSIGATE_SERPAPI_API_KEY` / `OBSIGATE_EXA_API_KEY` | Fournisseurs de recherche web à clé (essayés avant SearXNG) | — |
|
||
| `OBSIGATE_WEB_PROVIDERS` | Ordre des fournisseurs de recherche (ex. `brave,searxng`) | — |
|
||
| `OBSIGATE_WEB_RETRY` | Réessais réseau des outils web (backoff maison) | `1` |
|
||
| `OBSIGATE_WEB_CACHE_TTL` | Durée du cache SQLite des résultats web (secondes, `0` = off) | `900` |
|
||
| `OBSIGATE_GITEA_URL` / `OBSIGATE_GITEA_TOKEN` | Source connectée Gitea (outil `git_list_repos`…) | — |
|
||
| `OBSIGATE_GITHUB_TOKEN` | Jeton GitHub (outil `git_list_repos`…) | — |
|
||
|
||
> Ces clés peuvent aussi être saisies **depuis l'interface** (menu → Configurations →
|
||
> « Sources connectées & recherche ») : la valeur saisie est stockée dans `data/api_keys.json`
|
||
> et prime sur la variable d'environnement.
|
||
|
||
### Volume pour la persistance
|
||
|
||
Les données d'auth (`users.json`, `secret.key`) sont stockées dans `/app/data` :
|
||
|
||
```yaml
|
||
volumes:
|
||
- ./data:/app/data # Persistance des utilisateurs et clé JWT
|
||
```
|
||
|
||
---
|
||
|
||
## ➕ Ajouter une nouvelle vault
|
||
|
||
### Méthode 1 : Édition directe
|
||
|
||
1. `docker-compose down`
|
||
2. Ajoutez le volume : `- /nouveau/chemin/vault:/vaults/NouvelleVault:ro`
|
||
3. Ajoutez les variables : `VAULT_4_NAME=NouvelleVault` + `VAULT_4_PATH=/vaults/NouvelleVault`
|
||
4. Redémarrez : `./build.sh`
|
||
|
||
### Méthode 2 : Hot-reload (recommandé)
|
||
|
||
1. Ajoutez volume + variables comme ci-dessus
|
||
2. `./build.sh`
|
||
3. `curl http://localhost:2020/api/index/reload`
|
||
|
||
### Méthode 3 : API dynamique (sans redémarrage)
|
||
|
||
```bash
|
||
curl -X POST http://localhost:2020/api/vaults/add \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"name": "NouvelleVault", "path": "/vaults/NouvelleVault"}'
|
||
|
||
curl -X DELETE http://localhost:2020/api/vaults/NouvelleVault
|
||
```
|
||
|
||
---
|
||
|
||
## 🔨 Build & déploiement avec build.sh
|
||
|
||
### 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** Docker et Docker Compose (versions)
|
||
2. **Valide** `docker-compose.yml` (présence + syntaxe)
|
||
3. **Vérifie chaque volume** monté (avertit si la source n'existe pas)
|
||
4. **Construit** l'image Docker (multi-stage, ~180MB)
|
||
5. **Démarre** le conteneur (`docker compose up -d`)
|
||
6. **Affiche** statut + logs en temps réel
|
||
|
||
### Arrêter / redémarrer
|
||
|
||
```bash
|
||
docker compose down # Arrêter
|
||
docker compose up -d # Redémarrer sans rebuild
|
||
docker compose logs -f # Voir les logs
|
||
```
|
||
|
||
---
|
||
|
||
## 🖼️ Rendu d'images Obsidian
|
||
|
||
ObsiGate supporte **toutes les syntaxes d'images Obsidian** avec résolution intelligente multi-stratégies.
|
||
|
||
### Syntaxes supportées
|
||
|
||
1. **Markdown standard avec attributs HTML** : `[<img width="180" src="path/to/image.svg"/>](https://example.com)`
|
||
2. **Wiki-link embed chemin complet** : `![[06_Boite_a_Outils/6.2_Attachments/image.svg]]`
|
||
3. **Wiki-link embed nom de fichier** : `![[image.svg]]`
|
||
4. **Markdown standard** : ``
|
||
|
||
### Résolution intelligente (7 stratégies, par priorité)
|
||
|
||
1. Chemin absolu
|
||
2. Dossier d'attachements configuré (`VAULT_N_ATTACHMENTS_PATH`)
|
||
3. Index de démarrage (match unique)
|
||
4. Même répertoire que le fichier markdown
|
||
5. Racine du vault
|
||
6. Index de démarrage (match le plus proche)
|
||
7. Fallback : placeholder stylisé `[image not found: filename.ext]`
|
||
|
||
### Visionneuse & arborescence
|
||
|
||
Les images sont de plein droit des fichiers du vault : elles apparaissent dans
|
||
l'arborescence, sont indexées (nom + métadonnées, **jamais les octets**) et
|
||
s'ouvrent dans une **visionneuse dédiée** — zoom molette 0,1×–8×, pan au
|
||
glisser, double-clic pour réinitialiser, navigation ←/→ entre les images du
|
||
dossier (avec pellicule de miniatures WebP), panneau de métadonnées, lightbox
|
||
plein écran, ouverture de l'original et téléchargement. Le filtre de recherche
|
||
`ext:png`/`ext:jpg` est disponible. Formats décodables : PNG, JPEG, GIF, WebP,
|
||
BMP, ICO, SVG (SVG servi avec une politique CSP `sandbox`). **HEIC/HEIF**
|
||
(iPhone) n'est pas décodable par les navigateurs et n'est pas pris en charge.
|
||
|
||
### Configuration
|
||
|
||
```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
|
||
|
||
```bash
|
||
curl -X POST http://localhost:2020/api/attachments/rescan/MonVault
|
||
```
|
||
|
||
---
|
||
|
||
## 🖥️ Desktop (Tauri) — Application native
|
||
|
||
> 📖 Guide complet : [Desktop (Tauri)](docs/GUIDES/DESKTOP.md)
|
||
|
||
ObsiGate Desktop est une application native construite avec [Tauri](https://tauri.app/) (Rust + webview système). Elle embarque le backend Python et le frontend dans un exécutable standalone — zéro Docker, zéro ligne de commande.
|
||
|
||
> 🚧 **Version 2.0.0 — binaires en cours de stabilisation.** Pour l'instant, le build depuis les sources est recommandé.
|
||
|
||
### Fonctionnalités desktop natives
|
||
|
||
| Fonctionnalité | Web | Desktop |
|
||
|---|---|---|
|
||
| Accès fichiers local | Via upload | Natif (sélecteur dossier) |
|
||
| Thème système | Manuel | Auto (suit OS dark/light) |
|
||
| Notifications | Service Worker | Natif OS |
|
||
| Associations `.md` | ❌ | ✅ « Ouvrir avec ObsiGate » |
|
||
| Tray icon | ❌ | ✅ Barre des tâches |
|
||
| Auto-update | ❌ | ✅ Vérifie les releases Gitea |
|
||
| Mode hors-ligne | Limité | Complet (backend local) |
|
||
|
||
### Téléchargement (binaires pré-buildés)
|
||
|
||
Les releases sont publiées sur [Gitea](https://git.dracodev.net/Projets/ObsiGate/releases) :
|
||
|
||
| Plateforme | Format |
|
||
|---|---|
|
||
| **Linux** | `.deb` + `.AppImage` |
|
||
| **Windows** | `.msi` + `.exe` (NSIS) |
|
||
|
||
```bash
|
||
# Linux — .deb (Debian/Ubuntu/Deepin)
|
||
sudo dpkg -i obsigate_2.0.0_amd64.deb
|
||
# Linux — .AppImage (toute distrib)
|
||
chmod +x ObsiGate_2.0.0_amd64.AppImage && ./ObsiGate_2.0.0_amd64.AppImage
|
||
```
|
||
|
||
```cmd
|
||
REM Windows : double-cliquer sur ObsiGate_2.0.0_x64.msi (ou le setup NSIS)
|
||
```
|
||
|
||
### Démarrage
|
||
|
||
1. **Lancez l'application** depuis le menu ou la ligne de commande
|
||
2. Le backend Python démarre automatiquement sur `127.0.0.1:17890` (splash « Démarrage… » pendant le boot)
|
||
3. La fenêtre s'ouvre et charge l'interface ObsiGate
|
||
4. **Premier lancement** : sélectionnez le dossier de vos vaults Obsidian via le sélecteur natif
|
||
5. Pour fermer : icône tray → Quitter (arrêt propre du backend)
|
||
|
||
### Build depuis les sources
|
||
|
||
Guide détaillé : [desktop/README.md](./desktop/README.md).
|
||
|
||
#### Prérequis communs
|
||
|
||
| Outil | Version | Installation |
|
||
|---|---|---|
|
||
| Rust (cargo) | ≥ 1.75 | `rustup` |
|
||
| Tauri CLI | ≥ 2.0 | `cargo install tauri-cli` |
|
||
| Git | — | — |
|
||
| Dépendances système Linux | — | `sudo apt install libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev` |
|
||
|
||
> **Important — staging :** `tauri.conf.json` embarque `backend/**` et `frontend/**` **depuis le dossier `desktop/`**.
|
||
> Les scripts de build copient automatiquement `../backend` et `../frontend` dans `desktop/` avant `cargo tauri build`.
|
||
> Sans ce staging, le build échoue avec « glob pattern backend/**/* path not found ».
|
||
|
||
#### 🪟 Windows — `build-windows.bat`
|
||
|
||
```cmd
|
||
REM Prérequis (via Scoop) : rustup, curl, git
|
||
scoop install rustup curl git
|
||
rustup default stable
|
||
cargo install tauri-cli
|
||
|
||
cd desktop
|
||
build-windows.bat
|
||
```
|
||
|
||
Étapes du script :
|
||
|
||
1. Tue les processus Python résiduels (`taskkill /F /IM python.exe`)
|
||
2. Télécharge **Python 3.11 embed** (python.org) → `desktop\python-embed\` + activation de pip (`python311._pth`)
|
||
3. `pip install -r ..\backend\requirements.txt` dans l'embed
|
||
4. **Staging** : copie `..\backend` et `..\frontend` dans `desktop\`
|
||
5. `cargo tauri build --target x86_64-pc-windows-msvc --bundles nsis`
|
||
6. Copie `python-embed` à côté de l'exécutable (`target\x86_64-pc-windows-msvc\release\`) pour le mode dev local
|
||
7. Nettoie les dossiers stagés
|
||
|
||
→ **Artefact :** `desktop\target\x86_64-pc-windows-msvc\release\bundle\nsis\ObsiGate_2.0.0_x64-setup.exe`
|
||
|
||
#### 🐧 Linux — `build-linux.sh`
|
||
|
||
```bash
|
||
cd desktop
|
||
chmod +x build-linux.sh
|
||
./build-linux.sh
|
||
```
|
||
|
||
Étapes du script :
|
||
|
||
1. Vérifie Rust + Tauri CLI, installe les dépendances système (apt)
|
||
2. Crée un venv Python `desktop/python-embed/venv` + `pip install -r ../backend/requirements.txt`
|
||
3. **Staging** : copie `../backend` et `../frontend` dans `desktop/`
|
||
4. `cargo tauri build --target x86_64-unknown-linux-gnu --bundles deb,appimage`
|
||
5. Copie le runtime (`python-embed/`, `backend/`, `frontend/`) à côté de l'exécutable
|
||
|
||
→ **Artefacts :**
|
||
|
||
- `desktop/target/x86_64-unknown-linux-gnu/release/bundle/deb/obsigate_2.0.0_amd64.deb`
|
||
- `desktop/target/x86_64-unknown-linux-gnu/release/bundle/appimage/ObsiGate_2.0.0_amd64.AppImage`
|
||
|
||
### 🤖 Builds CI/CD — artefacts automatiques
|
||
|
||
**Oui** — le workflow [`.gitea/workflows/desktop-build.yml`](./.gitea/workflows/desktop-build.yml) construit les binaires desktop à chaque push sur `main` touchant `desktop/**`, `frontend/**` ou `backend/**` (et manuellement via `workflow_dispatch`), sur des **runners self-hosted** :
|
||
|
||
| Job | Runner | Artefacts (conservés 30 jours) |
|
||
|---|---|---|
|
||
| `build-windows` | `[self-hosted, windows, desktop]` | `desktop/target/release/bundle/msi/*.msi` |
|
||
| `build-linux` | `[self-hosted, linux, desktop]` | `*.AppImage` + `*.deb` |
|
||
|
||
- Les artefacts sont téléchargeables depuis la page **Actions** du run Gitea.
|
||
- La publication en **Gitea Release** est prévue sur les tags `v*` (étape `Publish to Gitea Release`).
|
||
- Le workflow web [`.gitea/workflows/ci.yml`](./.gitea/workflows/ci.yml) gère de son côté lint → tests → sécurité → build Docker → e2e Playwright.
|
||
|
||
### Architecture Desktop
|
||
|
||
```
|
||
┌────────────────────────────────────────────┐
|
||
│ Tauri (Rust) │
|
||
│ ├─ Webview (webview système) │
|
||
│ │ └─ Frontend (HTML/JS/CSS) │
|
||
│ └─ Sidecar Python │
|
||
│ └─ uvicorn backend.main:app │
|
||
│ └─ port 127.0.0.1:17890 │
|
||
└────────────────────────────────────────────┘
|
||
```
|
||
|
||
Cycle de vie : Tauri spawn le backend Python → health check → splash de démarrage → redirection vers la webview. À la fermeture : kill propre du backend.
|
||
|
||
---
|
||
|
||
## 📖 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` |
|
||
|
||
---
|
||
|
||
## 👥 Collaboration temps réel
|
||
|
||
> 📖 Guide complet : [Édition & collaboration](docs/GUIDES/COLLABORATION.md)
|
||
|
||
Plusieurs utilisateurs peuvent éditer le même document markdown simultanément (façon Google Docs) :
|
||
|
||
- **Fusion sans conflit** grâce à Yjs (CRDT) : deux personnes peuvent taper au même endroit, aucune
|
||
modification n'est perdue.
|
||
- **Curseurs distants colorés** et sélections visibles dans CodeMirror, avec le nom de chaque
|
||
utilisateur.
|
||
- **Indicateur de présence** dans l'en-tête de l'éditeur (avatars + statut de connexion).
|
||
- **Reconnexion automatique** (backoff exponentiel) : l'état est fusionné au retour.
|
||
- **Persistance serveur** : le document est écrit sur disque 2 s après la dernière modification.
|
||
- **Transport** : WebSocket `ws(s)://<hôte>/ws/collab/{vault}/{chemin}`, authentifié par cookie
|
||
`access_token` (ou `?token=`) et soumis au contrôle d'accès par vault.
|
||
|
||
Aucune configuration n'est nécessaire : ouvrez le même fichier dans deux navigateurs (ou deux
|
||
fenêtres) pour voir la collaboration en action.
|
||
|
||
---
|
||
|
||
## 🔌 API
|
||
|
||
> 📖 Guide complet : [API REST](docs/GUIDES/API_REST.md) · [Serveur MCP](docs/GUIDES/MCP.md)
|
||
|
||
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 (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 (+ `semantic=true` pour l'hybride) | 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/media/{vault}/thumb?path=&size=` | Miniature WebP (cache disque) | 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 ; doc interactive sur `/docs` (Swagger UI).
|
||
|
||
**Exemples :**
|
||
|
||
```bash
|
||
curl http://localhost:2020/api/health
|
||
curl http://localhost:2020/api/vaults
|
||
curl "http://localhost:2020/api/search/advanced?q=recette%20tag:cuisine&vault=all&limit=20&offset=0&sort=relevance"
|
||
curl "http://localhost:2020/api/suggest?q=piz&vault=all"
|
||
curl "http://localhost:2020/api/file/Recettes?path=pizza.md"
|
||
```
|
||
|
||
---
|
||
|
||
## 🔍 Recherche avancée
|
||
|
||
> 📖 Guide complet : [Recherche, PDF, Excel & Excalidraw](docs/GUIDES/RECHERCHE_PDF_EXCALIDRAW.md)
|
||
|
||
### 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"` |
|
||
|
||
Les opérateurs sont combinables : `tag:linux vault:IT ext:md serveur web`.
|
||
|
||
### Support PDF
|
||
|
||
Les fichiers PDF de vos vaults s'affichent en ligne dans le navigateur via le visualiseur PDF natif (iframe + `<embed>`).
|
||
Le visualiseur streame le fichier via HTTP Range (206 Partial Content) — les gros PDF se chargent progressivement.
|
||
Le texte est extrait à l'indexation (pypdf / pymupdf) — le contenu PDF est donc recherchable via la recherche full-text.
|
||
Filtrez avec `ext:pdf` pour restreindre les résultats aux PDF.
|
||
Les métadonnées (pages, titre, auteur) sont disponibles via `GET /api/file/{vault}/pdf/info` sans transférer le document.
|
||
|
||
**Limitations :** pas d'OCR (les PDF scannés ne sont pas recherchables), pas d'annotation, pas d'édition du PDF lui-même.
|
||
|
||
### Diagrammes Excalidraw
|
||
|
||
Les fichiers `.excalidraw` et `.excalidraw.md` s'ouvrent dans un éditeur visuel Excalidraw complet, intégré dans une iframe sandboxée — dessinez, modifiez et sauvegardez sans quitter ObsiGate.
|
||
Les modifications sont sauvegardées automatiquement (2 s) ou avec `Ctrl+S` ; l'éditeur suit le thème clair/sombre.
|
||
Le texte des éléments du diagramme est extrait à l'indexation : il est donc recherchable via la recherche full-text (`ext:excalidraw`).
|
||
Les fichiers créés avec le **plugin Obsidian Excalidraw** (y compris le format `.excalidraw.md` compressé) sont compatibles.
|
||
|
||
### 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 par fréquence pondérée des termes
|
||
- **Boost titre** : correspondances dans le titre ×3
|
||
- **Normalisation des accents** : `resume` trouve `résumé`
|
||
- **Snippets surlignés** (`<mark>`), **facettes** (compteurs par vault/tag), **pagination** (50/page), **tri** pertinence/date, **chips** de filtres, **historique** (50 recherches)
|
||
- **Recherche sémantique** (optionnelle) : le toggle `~` (ou `Alt+S`) fusionne le classement
|
||
TF-IDF avec un classement par embeddings (RRF). Fonctionne sans dépendance avec un provider de
|
||
hachage ; installez `backend/requirements-semantic.txt` et/ou renseignez `OBSIGATE_EMBEDDING_*`
|
||
pour de vrais embeddings `all-MiniLM-L6-v2`. Voir
|
||
[docs/features/semantic-search.md](docs/features/semantic-search.md).
|
||
|
||
---
|
||
|
||
## 🔧 Dépannage
|
||
|
||
**Port déjà utilisé :**
|
||
|
||
```bash
|
||
sudo netstat -tulpn | grep 2020
|
||
# Changer le port dans docker-compose.yml : ports: - "2021:8080"
|
||
```
|
||
|
||
**Vault non trouvée :** chemins absolus, permissions de lecture, redémarrer le conteneur après modification.
|
||
|
||
**Build échoue :**
|
||
|
||
```bash
|
||
docker system prune -f
|
||
docker compose down
|
||
./build.sh --progress=plain
|
||
# Si l'échec persiste :
|
||
./build.sh --progress=plain 2>&1 | tee build.log
|
||
```
|
||
|
||
**Logs pour debugging :**
|
||
|
||
```bash
|
||
docker compose logs -f obsigate
|
||
docker compose logs --tail=100 obsigate
|
||
```
|
||
|
||
**Desktop :** les logs du backend sont dans `%APPDATA%\ObsiGate\logs\backend.log` (Windows) / `~/.config/obsigate/logs/backend.log` (Linux).
|
||
|
||
---
|
||
|
||
## ⚡ 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) |
|
||
| **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 |
|
||
|
||
Configurables via l'interface (Settings) ou l'API `/api/config`.
|
||
|
||
### Optimisations clés
|
||
|
||
- **Index inversé avec set-intersection** + prefix matching par recherche binaire
|
||
- **ThreadPoolExecutor** : recherche CPU-bound hors de l'event loop asyncio
|
||
- **InvertedIndex incrémental** : hooks `add_document`/`remove_document`, plus de rebuild O(N)
|
||
- **Compression GZip** (~70% de bande passante économisée) + **Cache-Control immutable** (1 an)
|
||
- **Race condition guard** (`currentSearchId` + AbortController), progress bar, timeout 30s
|
||
- **Rendu Markdown singleton**, debounced icon rendering, recherche sans I/O disque
|
||
|
||
---
|
||
|
||
## 🛡️ Sécurité
|
||
|
||
> 📖 Guide complet : [Authentification & sécurité](docs/GUIDES/AUTHENTIFICATION_SECURITE.md)
|
||
|
||
- **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** : écritures/suppressions/config journalisées dans `data/audit.log` (JSON lines, rotation 10 MB)
|
||
- **Backup automatique** : chaque modification/suppression sauvegardée dans `.obsigate-backup/` avec timestamp
|
||
- **Secret redaction** : masquage automatique des JWT, clés API, tokens dans les aperçus
|
||
- **Utilisateur non-root** : conteneur Docker sous `obsigate` (UID 1000)
|
||
- **Volumes read-only** : vaults montées en `:ro` par défaut
|
||
- **Secrets dans `.env`** : jamais dans `docker-compose.yml`
|
||
- **Atomic writes** : tmp+replace pour users.json, shares.json, webhooks.json
|
||
|
||
---
|
||
|
||
## 🏗️ 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
|
||
- **PDF Export** : WeasyPrint 60+
|
||
- **Desktop** : Tauri v2 (Rust) + Python embarqué
|
||
- **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 │ ┌──────────────┐ ┌──────────────┐ │
|
||
│ │ │ │ indexer.py │ │ search.py │ │
|
||
│ app.js │ │ │ (scan+cache) │ │ (in-memory) │ │
|
||
│ style.css │ │ └───────┬────── └──────┬───────┘ │
|
||
│ index.html │ │ │ │ │
|
||
└─────────────────┘ │ ┌───┴──────────────┴───┐ │
|
||
│ │ Index en mémoire │ │
|
||
│ │ (fichiers, tags, │ │
|
||
│ │ contenu, lookup) │ │
|
||
│ └───────────────────────┘ │
|
||
└─────────────────────────────────────────┘
|
||
```
|
||
|
||
**Flux de données :**
|
||
|
||
1. Au démarrage, `indexer.py` scanne tous les vaults en parallèle (thread pool, non-bloquant)
|
||
2. Contenu, tags (YAML + inline) et métadonnées mis en cache en mémoire
|
||
3. Table de lookup O(1) pour la résolution des wikilinks
|
||
4. `watcher.py` surveille les fichiers (watchdog natif ou polling)
|
||
5. Modifications → mise à jour incrémentale de l'index
|
||
6. Changements notifiés au frontend via SSE
|
||
7. Recherche sur l'index mémoire (zéro I/O disque)
|
||
8. Le frontend SPA communique via REST + SSE
|
||
|
||
---
|
||
|
||
## 📝 Développement
|
||
|
||
### 🧪 Tests et CI — règles obligatoires (avant chaque push)
|
||
|
||
> ⚠️ **Règle absolue — contributeurs humains et agents IA** : ne jamais faire
|
||
> de `git push` sans avoir exécuté **localement** la séquence complète
|
||
> ci-dessous avec **100 % de succès**. Le push ne sert pas à découvrir les
|
||
> échecs : la CI est un filet de sécurité, pas un outil de diagnostic.
|
||
|
||
#### Vérifications locales avant commit
|
||
|
||
| Étape | Commande | Job CI équivalent |
|
||
|---|---|---|
|
||
| Lint backend | `ruff check backend/` | `lint` |
|
||
| Validation des imports frontend | `node tests/frontend/validate-imports.mjs` | `lint` |
|
||
| Tests unitaires frontend | `node tests/frontend/unit.test.mjs` | `lint` |
|
||
| Tests backend | `pytest tests/ -q` | `test` |
|
||
| **E2E Playwright** | `npm run test:e2e` (~10 min) | `e2e` |
|
||
|
||
#### Tests E2E locaux (`npm run test:e2e`)
|
||
|
||
Le script `scripts/run-e2e-local.sh` reproduit fidèlement les conditions du
|
||
job CI `e2e` (`.gitea/workflows/ci.yml`) :
|
||
|
||
- backend démarré nativement (venv `.venv-e2e`, Python 3.11 via `uv`) ;
|
||
- authentification désactivée (`OBSIGATE_AUTH_ENABLED=false`) ;
|
||
- fixtures `test_vault` (TestVault) et `test_dir` (TestDir) ;
|
||
- port `2029` (baseURL de `playwright.config.ts`) ;
|
||
- projet Playwright `chromium-desktop` uniquement (comme la CI).
|
||
|
||
Prérequis : `uv`, Node.js ≥ 20, navigateurs Playwright
|
||
(`npx playwright install chromium`). Le venv est créé automatiquement au
|
||
premier lancement. Docker n'est pas nécessaire.
|
||
|
||
```bash
|
||
npm run test:e2e # suite complète
|
||
bash scripts/run-e2e-local.sh --headed # navigateur visible
|
||
bash scripts/run-e2e-local.sh -g "reset panes" # filtre sur un test
|
||
```
|
||
|
||
Sous Windows, si `bash` n'est pas exploitable (WSL indisponible, git-bash
|
||
bloqué par une politique de contrôle d'application), utiliser le lanceur
|
||
PowerShell équivalent :
|
||
|
||
```powershell
|
||
npm run test:e2e:ps
|
||
pwsh -File scripts/run-e2e-local.ps1 -PlaywrightArgs @('-g','reset panes')
|
||
```
|
||
|
||
La suite doit se terminer sur **tous les tests passant** (60 actuellement),
|
||
sans échec ni dépendance aux retries. En cas d'échec : corriger et relancer
|
||
localement jusqu'à 100 %, puis seulement commiter.
|
||
|
||
#### Règles d'écriture des tests (leçon apprise)
|
||
|
||
- **Vérifier chaque sélecteur dans le DOM réel** avant de l'utiliser dans un
|
||
test. Bug réel rencontré : un test ciblait `#cp-input` alors que l'input de
|
||
la palette porte uniquement la classe `.cp-input` (aucun id) — le test ne
|
||
pouvait passer dans aucun environnement.
|
||
- Tout test nouveau ou modifié doit être **exécuté localement** (au minimum
|
||
avec `-g "nom du test"`) avant le push.
|
||
- Pas de contournements qui masquent une instabilité (`waitForTimeout`
|
||
arbitraires, fallback silencieux) : traiter la cause racine.
|
||
|
||
#### Commits et push
|
||
|
||
- Message de commit : sujet à l'impératif ≤ 50 caractères ; corps (72 col.)
|
||
uniquement s'il apporte une information utile.
|
||
- Un commit = un changement logique (ne pas mêler fix et refactoring).
|
||
- Push uniquement quand les 5 étapes locales sont vertes. Pipeline CI :
|
||
`lint → test → security → build → e2e`.
|
||
|
||
### 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 (JWT + Argon2id)
|
||
│ └── create_admin.py # CLI gestion utilisateurs
|
||
├── frontend/ # Interface web (Vanilla JS, zéro framework)
|
||
│ ├── index.html # Page SPA + écran de login + splash de boot
|
||
│ ├── js/ # Modules ES (app, viewer, editor, graph, …)
|
||
│ └── style.css # Styles (CSS variables, thèmes, responsive)
|
||
├── desktop/ # Application native Tauri (Rust + Python embarqué)
|
||
│ ├── src/main.rs # Shell natif : cycle de vie backend, tray, menus
|
||
│ ├── build-windows.bat
|
||
│ └── build-linux.sh
|
||
├── data/ # Données persistantes (créé au démarrage)
|
||
├── Dockerfile # Multi-stage, healthcheck, non-root
|
||
├── docker-compose.yml # Déploiement avec healthcheck et auth env vars
|
||
├── build.sh # Build & déploiement automatisé
|
||
└── docs/ # ROADMAP, guides, audits
|
||
```
|
||
|
||
### Contribuer
|
||
|
||
Voir [docs/CONTRIBUTING.md](./docs/CONTRIBUTING.md) pour les standards de code et
|
||
[docs/DELIVERY_WORKFLOW.md](./docs/DELIVERY_WORKFLOW.md) pour la méthode de livraison obligatoire.
|
||
|
||
---
|
||
|
||
## 📄 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)
|
||
|
||
---
|
||
|
||
## 📝 Changelog
|
||
|
||
Consultez le [CHANGELOG.md](./CHANGELOG.md) pour l'historique complet de toutes les versions (v1.0.0 → v2.39.10).
|
||
|
||
---
|
||
|
||
*Projet : ObsiGate | Version : 2.39.10 | Dernière mise à jour : Septembre 2026*
|