From 331c614330ee0e35036adac15805ae437956bdc1 Mon Sep 17 00:00:00 2001 From: Bruno Charest Date: Mon, 24 Aug 2026 15:50:49 -0400 Subject: [PATCH] =?UTF-8?q?feat(boot):=20=C3=A9cran=20de=20chargement=20in?= =?UTF-8?q?line=20+=20staging=20backend/frontend=20pour=20le=20bundle=20de?= =?UTF-8?q?sktop?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - frontend/index.html : splash #boot-splash 100% inline (CSS+JS), scripts CDN en defer pour ne pas bloquer le premier paint - frontend/js/app.js : signale le boot terminé via __OBSIGATE_BOOTED (try/finally) - desktop/src/main.rs : met à jour le statut du splash avant la redirection vers le backend - ci: stage backend/ + frontend/ sous desktop/ avant tauri build (resources backend/** et frontend/**) dans desktop-build.yml et build-linux.sh - desktop/gen/schemas/windows-schema.json : régénéré (permissions store/updater) - docs: README, README.fr, ROADMAP --- .gitea/workflows/desktop-build.yml | 12 + README.fr.md | 891 +++++++++++++++++++++--- README.md | 73 +- desktop/build-linux.sh | 13 + desktop/gen/schemas/windows-schema.json | 228 ++++++ desktop/src/main.rs | 4 + docs/ROADMAP.md | 2 +- frontend/index.html | 72 +- frontend/js/app.js | 11 +- 9 files changed, 1199 insertions(+), 107 deletions(-) diff --git a/.gitea/workflows/desktop-build.yml b/.gitea/workflows/desktop-build.yml index 828e4e7..41bc784 100644 --- a/.gitea/workflows/desktop-build.yml +++ b/.gitea/workflows/desktop-build.yml @@ -29,6 +29,12 @@ jobs: cd desktop/python-embed ./python.exe -m pip install --no-cache-dir -r ../../backend/requirements.txt + - name: Stage backend + frontend for bundle + working-directory: desktop + run: | + xcopy /E /I /Q /Y "..\backend" "backend" + xcopy /E /I /Q /Y "..\frontend" "frontend" + - name: Build MSI working-directory: desktop run: cargo tauri build --bundles msi @@ -68,6 +74,12 @@ jobs: source desktop/python-embed/venv/bin/activate pip install -r backend/requirements.txt + - name: Stage backend + frontend for bundle + working-directory: desktop + run: | + cp -r ../backend backend + cp -r ../frontend frontend + - name: Build AppImage working-directory: desktop run: cargo tauri build --bundles appimage diff --git a/README.fr.md b/README.fr.md index dfa8370..ee58737 100644 --- a/README.fr.md +++ b/README.fr.md @@ -1,99 +1,810 @@ -# ObsiGate — Gestionnaire de connaissances personnel +# ObsiGate -**ObsiGate** est une application web moderne de gestion de notes Markdown, conçue pour -remplacer des outils comme Obsidian avec une expérience auto-hébergée complète. +> **Version française** — ce document est le miroir synchronisé de [README.md](README.md) (référence complète). Dernière synchronisation : juin 2026. -## ✨ Fonctionnalités principales +**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. -- **📝 Éditeur Markdown** avancé avec aperçu en temps réel -- **🤖 IA intégrée** : DeepSeek, OpenRouter, Gemini — complétion, réécriture, traduction -- **🔍 Recherche full-text** avec TF-IDF et filtres avancés -- **📊 Dashboard** avec statistiques, fichiers récents, et contenu partagé -- **🏷️ Tags** : organisation et filtrage par tags -- **🗺️ Vue Graphe** : visualisation des liens entre fichiers -- **🔄 Synchronisation** en temps réel via SSE (Server-Sent Events) -- **🔗 Webhooks** : notifications automatiques vers des services externes -- **💾 Sauvegardes** automatiques et restauration -- **🎨 15 thèmes** avec modes clair et sombre -- **🌐 Internationalisation** : anglais et français (sélecteur de langue intégré) -- **📱 PWA** : installation en tant qu'application native -- **🔒 Authentification** JWT avec tokens refresh - -## 🚀 Installation rapide - -### Docker (recommandé) -```bash -git clone https://git.dracodev.net/Projets/ObsiGate.git -cd ObsiGate -cp .env.example .env -# Configurer DEEPSEEK_API_KEY dans .env -docker compose up -d --build -``` - -### Accès -- **Interface web** : http://localhost:2020 -- **Utilisateur par défaut** : admin / admin (à changer immédiatement) - -## 📋 Prérequis - -- Docker & Docker Compose -- Clé API DeepSeek, OpenRouter, ou Gemini (optionnelle, pour l'IA) -- Python 3.11+ (pour le développement) - -## 🛠️ Développement - -```bash -pip install -r requirements.txt -python backend/main.py -# Dans un autre terminal : -cd frontend && python -m http.server 8080 -``` - -## 📁 Structure du projet +[![Version](https://img.shields.io/badge/Version-2.0.0--dev-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) ``` -ObsiGate/ - backend/ # API FastAPI (Python) - frontend/ # Interface utilisateur (HTML/CSS/JS vanilla) - locales/ # Traductions (en.json, fr.json) - docker/ # Configuration Docker - docs/ # Documentation et roadmap +┌─────────────────────────────────────────────────────────┐ +│ [🔍 Recherche...] [☀/🌙 Thème] ObsiGate │ +├──────────────┬──────────────────────────────────────────┤ +│ SIDEBAR │ CONTENT AREA │ +│ ▼ Recettes │ 📄 Titre du fichier │ +│ 📁 Soupes │ Tags: #recette #rapide │ +│ 📄 Pizza │ [Contenu Markdown rendu] │ +│ ▼ IT │ │ +│ 📁 Docker │ │ +│ Tags Cloud │ │ +└──────────────┴──────────────────────────────────────────┘ ``` -## 🌍 Internationalisation - -ObsiGate supporte l'anglais et le français. Le sélecteur de langue se trouve dans -**Paramètres > Langue**. La traduction couvre : - -- ✅ Interface utilisateur complète -- ✅ Guide d'utilisation (18 sections) -- ✅ Messages système et toasts -- ✅ Descriptions des thèmes et commandes - -## 📖 Documentation - -- [README.md](README.md) — Documentation complète en anglais -- [docs/ROADMAP.md](docs/ROADMAP.md) — Feuille de route du projet -- [docs/CONTRIBUTING.md](docs/CONTRIBUTING.md) — Guide de contribution -- [docs/TROUBLESHOOTING.md](TROUBLESHOOTING.md) — Dépannage - -## 🔧 Configuration - -Variables d'environnement principales (`.env`) : - -| Variable | Description | -|----------|-------------| -| `DEEPSEEK_API_KEY` | Clé API pour l'IA DeepSeek | -| `OPENROUTER_API_KEY` | Clé API pour OpenRouter | -| `GEMINI_API_KEY` | Clé API pour Gemini | -| `ADMIN_PASSWORD` | Mot de passe administrateur | -| `JWT_SECRET` | Secret pour les tokens JWT | -| `WEBHOOK_SECRET` | Secret pour la signature des webhooks | - -## 📝 Licence - -Ce projet est sous licence propriétaire. Voir le fichier [LICENSE](LICENSE) pour plus de détails. - --- -**ObsiGate** — Votre savoir, votre serveur. 🏰 +## 📋 Table des matières + +- [Fonctionnalités](#fonctionnalites) +- [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) +- [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) +- [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 +- **🖥️ 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_LOGIN_WINDOW_SECONDS` | Fenêtre de rate limiting (secondes) | `900` | + +### 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** : `[](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** : `![alt text](path/to/image.png)` + +### 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]` + +### 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 + +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` | + +--- + +## 🔌 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 (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 ; 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 + +### Syntaxe de requête + +| Opérateur | Description | Exemple | +|-----------|-------------|---------| +| `tag:` | Filtrer par tag | `tag:recette docker` | +| `#` | Raccourci tag | `#linux serveur` | +| `vault:` | Filtrer par vault | `vault:IT kubernetes` | +| `title:` | Filtrer par titre | `title:pizza` | +| `path:` | Filtrer par chemin | `path:recettes/soupes` | +| `ext:` | 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`. + +### 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** (``), **facettes** (compteurs par vault/tag), **pagination** (50/page), **tri** pertinence/date, **chips** de filtres, **historique** (50 recherches) + +--- + +## 🔧 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é + +- **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 + +### 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 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) + +--- + +## 📝 Changelog + +Consultez le [CHANGELOG.md](./CHANGELOG.md) pour l'historique complet de toutes les versions (v1.0.0 → v2.0.0-dev). + +--- + +*Projet : ObsiGate | Version : 2.0.0-dev | Dernière mise à jour : Juin 2026* diff --git a/README.md b/README.md index 78c665e..a92adb2 100644 --- a/README.md +++ b/README.md @@ -552,28 +552,79 @@ chmod +x ObsiGate_2.0.0_amd64.AppImage ### Build depuis les sources -Voir le [guide détaillé dans desktop/](./desktop/README.md). +Guide détaillé : [desktop/README.md](./desktop/README.md). -```bash -# Linux -cd desktop -chmod +x build-linux.sh -./build-linux.sh -# → target/release/bundle/deb/obsigate_2.0.0_amd64.deb -# → target/release/bundle/appimage/ObsiGate_2.0.0_amd64.AppImage -``` +#### 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 Windows (via Scoop) +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 -REM → target\release\bundle\msi\ObsiGate_2.0.0_x64.msi ``` +É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 ``` diff --git a/desktop/build-linux.sh b/desktop/build-linux.sh index 08039e8..08eb1d2 100755 --- a/desktop/build-linux.sh +++ b/desktop/build-linux.sh @@ -29,6 +29,15 @@ pip install --upgrade pip -q pip install -r ../backend/requirements.txt -q echo "✅ Python environment ready" +# ── 2b. Stage backend + frontend pour le bundle ─────────────── +# tauri.conf.json embarque backend/** et frontend/** depuis desktop/ : +# sans ce staging, tauri_build échoue (« glob pattern backend/**/* path not found »). +echo "[2b/5] Staging backend + frontend for bundle..." +rm -rf backend frontend +cp -r ../backend backend +cp -r ../frontend frontend +echo "✅ Staged" + # ── 3. Build Tauri ──────────────────────────────────────────── echo "[3/5] Building Tauri application..." cargo tauri build --target x86_64-unknown-linux-gnu --bundles deb,appimage @@ -43,6 +52,10 @@ cp -r ../backend "$OUTDIR/" cp -r ../frontend "$OUTDIR/" echo "✅ Runtime + frontend copied" +# ── 4b. Nettoyer les dossiers stagés ────────────────────────── +rm -rf backend frontend +echo "✅ Cleaned" + # ── 5. Résultats ─────────────────────────────────────────────── OUTDIR="target/x86_64-unknown-linux-gnu/release" echo "[5/5] Results:" diff --git a/desktop/gen/schemas/windows-schema.json b/desktop/gen/schemas/windows-schema.json index d2f01e3..438ec59 100644 --- a/desktop/gen/schemas/windows-schema.json +++ b/desktop/gen/schemas/windows-schema.json @@ -6337,6 +6337,234 @@ "type": "string", "const": "shell:deny-stdin-write", "markdownDescription": "Denies the stdin_write command without any pre-configured scope." + }, + { + "description": "This permission set configures what kind of\noperations are available from the store plugin.\n\n#### Granted Permissions\n\nAll operations are enabled by default.\n\n\n#### This default permission set includes:\n\n- `allow-load`\n- `allow-get-store`\n- `allow-set`\n- `allow-get`\n- `allow-has`\n- `allow-delete`\n- `allow-clear`\n- `allow-reset`\n- `allow-keys`\n- `allow-values`\n- `allow-entries`\n- `allow-length`\n- `allow-reload`\n- `allow-save`", + "type": "string", + "const": "store:default", + "markdownDescription": "This permission set configures what kind of\noperations are available from the store plugin.\n\n#### Granted Permissions\n\nAll operations are enabled by default.\n\n\n#### This default permission set includes:\n\n- `allow-load`\n- `allow-get-store`\n- `allow-set`\n- `allow-get`\n- `allow-has`\n- `allow-delete`\n- `allow-clear`\n- `allow-reset`\n- `allow-keys`\n- `allow-values`\n- `allow-entries`\n- `allow-length`\n- `allow-reload`\n- `allow-save`" + }, + { + "description": "Enables the clear command without any pre-configured scope.", + "type": "string", + "const": "store:allow-clear", + "markdownDescription": "Enables the clear command without any pre-configured scope." + }, + { + "description": "Enables the delete command without any pre-configured scope.", + "type": "string", + "const": "store:allow-delete", + "markdownDescription": "Enables the delete command without any pre-configured scope." + }, + { + "description": "Enables the entries command without any pre-configured scope.", + "type": "string", + "const": "store:allow-entries", + "markdownDescription": "Enables the entries command without any pre-configured scope." + }, + { + "description": "Enables the get command without any pre-configured scope.", + "type": "string", + "const": "store:allow-get", + "markdownDescription": "Enables the get command without any pre-configured scope." + }, + { + "description": "Enables the get_store command without any pre-configured scope.", + "type": "string", + "const": "store:allow-get-store", + "markdownDescription": "Enables the get_store command without any pre-configured scope." + }, + { + "description": "Enables the has command without any pre-configured scope.", + "type": "string", + "const": "store:allow-has", + "markdownDescription": "Enables the has command without any pre-configured scope." + }, + { + "description": "Enables the keys command without any pre-configured scope.", + "type": "string", + "const": "store:allow-keys", + "markdownDescription": "Enables the keys command without any pre-configured scope." + }, + { + "description": "Enables the length command without any pre-configured scope.", + "type": "string", + "const": "store:allow-length", + "markdownDescription": "Enables the length command without any pre-configured scope." + }, + { + "description": "Enables the load command without any pre-configured scope.", + "type": "string", + "const": "store:allow-load", + "markdownDescription": "Enables the load command without any pre-configured scope." + }, + { + "description": "Enables the reload command without any pre-configured scope.", + "type": "string", + "const": "store:allow-reload", + "markdownDescription": "Enables the reload command without any pre-configured scope." + }, + { + "description": "Enables the reset command without any pre-configured scope.", + "type": "string", + "const": "store:allow-reset", + "markdownDescription": "Enables the reset command without any pre-configured scope." + }, + { + "description": "Enables the save command without any pre-configured scope.", + "type": "string", + "const": "store:allow-save", + "markdownDescription": "Enables the save command without any pre-configured scope." + }, + { + "description": "Enables the set command without any pre-configured scope.", + "type": "string", + "const": "store:allow-set", + "markdownDescription": "Enables the set command without any pre-configured scope." + }, + { + "description": "Enables the values command without any pre-configured scope.", + "type": "string", + "const": "store:allow-values", + "markdownDescription": "Enables the values command without any pre-configured scope." + }, + { + "description": "Denies the clear command without any pre-configured scope.", + "type": "string", + "const": "store:deny-clear", + "markdownDescription": "Denies the clear command without any pre-configured scope." + }, + { + "description": "Denies the delete command without any pre-configured scope.", + "type": "string", + "const": "store:deny-delete", + "markdownDescription": "Denies the delete command without any pre-configured scope." + }, + { + "description": "Denies the entries command without any pre-configured scope.", + "type": "string", + "const": "store:deny-entries", + "markdownDescription": "Denies the entries command without any pre-configured scope." + }, + { + "description": "Denies the get command without any pre-configured scope.", + "type": "string", + "const": "store:deny-get", + "markdownDescription": "Denies the get command without any pre-configured scope." + }, + { + "description": "Denies the get_store command without any pre-configured scope.", + "type": "string", + "const": "store:deny-get-store", + "markdownDescription": "Denies the get_store command without any pre-configured scope." + }, + { + "description": "Denies the has command without any pre-configured scope.", + "type": "string", + "const": "store:deny-has", + "markdownDescription": "Denies the has command without any pre-configured scope." + }, + { + "description": "Denies the keys command without any pre-configured scope.", + "type": "string", + "const": "store:deny-keys", + "markdownDescription": "Denies the keys command without any pre-configured scope." + }, + { + "description": "Denies the length command without any pre-configured scope.", + "type": "string", + "const": "store:deny-length", + "markdownDescription": "Denies the length command without any pre-configured scope." + }, + { + "description": "Denies the load command without any pre-configured scope.", + "type": "string", + "const": "store:deny-load", + "markdownDescription": "Denies the load command without any pre-configured scope." + }, + { + "description": "Denies the reload command without any pre-configured scope.", + "type": "string", + "const": "store:deny-reload", + "markdownDescription": "Denies the reload command without any pre-configured scope." + }, + { + "description": "Denies the reset command without any pre-configured scope.", + "type": "string", + "const": "store:deny-reset", + "markdownDescription": "Denies the reset command without any pre-configured scope." + }, + { + "description": "Denies the save command without any pre-configured scope.", + "type": "string", + "const": "store:deny-save", + "markdownDescription": "Denies the save command without any pre-configured scope." + }, + { + "description": "Denies the set command without any pre-configured scope.", + "type": "string", + "const": "store:deny-set", + "markdownDescription": "Denies the set command without any pre-configured scope." + }, + { + "description": "Denies the values command without any pre-configured scope.", + "type": "string", + "const": "store:deny-values", + "markdownDescription": "Denies the values command without any pre-configured scope." + }, + { + "description": "This permission set configures which kind of\nupdater functions are exposed to the frontend.\n\n#### Granted Permissions\n\nThe full workflow from checking for updates to installing them\nis enabled.\n\n\n#### This default permission set includes:\n\n- `allow-check`\n- `allow-download`\n- `allow-install`\n- `allow-download-and-install`", + "type": "string", + "const": "updater:default", + "markdownDescription": "This permission set configures which kind of\nupdater functions are exposed to the frontend.\n\n#### Granted Permissions\n\nThe full workflow from checking for updates to installing them\nis enabled.\n\n\n#### This default permission set includes:\n\n- `allow-check`\n- `allow-download`\n- `allow-install`\n- `allow-download-and-install`" + }, + { + "description": "Enables the check command without any pre-configured scope.", + "type": "string", + "const": "updater:allow-check", + "markdownDescription": "Enables the check command without any pre-configured scope." + }, + { + "description": "Enables the download command without any pre-configured scope.", + "type": "string", + "const": "updater:allow-download", + "markdownDescription": "Enables the download command without any pre-configured scope." + }, + { + "description": "Enables the download_and_install command without any pre-configured scope.", + "type": "string", + "const": "updater:allow-download-and-install", + "markdownDescription": "Enables the download_and_install command without any pre-configured scope." + }, + { + "description": "Enables the install command without any pre-configured scope.", + "type": "string", + "const": "updater:allow-install", + "markdownDescription": "Enables the install command without any pre-configured scope." + }, + { + "description": "Denies the check command without any pre-configured scope.", + "type": "string", + "const": "updater:deny-check", + "markdownDescription": "Denies the check command without any pre-configured scope." + }, + { + "description": "Denies the download command without any pre-configured scope.", + "type": "string", + "const": "updater:deny-download", + "markdownDescription": "Denies the download command without any pre-configured scope." + }, + { + "description": "Denies the download_and_install command without any pre-configured scope.", + "type": "string", + "const": "updater:deny-download-and-install", + "markdownDescription": "Denies the download_and_install command without any pre-configured scope." + }, + { + "description": "Denies the install command without any pre-configured scope.", + "type": "string", + "const": "updater:deny-install", + "markdownDescription": "Denies the install command without any pre-configured scope." } ] }, diff --git a/desktop/src/main.rs b/desktop/src/main.rs index 7ee0992..23343a0 100644 --- a/desktop/src/main.rs +++ b/desktop/src/main.rs @@ -566,6 +566,10 @@ fn main() { match wait_for_backend().await { Ok(()) => { if let Some(window) = handle.get_webview_window("main") { + // Met à jour le splash inline avant la redirection + let _ = window.eval( + r#"if (window.__setBootStatus) window.__setBootStatus("Backend prêt — chargement de l'interface…")"#, + ); match window.eval("window.location.href = 'http://127.0.0.1:17890'") { Ok(_) => info!("Redirect to backend OK"), Err(e) => error!("Redirect eval failed: {}", e), diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 36bf690..57af31a 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -841,7 +841,7 @@ - [ ] **Page de release** : intégrer le build desktop dans les releases Gitea + README d'installation ##### E. Expérience utilisateur (1 jour) - - [ ] Écran de chargement pendant le démarrage du backend (« ObsiGate démarre... » avec spinner) + - [x] Écran de chargement pendant le démarrage du backend (« ObsiGate démarre... » avec spinner) — splash inline `#boot-splash` dans `index.html`, retiré quand `app.js` signale le boot ; statut mis à jour depuis Rust - [ ] Gestion des erreurs : backend crash → message explicite + bouton « Redémarrer » - [ ] Sauvegarde des préférences desktop (taille fenêtre, position, dernier vault) - [ ] Première expérience : wizard « Choisissez votre vault » au premier lancement diff --git a/frontend/index.html b/frontend/index.html index 3685243..1cd87bd 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -47,6 +47,33 @@ href="/static/icons/icon-192x192.svg" /> + + - - - + + + + + +
+
+ +
ObsiGate
+ +
Démarrage…
+
+
+
diff --git a/frontend/js/app.js b/frontend/js/app.js index 0dc50cb..582c790 100644 --- a/frontend/js/app.js +++ b/frontend/js/app.js @@ -102,8 +102,15 @@ async function init() { } document.addEventListener("DOMContentLoaded", () => { - init(); - Sync.init(); + try { + init(); + Sync.init(); + } finally { + // Signale au splash de démarrage (index.html #boot-splash) que l'app est + // initialisée : le splash est retiré et le squelette stylé révélé. + window.__OBSIGATE_BOOTED = true; + if (typeof window.__obsigateBootReady === "function") window.__obsigateBootReady(); + } // Show version in header fetch('/api/health') .then(r => r.json())