Commit Graph
105 Commits
Author SHA1 Message Date
bruno 7042307955 feat(pdf): #74 au complet — fix auth 500 stream + endpoint pdf/info + HTTP Range 206 + indexation incrementale PDF + config taille/timeout 2026-09-07 23:24:13 -04:00
bruno f37d5fda3c fix(version): ordre des imports (ruff I001)
CI / lint (push) Successful in 39s
CI / security (push) Successful in 28s
CI / test (push) Successful in 50s
CI / build (push) Successful in 23s
CI / e2e (push) Successful in 5m51s
Desktop Build / build-windows (push) Canceled after 0s
Desktop Build / build-linux (push) Canceled after 0s
2026-09-07 22:15:39 -04:00
bruno c5b92eabe1 feat(version): version x.y.z propre et cohérente partout + bump script
CI / lint (push) Failing after 21s
CI / test (push) Skipped
CI / build (push) Skipped
CI / e2e (push) Skipped
CI / security (push) Successful in 27s
Desktop Build / build-windows (push) Canceled after 0s
Desktop Build / build-linux (push) Canceled after 0s
Corrige l'incoherence d'affichage (page principale 0.0.0-dev vs a propos
v2.0.0-dev) et unifie la gestion de version sur une seule source de verite.

Backend:
- version.py: get_version() retourne toujours le tag x.y.z propre du dernier
  tag (plus de 0.0.0-dev / -N-gHASH dans l'UI). Ajout get_git_describe() et
  get_git_commit() pour le detail.
- main.py: /api/health expose git_describe + git_commit (nouveaux champs).

Frontend:
- modale A propos (populateAbout): version + commit lus depuis /api/health,
  suppression du hardcode v2.0.0-dev dans index.html.
- section config A propos (loadAbout): lit health.version, plus de state.
  APP_VERSION obsolete.
- badge header: fetch /api/health (fallback neutre '...').
- state.js: suppression de APP_VERSION (1.5.0) devenu mort; imports nettoyes.

Script:
- scripts/bump_version.sh: incrementation SemVer selon les commits
  (breaking->major, feat->minor, sinon patch), creer/pousser le tag vX.Y.Z.

Tests: 507 passed, frontend validators OK.
2026-09-07 22:13:54 -04:00
bruno 88ab8db88d fix(admin): /admin.html retombait sur la page principale (ROADMAP #71)
CI / lint (push) Successful in 54s
CI / security (push) Successful in 26s
CI / test (push) Successful in 50s
CI / build (push) Successful in 23s
CI / e2e (push) Successful in 6m2s
Desktop Build / build-windows (push) Canceled after 0s
Desktop Build / build-linux (push) Canceled after 0s
Le menu Admin redirigeait vers /admin.html mais aucune route backend ne le
servait : le catch-all SPA /{full_path:path} renvoyait index.html, d'où le
retour à la page principale.

- backend/main.py: route GET /admin.html (gate require_admin) déclarée avant
  le catch-all SPA + correction des imports JS /frontend/js -> /static/js
- frontend/admin.html: chemins d'assets alignés sur le mount /static
- tests/test_admin.py: 3 tests de régression (page servie admin, refus 401/403)
2026-09-07 20:29:33 -04:00
bruno 72383b1634 feat(config): badges statut + suppression par provider dans Clés API IA
CI / lint (push) Failing after 33s
CI / test (push) Skipped
CI / build (push) Skipped
CI / e2e (push) Skipped
CI / security (push) Successful in 24s
Desktop Build / build-windows (push) Canceled after 0s
Desktop Build / build-linux (push) Canceled after 0s
- backend: ajout DELETE /api/config/ai-keys/{provider_env} pour supprimer une clé
- frontend: badge ✓ Configuré / Non configuré sur chaque ligne provider
- frontend: bouton × Supprimer avec confirmation sur les providers configurés
- testAIKeys met à jour les badges selon les résultats du test
2026-09-07 15:43:24 -04:00
bruno d441481930 fix(xiaomi): URL MiMo + header api-key + fallback polling admin SSE
CI / lint (push) Successful in 37s
CI / security (push) Successful in 32s
CI / test (push) Successful in 48s
CI / build (push) Successful in 22s
CI / e2e (push) Successful in 6m1s
Desktop Build / build-windows (push) Canceled after 0s
Desktop Build / build-linux (push) Canceled after 0s
1. **fix(xiaomi): la clé Xiaomi échouait avec 'Name or service not known'**
   - URL précédente api.xiaomi.com n'existe pas (DNS fail)
   - Vraie URL: https://api.xiaomimimo.com/v1/models
   - Xiaomi MiMo utilise un header custom 'api-key:' (PAS 'Authorization: Bearer')
   - Modèles par défaut mis à jour: mimo-v2.5-pro, mimo-v2.5, mimo-v2.5-asr, etc.
   - Le chat dans ai.py supporte maintenant un header custom via auth_header_name

2. **fix(admin): EventSource 503 → fallback polling**
   - Ajout d'un fallback: si EventSource ne reçoit jamais le premier event
     (cookie expiré, réseau bloqué, proxy timeout), on bascule sur du polling
     /api/admin/stats toutes les 5s. Le UI continue de se mettre à jour.
   - Cleanup correct du timer dans disconnectSSE

3. **fix(test_ai_models)**: 2 nouveaux tests de régression
   - test_xiaomi_uses_api_key_header_not_bearer: vérifie URL + header
   - test_xiaomi_test_endpoint_uses_real_url: vérifie /api/config/ai-keys/test
   - Patche backend.ai ET backend.main (import local)
   - Header case-insensitive (urllib normalise à 'Api-key', HTTP est insensible)

Vérifié :
- pytest : 504 passed (502 + 2 nouveaux Xiaomi)
- frontend unit : 7 passed
- validate-imports : 30 modules / 204 exports
- pane-manager JSDOM : 9/9
- ruff check backend/ : All checks passed
2026-09-07 12:47:24 -04:00
bruno 7ff9b854b6 fix(admin,ai): redirect admin.html + modèles fallback + picker provider/modèle par requête
CI / lint (push) Successful in 41s
CI / security (push) Successful in 28s
CI / test (push) Successful in 48s
CI / build (push) Successful in 22s
CI / e2e (push) Successful in 6m5s
Desktop Build / build-windows (push) Canceled after 0s
Desktop Build / build-linux (push) Canceled after 0s
Trois bugs corrigés + une amélioration demandée :

1. **fix(admin): /admin.html redirigeait toujours vers /**
   - admin.js _gateAdmin() lisait /api/auth/status qui ne contient PAS le rôle user
   - Remplacé par /api/auth/me (retourne username, role, vaults)
   - Le code distingue maintenant le cas 'auth désactivé' (admin anonyme) du
     cas 'auth requise non admin' (affiche écran forbidden)

2. **fix(ai): les modèles Nvidia/Xiaomi ne se chargeaient pas dans les dropdowns**
   - Xiaomi : l'endpoint public /v1/models est instable, échec réseau fréquent
   - Toutes les erreurs réseau/d'API renvoyaient models=[] → dropdown vide
   - Ajout d'un fallback curé : _FALLBACK_MODELS dict avec 4-8 modèles populaires
     par provider, TOUJOURS retourné si la clé manque OU si l'API distante échoue
   - Le frontend voit désormais 'fallback' vs 'live' comme hint pour l'utilisateur
   - Gemini parsing : strip du préfixe 'models/' retourné par l'API Gemini
   - 7 nouveaux tests pytest pour _FALLBACK_MODELS + endpoint /api/config/ai-models

3. **feat(ai): picker provider/model dans la toolbar AI + BooksLM**
   - Plusieurs providers peuvent maintenant être activés simultanément
   - Sélection provider+model par section (Forge, BooksLM, etc.) via dropdown
   - État persisté en localStorage (le choix suit l'utilisateur entre sections)
   - Chaque appel AI passe maintenant {provider, model} au backend
   - ai.js : aiAction() lit le picker et l'injecte dans le body
   - bookslm.js : envoie provider+model à /api/ai/bookslm/chat
   - ai_routes.py + bookslm_routes.py : AIRequest et BooksLMChatRequest
     acceptent provider+model, avec save/restore du modèle original
     pour ne pas affecter les autres requêtes concurrentes
   - 7 nouvelles clés i18n (ai.provider, ai.model, ai.model_loading, etc.)
   - 1 nouveau test bookslm vérifie que le schema accepte provider+model
   - validate-imports.mjs : fix faux positif sur 'export { X as Y }'

Vérifié :
- pytest : 502 passed, 5 skipped (494 baseline + 7 AI models + 1 BooksLM)
- frontend unit : 7 passed
- validate-imports : 30 modules / 204 exports / 0 erreur
- pane-manager JSDOM : 9/9
- ruff check backend/ : All checks passed
2026-09-07 12:00:05 -04:00
bruno 83355a25c8 chore(lint): ruff --fix sur le backend (cleanup pré-existant)
Réduit les erreurs ruff de 11 à 5 (toutes pré-existantes non auto-fixables) :
- F401 imports inutilisés dans auth/mfa.py
- I001 blocs d'imports non triés dans auth/router.py + main.py + pdf_reader.py

Les 5 restantes sont dans bookslm/export/watcher (code pré-existant, hors scope).

Vérifié : pytest full suite reste 492 passed, 5 skipped, 0 failed.
2026-09-07 09:01:40 -04:00
bruno b649c6b301 feat(admin): backend dashboard pour #71
- backend/admin.py : module FastAPI avec 4 endpoints admin-gated
  - GET /api/admin/stats (CPU/RAM/Disk/Uptime via psutil)
  - GET /api/admin/audit (filtres user/action/limit/offset)
  - GET /api/admin/backup-stats (count + size + age par vault)
  - GET /api/admin/stream (Server-Sent Events 5s)
- backend/main.py : routeur monté + middleware gzip bypass pour SSE
- backend/requirements.txt : ajout psutil>=5.9
- tests/test_admin.py : 13 tests (auth + filtres + format SSE), 100% verts

Pas de frontend dans cette PR — page admin.html + admin.js restent à faire.
2026-09-07 08:54:25 -04:00
bruno 524e6da591 feat(bookslm): v2.2.0 - BooksLM chat AI contextuel par répertoire + 4 providers AI
CI / lint (push) Failing after 16s
CI / test (push) Skipped
CI / build (push) Skipped
CI / e2e (push) Skipped
CI / security (push) Successful in 21s
Desktop Build / build-windows (push) Canceled after 0s
Desktop Build / build-linux (push) Canceled after 0s
BooksLM (#76):
- Panneau chat slide-in 450px, clic-droit répertoire → 🧠 BooksLM
- Collecte récursive .md avec limites (200 fichiers, 200K chars)
- Cache SHA-256, redaction secrets, priorité README/index
- SSE streaming, badges sources cliquables, historique localStorage
- Commandes palette: BooksLM ouvrir/nouvelle conversation

Providers AI (4 nouveaux):
- NVIDIA (integrate.api.nvidia.com)
- QwenCloud (dashscope.aliyuncs.com)
- Xiaomi (api.xiaomi.com)
- Mistral (api.mistral.ai)
- Tous OpenAI-compatible, auto-listing modèles

Fix: dropdown config-select suit maintenant le thème (option bg/color)
27 tests BooksLM + 466 tests au total
2026-09-06 19:42:40 -04:00
bruno 83063d3a18 feat(auth): v2.1.0 - MFA TOTP avec QR code, recovery codes
CI / lint (push) Failing after 16s
CI / test (push) Skipped
CI / build (push) Skipped
CI / e2e (push) Skipped
CI / security (push) Successful in 21s
Desktop Build / build-windows (push) Canceled after 0s
Desktop Build / build-linux (push) Canceled after 0s
- backend/auth/mfa.py: TOTP (pyotp), recovery codes SHA-256
- Login flow: mfa_required → totp/verify → token (ou recovery)
- 6 nouveaux endpoints /api/auth/mfa/*
- Frontend: QR code setup, 6-digit auto-submit, recovery codes
- Settings: section Sécurité avec enable/disable MFA
- CSS: mfa-challenge, setup-card, recovery-list, badges
- i18n: 36 nouvelles clés EN/FR
- pyotp ajouté aux dépendances
- 30 tests (TOTP, recovery, API endpoints, login flow)
- 439 tests passent au total
2026-09-06 18:42:32 -04:00
bruno 62e191abfc fix(ci): lint ruff 0.16.3 - imports tries, check explicite, removeprefix
CI / lint (push) Successful in 28s
CI / security (push) Successful in 18s
CI / test (push) Successful in 36s
CI / build (push) Failing after 12s
CI / e2e (push) Skipped
Desktop Build / build-windows (push) Canceled after 0s
Desktop Build / build-linux (push) Canceled after 0s
- backend/main.py : split import csv / import io as csv_io (I001)

- backend/version.py : check=False explicite sur subprocess.run (PLW1510)

- backend/version.py : tag.removeprefix() au lieu du slice conditionnel (FURB188)

- verifie localement avec ruff 0.16.3 : All checks passed!
2026-08-24 16:28:28 -04:00
bruno 6da7fa6786 fix(wikilinks): supporte les ancres internes [[#Titre|Texte]] et corrige les IDs de titres
CI / lint (push) Failing after 19s
CI / test (push) Skipped
CI / build (push) Skipped
CI / e2e (push) Skipped
CI / security (push) Successful in 23s
Desktop Build / build-windows (push) Canceled after 0s
Desktop Build / build-linux (push) Canceled after 0s
- Ajoute la reconnaissance de la syntaxe Obsidian [[#Titre|Texte]] pour
  créer des liens d'ancre vers les titres du document courant.
- Corrige _heading_slugify pour ignorer les balises HTML et décoder les
  entités HTML (ex. &) avant de générer l'ID, évitant ainsi la
  pollution par les wikilinks placés dans les titres.
- Ajoute le style .wikilink-anchor et des tests couvrant ces cas.

Fixe le rendu du document TestDir/Agents IA - Panorama Complet 2026.md.
2026-08-13 15:24:52 -04:00
bruno ba73af8ced fix: CSP font-src blocks Excalidraw fonts from esm.sh
CI / lint (push) Failing after 9s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / e2e (push) Has been skipped
CI / security (push) Successful in 14s
Desktop Build / build-windows (push) Has been cancelled
Desktop Build / build-linux (push) Has been cancelled
CSP directive 'font-src' only allowed self, data:, and fonts.gstatic.com.
Excalidraw loads its handwriting fonts (*.woff2) from esm.sh via
EXCALIDRAW_ASSET_PATH. All ~12 font files were blocked (blocked:csp)
→ Excalidraw fails to initialize → infinite 'Loading...' spinner.

Fix: added https://esm.sh to font-src directive.
2026-07-29 21:14:36 -04:00
bruno 9804cf9a6d feat(excalidraw): support .excalidraw.md (Obsidian plugin format) with lz-string decompression
CI / lint (push) Failing after 14s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / e2e (push) Has been skipped
CI / security (push) Successful in 13s
Desktop Build / build-windows (push) Has been cancelled
Desktop Build / build-linux (push) Has been cancelled
2026-07-29 16:08:03 -04:00
bruno dfd8d5929c fix(excalidraw): fusion imports ESM en un seul module + CSP worker-src/blob:
CI / lint (push) Failing after 13s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / e2e (push) Has been skipped
CI / security (push) Successful in 29s
Desktop Build / build-windows (push) Has been cancelled
Desktop Build / build-linux (push) Has been cancelled
2026-07-29 11:17:06 -04:00
bruno 1b5319fde1 feat(excalidraw): support complet des fichiers .excalidraw (#78)
CI / lint (push) Failing after 9s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / e2e (push) Has been skipped
CI / security (push) Successful in 13s
Desktop Build / build-windows (push) Has been cancelled
Desktop Build / build-linux (push) Has been cancelled
- Backend: ajout .excalidraw dans SUPPORTED_EXTENSIONS + squelette JSON creation
- Backend: détection is_excalidraw dans api_file() + FileContentResponse
- Frontend: icône pen-tool dans EXT_ICONS
- Frontend: ajout dans la modale de création (ui.js)
- Frontend: excalidraw-editor.html (iframe autonome, React+Excalidraw via esm.sh)
- Frontend: excalidraw-viewer.js (postMessage, auto-save 2s, thème)
- Frontend: dispatch dans viewer.js (renderFile)
- Tests: 6 tests (détection, création squelette, fallback, tree, roundtrip)
- 359/359 tests passent
2026-07-29 10:55:00 -04:00
bruno 7389d26520 fix(markdown): convert single newlines to hard breaks matching Obsidian behavior
CI / lint (push) Failing after 36s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / e2e (push) Has been skipped
CI / security (push) Successful in 14s
Desktop Build / build-windows (push) Has been cancelled
Desktop Build / build-linux (push) Has been cancelled
Add _normalize_line_breaks() pre-processor that converts single \n to
two-spaces+\n (hard break) before mistune rendering, while protecting
fenced code blocks. This matches Obsidian's default 'Strict Line Breaks
= off' behavior where standalone lines render on separate rows instead
of merging into a single paragraph.
2026-07-29 09:56:51 -04:00
bruno c8b19d507a feat: versioning SemVer MAJOR.MINOR.PATCH basé sur git describe
CI / lint (push) Failing after 9s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / e2e (push) Has been skipped
CI / security (push) Successful in 13s
Desktop Build / build-windows (push) Has been cancelled
Desktop Build / build-linux (push) Has been cancelled
- backend/version.py: get_version() lit git describe --tags (ex: 1.8.0-301-gabc1234)
- main.py: FastAPI version = get_version() dynamique
- build.rs: injecte GIT_VERSION, GIT_HASH, SEMVER depuis git describe
- main.rs: ABOUT_MSG, get_version(), log utilisent GIT_VERSION
- Format: MAJOR.MINOR.PATCH[-commits-gHASH] (ex: 1.8.0, 1.8.0-3-gabc1234)
- Fallback: VERSION file ou 0.0.0-dev si Git indisponible
2026-07-27 17:16:52 -04:00
bruno e213ad183e feat: support multi-format — images, CSV, JSON, TXT + fix PDF embed
CI / lint (push) Failing after 19s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / e2e (push) Has been skipped
CI / security (push) Successful in 14s
Desktop Build / build-windows (push) Has been cancelled
Desktop Build / build-linux (push) Has been cancelled
- PDF: <iframe> → <embed> (plus compatible webview Tauri)
- Images: .png .jpg .gif .svg .webp .bmp .ico → rendu standalone avec toolbar
- CSV: rendu en tableau HTML avec headers sticky
- JSON: affichage formaté avec indentation (html.escape)
- Texte: .txt .log .yml .yaml .py .js .sh .toml .ini → <pre> mono
- CSS: .image-viewer-body, .csv-table, .text-viewer, .json-viewer, .file-toolbar
2026-07-27 15:06:24 -04:00
bruno 2ba7138881 feat: affichage PDF avec TOC + frame-src CSP
CI / lint (push) Successful in 23s
CI / security (push) Successful in 14s
CI / test (push) Successful in 27s
CI / build (push) Successful in 8s
CI / e2e (push) Failing after 1m29s
Desktop Build / build-windows (push) Has been cancelled
Desktop Build / build-linux (push) Has been cancelled
- CSP: ajout frame-src 'self' http://127.0.0.1:* (permet iframe PDF)
- pdf_reader.py: extract_pdf_toc() — extrait les bookmarks/outline (pypdf + pymupdf)
- main.py: inclut pdf_toc dans la réponse /api/file pour les PDF
- viewer.js: sidebar TOC avec liens vers les pages (si bookmarks présents)
- style.css: layout .pdf-body flex + .pdf-toc sidebar 240px
2026-07-27 12:32:11 -04:00
bruno 1673531b43 fix: resolve all CI lint and security issues
CI / lint (push) Failing after 9s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / e2e (push) Has been skipped
CI / security (push) Failing after 12s
- ruff: 602→0 errors (428 auto-fixed, pyproject.toml ignores for FastAPI patterns)
- bandit: skip B310 (urllib for vault file access is intentional)
- Fixed SIM118 (dict.keys()→dict), PERF102, SIM113, SIM117
- Created pyproject.toml with ruff + bandit config
- 285 tests still pass
2026-07-24 10:38:44 -04:00
bruno 93d0bda627 feat: PDF support + Split View (P1 roadmap items)
#74 — Support PDF complet:
- backend/pdf_reader.py — pymupdf/pypdf text extraction
- .pdf added to SUPPORTED_EXTENSIONS in indexer.py
- PDF-aware file view API endpoint (metadata, text preview)
- Streaming endpoint /api/file/{vault}/pdf/stream
- Frontend PDF viewer with iframe, toolbar, download
- CSS for PDF viewer container

#75 — Split View (PaneManager):
- New module frontend/js/pane-manager.js — ES module
- 1-4 pane CSS grid with resize handles
- Split right/down via keyboard (Ctrl+Alt+\) or PaneManager API
- Persistence in localStorage (obsigate-panes)
- Collapse to single pane, per-pane active state

285 tests passent
2026-07-23 17:33:51 -04:00
bruno 23f4f9f4f2 fix: CSP — ajout cloudflareinsights.com + legacy.js deja corrige
CI / lint (push) Failing after 3s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / e2e (push) Has been skipped
CI / security (push) Failing after -3s
- CSP script-src: ajout https://static.cloudflareinsights.com
- legacy.js ligne 98 a deja le null-check if(filterBtn)
- L'erreur production vient d'un ancien deploy, sera resolu au prochain rebuild
2026-06-15 23:29:46 -04:00
bruno feeb46b873 feat: finalisation éditeur Forge + mode hors-ligne PWA + consolidation roadmap
CI / lint (push) Failing after 7s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / security (push) Failing after -7s
- Forge: Tab indent, Ctrl+K, auto-focus, AI status, messages erreur FR
- Forge: slash commands clean, cache-bust iframe, preview serveur
- Forge: iframe 82vh, hauteur auto, table inline, sélecteur langage
- PWA hors-ligne: IndexedDB (offline-db.js + offline.js)
- Mermaid: thème dark complet, highlight.js preview
- Backend: màj CSP, rename chain, bookmarks/history/shares
- Roadmap: consolidation dans docs/ROADMAP.md (75 items)
- Infra: docker-compose.podman.yml, suppression test-vault
- .gitignore: exclusion odysseus/
2026-06-14 20:27:37 -04:00
bruno 739b359de2 feat: Editor POC — multi-zone toolbar mockup (TOOLBAR_DESIGN_ANALYSIS.md)
CI / lint (push) Successful in 15s
CI / security (push) Successful in 9s
CI / build (push) Has been cancelled
CI / test (push) Has been cancelled
Add standalone /editor-poc page demonstrating all 5 toolbar zones:
- Zone 1: Top Fixed Bar (hamburger, title, AI glow button, save indicator)
- Zone 2a: Slash Command Menu (/ on empty line, 15 commands, filtering)
- Zone 2b: Bubble Menu (on text selection, formatting + AI dropdown)
- Zone 3: Floating Quick Insert (per-line, Image/PDF/Code/Table/AI)
- Zone 4: AI Side Panel (Ctrl+J toggle, chat, suggestions)
- Zone 5: Drag & Drop overlay with dashed border

Files:
- new: frontend/editor-poc.html (46KB standalone vanilla JS)
- backend/main.py: +/editor-poc route before catch-all
- frontend/index.html: Editor POC button in header menu
- frontend/js/config.js: initEditorPocBtn() function + export
- frontend/js/legacy.js: re-export initEditorPocBtn
- frontend/js/app.js + frontend/app.js: init call in both versions
2026-06-05 20:29:56 -04:00
bruno fff658d551 fix: add isascii() check in safe_name to prevent non-ASCII chars in PDF Content-Disposition header
CI / lint (push) Successful in 14s
CI / security (push) Successful in 8s
CI / test (push) Successful in 26s
CI / build (push) Successful in 4s
2026-06-05 08:24:32 -04:00
bruno 69e86c239b fix: CSP add cdn.jsdelivr.net, fix regex syntax, fix manifest PNG->SVG
CI / lint (push) Successful in 14s
CI / security (push) Successful in 8s
CI / test (push) Failing after 22s
CI / build (push) Has been skipped
2026-06-04 23:50:10 -04:00
bruno 47dc79683f feat: backup manager #15 #16 #17 #18 #19 — preview, restore, purge vault, compress, auto-backup
CI / lint (push) Failing after 4s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / security (push) Successful in 8s
#15: Click backup item to preview content in side panel (100KB limit)
#16: Purge all backups per vault (red 'Vider' button)
#17: /api/backups/compress — gzip backups older than N days
#18: /api/backups/auto — backup files modified since N hours
#19: Restore button per backup in manager with timestamp extraction
+ Professional side-panel preview, action buttons on hover, vault grouping
2026-06-04 21:36:05 -04:00
bruno 7af347b6be fix: /api/backups — fpath.st_size -> fpath.stat().st_size + try/except
CI / lint (push) Failing after 4s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / security (push) Successful in 8s
2026-06-04 20:24:58 -04:00
bruno fb87c9f7c3 feat: backup management page + max backups config + auto-cleanup
CI / lint (push) Failing after 4s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / security (push) Successful in 9s
- Config: max_backups_per_file (default 10) in config.json + UI field
- Auto-cleanup: _backup_file deletes oldest backups when limit exceeded
- New endpoints: GET /api/backups (list all), POST /api/backups/delete,
  POST /api/backups/purge (by file or vault)
- New module: backup-manager.js - full-page view with filter, grouped by
  vault/file, individual delete, per-file purge, stats
- Link from backup viewer footer: 'Gerer tous les backups'
2026-06-04 20:15:31 -04:00
bruno 8c5a5fd6b2 feat: side-by-side diff view + auto-save skips backup
CI / lint (push) Failing after 7s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / security (push) Successful in 10s
- Side-by-side diff: toggle button (columns-2/align-justify icon) in toolbar
  switches between unified and side-by-side view
- Backend: diff endpoint returns left_content + right_content for side-by-side
- Frontend: SequenceMatcher-based LCS diff for two-column rendering
- CSS: .backup-diff-sidebyside with left/right borders, empty cell styling
- Auto-save: PUT /save?backup=false skips backup creation (auto-save=no backup)
- Footer text updated to reflect backup-on-manual-save behavior
2026-06-04 18:37:14 -04:00
bruno 1e9ecf656f fix: add missing timezone import in main.py
CI / lint (push) Failing after 6s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / security (push) Successful in 9s
_list_backup_files uses datetime.fromtimestamp(ts, tz=timezone.utc)
but only datetime was imported, not timezone
2026-06-04 17:20:44 -04:00
bruno 21117da114 fix: backup listing uses same vault for read/write + global close handler
CI / lint (push) Failing after 16s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / security (push) Successful in 9s
- _get_backup_dir: now resolves relative path using the SPECIFIC vault's
  directory (matching _backup_file exactly), not the first vault in index
- Removed _get_backup_root (unused after refactor)
- backups.js: global document click handler for #backup-close (same
  pattern as Graph View's initGraphView)
2026-06-04 17:10:40 -04:00
bruno 67043a76e5 fix: store .obsigate-backup inside vault directory (not parent) for Docker write permissions
CI / lint (push) Failing after 5s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / security (push) Failing after 5s
- _backup_file: backup_root = vault_path / '.obsigate-backup' (was vault_parent / ...)
- _get_backup_root: same resolution — inside vault, not parent
- Fixes PermissionError on Docker where /vaults/ is read-only
2026-06-04 16:51:07 -04:00
bruno bc8083fd6b fix: backup storage relative to vault parent + global capture click handler for close button
CI / lint (push) Failing after 4s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / security (push) Failing after 5s
- _backup_file: resolve relative backup path against vault parent (survives CWD changes)
- _get_backup_root: same resolution logic for listing
- backups.js: global document-level capture-phase click handler for .backup-close-btn
  (works regardless of innerHTML replacements, text node targets, or event bubbling)
2026-06-04 15:22:02 -04:00
bruno 0b3a7d5c95 feat: roadmap items #7 #8 #9 #10 — recent files, semver tag, search filters, dedup
- #7: Section « Récents » dans la page d'accueil vault (5 derniers fichiers)
- #8: Git tag v1.8.0
- #9: Filtres search avancés created:/modified:/size: avec parsing dates/sizes
- #10: Déduplication IGNORED_DIRS (indexer.py source unique, watcher+main importent)
- ROADMAP: tous les items marqués FAIT, version → 1.9.0
2026-06-04 12:11:51 -04:00
bruno fc1a4e336e fix: vault home recursive + backup viewer fixes + AI status at startup
CI / security (push) Failing after 10m56s
CI / lint (push) Failing after 11m14s
CI / test (push) Has been cancelled
CI / build (push) Has been cancelled
- Vault home (#12): recursive file listing (rglob), rel_dir in metadata,
  'recursif' badge, updated roadmap description
- Backup viewer: fix close button (inline onclick + event delegation
  fallback for text nodes), improve error messages
- Backup API: remove response_model to prevent Pydantic 500 errors,
  add try/except + logging on backups/diff/restore endpoints,
  per-entry error handling in _list_backup_files,
  encoding safety (errors='replace') on read_text
- AI status: call /api/ai/status at startup in _initAIStatus
  instead of waiting for user click on sync badge
2026-06-04 09:28:57 -04:00
bruno 873c7a572f fix: statut IA — suppression endpoint redondant + lecture runtime des clés API
CI / lint (push) Failing after 4s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / security (push) Successful in 8s
Problème: l'indicateur affichait 'IA non configurée' alors que les
actions AI fonctionnaient.

Causes:
1. Deux endpoints /api/ai/status en conflit (main.py + ai_routes.py)
2. ai_routes.py lisait PROVIDERS évalué à l'import (os.getenv figé)
3. Cache côté frontend (_aiStatusChecked) empêchait de re-vérifier

Fixes:
- Suppression de l'endpoint redondant dans main.py
- ai_routes.py: lecture os.getenv au runtime (pas depuis PROVIDERS)
- sync.js: _checkAIStatus() appelée à chaque ouverture du panneau
2026-06-04 07:55:42 -04:00
bruno 8819d04cf2 fix: indexation inversée non-bloquante — run_in_executor
CI / lint (push) Failing after 7s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / security (push) Successful in 9s
L'étape 'Building inverted index...' au démarrage bloquait l'event loop
asyncio, empêchant le serveur de répondre aux requêtes HTTP (navigation,
refresh) jusqu'à la fin de l'indexation.

- init_inverted_index() exécuté dans ThreadPoolExecutor via run_in_executor
- get_inverted_index() ne fait plus de rebuild auto bloquant
- Le serveur répond immédiatement, même pendant l'indexation
2026-06-04 07:33:50 -04:00
bruno 7a4b3d1329 feat: points 11a + 11b — Indicateur AI Actif & Page d'accueil voute
CI / lint (push) Failing after 4s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / security (push) Successful in 8s
Point 11a — Indicateur AI Actif:
- Badge sync flashe en blanc pendant traitement AI (animation CSS)
- Événements ai-processing-start/end dans ai.js
- Section statut IA dans panneau sync (configurée/non configurée/en cours)
- Endpoint GET /api/ai/status vérifiant les clés API configurées

Point 11b — Page d'accueil de voute:
- Endpoint GET /api/vault/{vault}/files?dir=... (trié par mtime)
- Module vaulthome.js: affichage style résultats de recherche
- Breadcrumb navigable, mise à jour auto sur clic dossier dans tree
- Déclenché auto quand un vault spécifique est sélectionné
2026-06-03 22:36:31 -04:00
bruno d1ba15c3aa feat: diff viewer backups — point 6 (backups, diff, restore)
CI / lint (push) Failing after 4s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / security (push) Successful in 8s
- Backend: 3 nouveaux endpoints
  - GET /api/file/{vault}/backups — liste les backups horodatés
  - GET /api/file/{vault}/diff — diff unifié entre versions
  - POST /api/file/{vault}/restore — restaure une version (backup auto avant)
- Frontend: module backups.js — UI modale avec:
  - Sélecteur gauche (backup) / droite (version actuelle ou autre backup)
  - Diff coloré: vert pour ajouts, rouge pour suppressions
  - Bouton Restaurer avec confirmation
- Menu contextuel: clic droit fichier → 'Backups / Versions'
- CSS: styles complets pour la modale, toolbar, table de diff
- ROADMAP: point 6 marqué comme complété
2026-06-03 22:22:50 -04:00
bruno cfb3825dbe feat: drag & drop de fichiers dans l'arborescence (portion 5)
CI / lint (push) Failing after 4s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / security (push) Successful in 13s
- Backend: nouvel endpoint POST /api/move/{vault_name} pour déplacer
  fichiers et dossiers vers un autre répertoire parent
- Frontend: module dragdrop.js avec délégation d'événements sur le
  vault-tree (dragstart, dragover, dragleave, drop)
- CSS: styles pour drag-source, drag-valid-target, drag-over
- Protections: pas de drop sur soi-même, pas dans un enfant,
  pas de cross-vault
- Mise à jour de l'index et broadcast SSE après move
- ROADMAP: portion 5 marquée comme complétée
2026-06-03 13:56:27 -04:00
bruno b92fd3da08 feat: AI Editor — toolbar avec menus dropdown, multi-provider (DeepSeek/OpenRouter/Gemini)
CI / lint (push) Failing after 7s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / security (push) Successful in 11s
2026-05-30 16:44:42 -04:00
bruno 279e632b4b fix: remove_recent() — nettoie les fichiers récents après suppression
CI / lint (push) Successful in 13s
CI / security (push) Successful in 8s
CI / test (push) Successful in 15s
CI / build (push) Successful in 2s
2026-05-29 21:27:18 -04:00
bruno a373279b08 feat(graph): Phase 1+2 — full-vault, tag filter, backlinks, tooltips, depth slider
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / security (push) Has been cancelled
CI / build (push) Has been cancelled
Backend (main.py):
- GraphNode: added tags, incoming_count, outgoing_count
- GraphEdge: added 'backlink' relation
- GraphResponse: added 'scope' field
- api_graph: scope=full|directory, tag= filter, backlinks
- Full-vault tree walk with configurable depth 0-3
- Tag index from in-memory file index for fast filtering
- Incoming/outgoing link count per node

Frontend (graph.js + index.html):
- Theme-adaptive colors via CSS custom properties
- Depth slider (0-3) with live reload
- Full-vault toggle button (🌐 Tout / 📁 Dossier)
- Search input with tag filtering + visual highlighting
- Tooltip on hover (name, path, tags, link counts)
- Backlink edges rendered in red dashed
- Node size proportional to link count
- Larger modal (1000px, 85vh)
2026-05-28 14:46:22 -04:00
bruno 1a14927f36 fix: resolve all 28 mypy type errors + re-enable coverage in CI
CI / lint (push) Successful in 11s
CI / security (push) Successful in 7s
CI / test (push) Successful in 13s
CI / build (push) Successful in 1s
2026-05-28 12:57:30 -04:00
bruno 6fc43e2485 fix: ruff lint errors + bandit false positives + pip-audit non-blocking
CI / lint (push) Failing after 11s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / security (push) Successful in 7s
2026-05-28 12:41:31 -04:00
bruno edb9e98f81 test: add pytest suite - 97 tests, search + indexer + auth
Create comprehensive test suite with 97 passing tests:
- tests/conftest.py: fixtures (TestClient, temp vault dirs, index setup)
- tests/test_search.py (27 tests): tokenizer, snippets, highlight,
  tag filter, search API, advanced search, suggest, tags API
- tests/test_indexer.py (32 tests): frontmatter parsing, inline tags,
  title extraction, scan_vault, find_file_in_index, backlinks
- tests/test_auth.py (38 tests): password hashing, JWT create/decode,
  token revocation, user CRUD, login lockout, rate limiting, middleware

Also fix: lazy WeasyPrint import (graceful fallback when GTK missing),
add data/ to .gitignore (runtime files from test runs).
2026-05-27 22:06:27 -04:00
bruno a5afbb1dc1 fix: SSE sync indicator stuck on 'Connexion...' (3 fixes)
1. Move initSyncStatus() AFTER auth check — EventSource was connecting
   before the access_token cookie was available, causing 401 errors.

2. Reconnect SSE after login — Login form handler now calls
   IndexUpdateManager.connect() + showWelcome() after successful auth.

3. SSESafeGZipMiddleware — GZip buffering breaks Server-Sent Events
   streaming. Custom middleware subclass skips compression for
   /api/events endpoint (path-based bypass).
2026-05-27 21:40:32 -04:00