Implement multi-stage Docker build with security hardening, add health check endpoint, optimize in-memory search with O(1) wikilink lookup, extract inline tags from markdown content, and enhance documentation with architecture diagrams and performance metrics

This commit is contained in:
2026-03-22 19:03:34 -04:00
parent d9add8dcba
commit d311a09527
11 changed files with 1097 additions and 182 deletions
+136 -25
View File
@@ -2,6 +2,7 @@
**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.1.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/)
@@ -25,6 +26,7 @@
## 📋 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)
@@ -33,8 +35,10 @@
- [Build multi-platform](#-build-multi-platform)
- [Utilisation](#-utilisation)
- [API](#-api)
- [Performance](#-performance)
- [Dépannage](#-dépannage)
- [Stack technique](#-stack-technique)
- [Changelog](#-changelog)
---
@@ -48,7 +52,8 @@
- **🎨 Syntax highlight** : Coloration syntaxique des blocs de code
- **🌓 Thème clair/sombre** : Toggle persisté en localStorage
- **🐳 Docker multi-platform** : linux/amd64, linux/arm64, linux/arm/v7, linux/386
- **🔒 Lecture seule** : Aucune écriture sur vos vaults (sécurité maximale)
- **🔒 Sécurité** : Protection contre le path traversal, utilisateur non-root dans Docker
- **❤️ Healthcheck** : Endpoint `/api/health` intégré pour Docker et monitoring
---
@@ -270,15 +275,25 @@ ObsiGate expose une API REST complète :
| Endpoint | Description | Méthode |
|----------|-------------|---------|
| `/api/health` | Health check (status, version, stats) | GET |
| `/api/vaults` | Liste des vaults configurées | GET |
| `/api/browse/{vault}?path=` | Navigation dans les dossiers | GET |
| `/api/file/{vault}?path=` | Contenu rendu d'un fichier .md | GET |
| `/api/file/{vault}?path=` | Contenu rendu d'un fichier | GET |
| `/api/file/{vault}/raw?path=` | Contenu brut d'un fichier | GET |
| `/api/file/{vault}/download?path=` | Téléchargement d'un fichier | GET |
| `/api/file/{vault}/save?path=` | Sauvegarder un fichier | PUT |
| `/api/file/{vault}?path=` | Supprimer un fichier | DELETE |
| `/api/search?q=&vault=&tag=` | Recherche fulltext | GET |
| `/api/tags?vault=` | Tags uniques avec compteurs | GET |
| `/api/index/reload` | Force un re-scan des vaults | GET |
> Tous les endpoints exposent des schémas Pydantic documentés. La doc interactive est disponible sur `/docs` (Swagger UI).
**Exemple d'utilisation :**
```bash
# Health check
curl http://localhost:2020/api/health
# Lister les vaults
curl http://localhost:2020/api/vaults
@@ -327,51 +342,106 @@ docker-compose logs -f obsigate
docker-compose logs --tail=100 obsigate
```
### Performance
---
- **Indexation** : Première utilisation peut prendre quelques secondes
- **Mémoire** : ~50-100MB par 1000 fichiers (index en mémoire)
- **CPU** : Minimal, sauf lors des recherches fulltext
## ⚡ Performance
| Métrique | Estimation |
|----------|------------|
| **Indexation** | ~1–2s pour 1 000 fichiers markdown |
| **Recherche fulltext** | < 50ms (index en mémoire, zéro I/O disque) |
| **Résolution wikilinks** | O(1) via table de lookup |
| **Mémoire** | ~80–150MB par 1 000 fichiers (contenu capé à 100 KB/fichier) |
| **Image Docker** | ~180MB (multi-stage, sans outils de build) |
| **CPU** | Minimal ; pas de polling, pas de watchers |
### Optimisations clés (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 + Uvicorn
- **Backend** : Python 3.11 + FastAPI 0.110 + Uvicorn
- **Frontend** : Vanilla JS + HTML + CSS (zéro framework, zéro build)
- **Rendu Markdown** : mistune 3.x
- **Image Docker** : python:3.11-slim
- **Image Docker** : python:3.11-slim (multi-stage)
- **Base de données** : Aucune (index en mémoire uniquement)
- **Architecture** : SPA + API REST
---
## 🏠 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. Les requêtes de recherche utilisent l'index en mémoire (zéro I/O disque)
5. Le frontend SPA communique via REST et gère l'état côté client
---
## 📝 Développement
### Structure du projet
```
ObsiGate/
├── backend/ # API FastAPI
│ ├── main.py # Point d'entrée
│ ├── indexer.py # Indexation des vaults
│ ├── search.py # Moteur de recherche
├── 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
│ └── requirements.txt
├── frontend/ # Interface web
│ ├── index.html # Page principale
│ ├── app.js # Logique SPA
│ └── style.css # Styles
├── Dockerfile # Configuration Docker
├── docker-compose.yml # Déploiement
└── build.sh # Build multi-platform
├── frontend/ # Interface web (Vanilla JS, zéro framework)
│ ├── index.html # Page SPA + modales (aide, config, éditeur)
│ ├── app.js # Logique SPA, gestion d'état, API client
│ └── style.css # Styles (CSS variables, thèmes, responsive)
├── Dockerfile # Multi-stage, healthcheck, non-root
├── docker-compose.yml # Déploiement avec healthcheck
├── build.sh # Build multi-platform (amd64/arm64/arm/v7/i386)
└── CONTRIBUTING.md # Guide de contribution
```
### Contribuer
1. Fork le projet
2. Créer une branche `feature/nouvelle-fonctionnalite`
3. Commit vos changements
4. Push vers la branche
5. Créer une Pull Request
Voir [CONTRIBUTING.md](CONTRIBUTING.md) pour les détails.
---
@@ -389,4 +459,45 @@ Ce projet est sous licence **MIT** - voir le fichier [LICENSE](LICENSE) pour les
---
*Projet : ObsiGate | Version : 1.0.0 | Dernière mise à jour : 2025*
## 📝 Changelog
### 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.1.0 | Dernière mise à jour : 2025*