refactor: #85 T1 extrait le domaine health vers backend/routers (comportement inchange)
This commit is contained in:
+13
-1
@@ -6,7 +6,7 @@ Format basé sur [Keep a Changelog](https://keepachangelog.com/fr/1.1.0/),
|
|||||||
et [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
et [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
> **En cours de développement** : les changements à venir sont listés dans la section
|
> **En cours de développement** : les changements à venir sont listés dans la section
|
||||||
> [Unreleased](#unreleased). La dernière version livrée est **2.27.1**.
|
> [Unreleased](#unreleased). La dernière version livrée est **2.27.2**.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -14,6 +14,18 @@ et [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## [2.27.2] — 2026-09-26
|
||||||
|
|
||||||
|
### Modifié
|
||||||
|
|
||||||
|
- **#85 (T1) — extraction du domaine `health` hors du monolithe `backend/main.py`.**
|
||||||
|
`GET /api/health` et `GET /api/health/detailed` (admin) sont servis par le
|
||||||
|
nouveau `backend/routers/health.py` (monté dans `main.py`) et le modèle
|
||||||
|
`HealthResponse` déménage dans `backend/schemas.py` — chemins, réponses,
|
||||||
|
tags OpenAPI et authentification inchangés (aucun impact utilisateur).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## [2.27.1] — 2026-09-26
|
## [2.27.1] — 2026-09-26
|
||||||
|
|
||||||
### Modifié
|
### Modifié
|
||||||
|
|||||||
+3
-3
@@ -4,7 +4,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.
|
**Porte d'entrée web ultra-léger pour vos vaults Obsidian** — Accédez, naviguez et recherchez dans toutes vos notes Obsidian depuis n'importe quel appareil via une interface web moderne et responsive.
|
||||||
|
|
||||||
[]()
|
[]()
|
||||||
[](https://opensource.org/licenses/MIT)
|
[](https://opensource.org/licenses/MIT)
|
||||||
[](https://www.docker.com/)
|
[](https://www.docker.com/)
|
||||||
[](https://www.python.org/)
|
[](https://www.python.org/)
|
||||||
@@ -976,8 +976,8 @@ Ce projet est sous licence **MIT** — voir le fichier [LICENSE](LICENSE) pour l
|
|||||||
|
|
||||||
## 📝 Changelog
|
## 📝 Changelog
|
||||||
|
|
||||||
Consultez le [CHANGELOG.md](./CHANGELOG.md) pour l'historique complet de toutes les versions (v1.0.0 → v2.27.1).
|
Consultez le [CHANGELOG.md](./CHANGELOG.md) pour l'historique complet de toutes les versions (v1.0.0 → v2.27.2).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
*Projet : ObsiGate | Version : 2.27.1 | Dernière mise à jour : Septembre 2026*
|
*Projet : ObsiGate | Version : 2.27.2 | Dernière mise à jour : Septembre 2026*
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
**Ultra-light web gateway for your Obsidian vaults** — Access, browse, and search all your Obsidian notes from any device via a modern, responsive web interface.
|
**Ultra-light web gateway for your Obsidian vaults** — Access, browse, and search all your Obsidian notes from any device via a modern, responsive web interface.
|
||||||
|
|
||||||
[]()
|
[]()
|
||||||
[](https://opensource.org/licenses/MIT)
|
[](https://opensource.org/licenses/MIT)
|
||||||
[](https://www.docker.com/)
|
[](https://www.docker.com/)
|
||||||
[](https://www.python.org/)
|
[](https://www.python.org/)
|
||||||
@@ -1151,8 +1151,8 @@ This project is licensed under the **MIT License** - see the [LICENSE](LICENSE)
|
|||||||
|
|
||||||
## 📝 Changelog
|
## 📝 Changelog
|
||||||
|
|
||||||
See [CHANGELOG.md](./CHANGELOG.md) for the complete version history (v1.0.0 → v2.27.1).
|
See [CHANGELOG.md](./CHANGELOG.md) for the complete version history (v1.0.0 → v2.27.2).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
*Project: ObsiGate | Version: 2.27.1 | Last updated: September 2026*
|
*Project: ObsiGate | Version: 2.27.2 | Last updated: September 2026*
|
||||||
|
|||||||
+4
-131
@@ -398,19 +398,6 @@ class ReloadResponse(BaseModel):
|
|||||||
vaults: dict[str, Any] = Field(description="Per-vault file counts after reload")
|
vaults: dict[str, Any] = Field(description="Per-vault file counts after reload")
|
||||||
|
|
||||||
|
|
||||||
class HealthResponse(BaseModel):
|
|
||||||
"""Application health status."""
|
|
||||||
status: str = Field(description="Health status ('ok' or 'error')")
|
|
||||||
version: str = Field(description="Application version (x.y.z — latest release tag)")
|
|
||||||
vaults: int = Field(description="Number of configured vaults")
|
|
||||||
total_files: int = Field(description="Total indexed files across all vaults")
|
|
||||||
total_tokens: int = Field(description="Total indexed tokens (approx.) across all vaults", default=0)
|
|
||||||
last_full_index_ts: str = Field(description="ISO timestamp of last full index rebuild", default="")
|
|
||||||
uptime_seconds: int = Field(description="Server uptime in seconds", default=0)
|
|
||||||
git_describe: str = Field(default="", description="Full git describe string (commits beyond tag), empty if no git")
|
|
||||||
git_commit: str = Field(default="", description="Short HEAD commit hash, empty if no git")
|
|
||||||
|
|
||||||
|
|
||||||
class DirectoryCreateRequest(BaseModel):
|
class DirectoryCreateRequest(BaseModel):
|
||||||
"""Request to create a new directory."""
|
"""Request to create a new directory."""
|
||||||
path: str = Field(description="Relative path of the new directory")
|
path: str = Field(description="Relative path of the new directory")
|
||||||
@@ -867,7 +854,7 @@ async def lifespan(app: FastAPI):
|
|||||||
_search_executor = None
|
_search_executor = None
|
||||||
|
|
||||||
|
|
||||||
from backend.version import get_git_commit, get_git_describe, get_version
|
from backend.version import get_version
|
||||||
|
|
||||||
app = FastAPI(
|
app = FastAPI(
|
||||||
title="ObsiGate API",
|
title="ObsiGate API",
|
||||||
@@ -959,6 +946,7 @@ except Exception: # pragma: no cover - WeasyPrint/GTK missing
|
|||||||
from backend.ai_routes import router as ai_router
|
from backend.ai_routes import router as ai_router
|
||||||
from backend.bookslm_routes import router as bookslm_router
|
from backend.bookslm_routes import router as bookslm_router
|
||||||
from backend.export import ExportError, export_epub, export_html, export_md_bundle
|
from backend.export import ExportError, export_epub, export_html, export_md_bundle
|
||||||
|
from backend.routers.health import router as health_router
|
||||||
from backend.saved_searches import delete_saved, get_saved, save_search
|
from backend.saved_searches import delete_saved, get_saved, save_search
|
||||||
from backend.share import (
|
from backend.share import (
|
||||||
create_share,
|
create_share,
|
||||||
@@ -981,6 +969,7 @@ app.include_router(auth_router)
|
|||||||
app.include_router(ai_router)
|
app.include_router(ai_router)
|
||||||
app.include_router(bookslm_router)
|
app.include_router(bookslm_router)
|
||||||
app.include_router(skills_router)
|
app.include_router(skills_router)
|
||||||
|
app.include_router(health_router) # ROADMAP #85 T1 — System / health
|
||||||
|
|
||||||
# Admin Dashboard endpoints (system stats, audit logs, backups, stream)
|
# Admin Dashboard endpoints (system stats, audit logs, backups, stream)
|
||||||
try:
|
try:
|
||||||
@@ -1306,125 +1295,9 @@ def _render_markdown(raw_md: str, vault_name: str, current_file_path: Path | Non
|
|||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# API Endpoints
|
# API Endpoints — System / health : voir backend.routers.health (#85 T1)
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
@app.get("/api/health", response_model=HealthResponse)
|
|
||||||
async def api_health():
|
|
||||||
"""Health check endpoint for Docker and monitoring.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Application status, version, vault count and total file count.
|
|
||||||
"""
|
|
||||||
total_files = sum(len(v["files"]) for v in index.values())
|
|
||||||
total_tokens = sum(len(v.get("files", [])) * 1000 for v in index.values()) # rough approx
|
|
||||||
import time
|
|
||||||
|
|
||||||
from backend.indexer import _last_full_index_ts
|
|
||||||
uptime = int(time.time() - _SERVER_START_TIME) if '_SERVER_START_TIME' in globals() else 0
|
|
||||||
return {
|
|
||||||
"status": "ok",
|
|
||||||
"version": app.version,
|
|
||||||
"vaults": len(index),
|
|
||||||
"total_files": total_files,
|
|
||||||
"total_tokens": total_tokens,
|
|
||||||
"last_full_index_ts": _last_full_index_ts,
|
|
||||||
"uptime_seconds": uptime,
|
|
||||||
"git_describe": get_git_describe(),
|
|
||||||
"git_commit": get_git_commit(),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/api/health/detailed", response_model=HealthResponse)
|
|
||||||
async def api_health_detailed(current_user=Depends(require_admin)):
|
|
||||||
"""Detailed health check — admin only.
|
|
||||||
|
|
||||||
Returns enriched metrics including memory, disk, SSE connections, and backup stats.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import psutil
|
|
||||||
|
|
||||||
from backend.admin import _count_active_sessions, _get_disk_stats
|
|
||||||
from backend.indexer import _last_full_index_ts, index
|
|
||||||
|
|
||||||
total_files = sum(len(v["files"]) for v in index.values())
|
|
||||||
total_tokens = sum(len(v.get("files", [])) * 1000 for v in index.values())
|
|
||||||
import time
|
|
||||||
uptime = int(time.time() - _SERVER_START_TIME) if '_SERVER_START_TIME' in globals() else 0
|
|
||||||
|
|
||||||
# Memory
|
|
||||||
vm = psutil.virtual_memory()
|
|
||||||
mem_used_mb = round(vm.used / (1024 ** 2), 1)
|
|
||||||
mem_total_mb = round(vm.total / (1024 ** 2), 1)
|
|
||||||
mem_pct = round(vm.percent, 1)
|
|
||||||
|
|
||||||
# CPU
|
|
||||||
cpu_pct = psutil.cpu_percent(interval=None)
|
|
||||||
|
|
||||||
# Disk
|
|
||||||
disk_used_gb, disk_total_gb = _get_disk_stats()
|
|
||||||
disk_free_gb = round(disk_total_gb - disk_used_gb, 2)
|
|
||||||
disk_pct = round((disk_used_gb / disk_total_gb * 100) if disk_total_gb > 0 else 0, 1)
|
|
||||||
|
|
||||||
# SSE connections (approximation)
|
|
||||||
active_sessions = _count_active_sessions()
|
|
||||||
|
|
||||||
# Backups
|
|
||||||
from backend.admin import _scan_backups
|
|
||||||
backup_rows = _scan_backups()
|
|
||||||
total_backups = len(backup_rows)
|
|
||||||
total_backup_size_mb = round(sum(r["size"] for r in backup_rows) / (1024 ** 2), 2)
|
|
||||||
oldest_backup_age_days = 0.0
|
|
||||||
if backup_rows:
|
|
||||||
now_ts = int(time.time())
|
|
||||||
oldest_ts = min(r["timestamp"] for r in backup_rows)
|
|
||||||
oldest_backup_age_days = round((now_ts - oldest_ts) / 86400, 2)
|
|
||||||
|
|
||||||
# Index details
|
|
||||||
index_detail = {}
|
|
||||||
for name, data in index.items():
|
|
||||||
index_detail[name] = {
|
|
||||||
"file_count": len(data["files"]),
|
|
||||||
"tag_count": len(data["tags"]),
|
|
||||||
"token_count_approx": len(data.get("files", [])) * 1000,
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
"status": "ok",
|
|
||||||
"version": app.version,
|
|
||||||
"vaults": len(index),
|
|
||||||
"total_files": total_files,
|
|
||||||
"total_tokens": total_tokens,
|
|
||||||
"last_full_index_ts": _last_full_index_ts,
|
|
||||||
"uptime_seconds": uptime,
|
|
||||||
"git_describe": get_git_describe(),
|
|
||||||
"git_commit": get_git_commit(),
|
|
||||||
# Enriched fields
|
|
||||||
"memory": {
|
|
||||||
"used_mb": mem_used_mb,
|
|
||||||
"total_mb": mem_total_mb,
|
|
||||||
"percent": mem_pct,
|
|
||||||
},
|
|
||||||
"cpu": {
|
|
||||||
"percent": cpu_pct,
|
|
||||||
},
|
|
||||||
"disk": {
|
|
||||||
"used_gb": disk_used_gb,
|
|
||||||
"total_gb": disk_total_gb,
|
|
||||||
"free_gb": disk_free_gb,
|
|
||||||
"percent": disk_pct,
|
|
||||||
},
|
|
||||||
"connections": {
|
|
||||||
"active_sse": active_sessions,
|
|
||||||
},
|
|
||||||
"backups": {
|
|
||||||
"total_count": total_backups,
|
|
||||||
"total_size_mb": total_backup_size_mb,
|
|
||||||
"oldest_age_days": oldest_backup_age_days,
|
|
||||||
},
|
|
||||||
"index": index_detail,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/api/vaults", response_model=list[VaultInfo])
|
@app.get("/api/vaults", response_model=list[VaultInfo])
|
||||||
async def api_vaults(current_user=Depends(require_auth)):
|
async def api_vaults(current_user=Depends(require_auth)):
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
"""ObsiGate — routers FastAPI par domaine (ROADMAP #85).
|
||||||
|
|
||||||
|
Découpage progressif du monolithe ``backend/main.py`` : chaque module de ce
|
||||||
|
paquet expose un ``APIRouter`` monté par ``main.py``. Les handlers sont
|
||||||
|
déplacés sans changement de comportement (mêmes chemins, mêmes modèles de
|
||||||
|
réponse, mêmes dépendances d'authentification).
|
||||||
|
"""
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
"""System health endpoints (ROADMAP #85, tranche 1).
|
||||||
|
|
||||||
|
Handlers déplacés depuis :mod:`backend.main` sans changement de
|
||||||
|
comportement : mêmes chemins (``/api/health``, ``/api/health/detailed``),
|
||||||
|
même ``response_model`` (:class:`backend.schemas.HealthResponse`), même
|
||||||
|
dépendance admin. Seule différence : la version est lue via
|
||||||
|
:func:`backend.version.get_version` au lieu de ``app.version`` (valeur
|
||||||
|
identique, figée au démarrage depuis le fichier ``VERSION``).
|
||||||
|
|
||||||
|
Note : ``uptime_seconds`` reprend l'expression d'origine
|
||||||
|
(``'_SERVER_START_TIME' in globals()``), qui vaut toujours 0 — le global
|
||||||
|
n'est défini nulle part dans ``backend.main`` (voir ``backend.admin`` qui
|
||||||
|
possède son propre compteur). Ce comportement est préservé tel quel ; le
|
||||||
|
corriger fera l'objet d'une tranche ultérieure avec test dédié.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from fastapi import APIRouter, Depends
|
||||||
|
|
||||||
|
from backend.auth.middleware import require_admin
|
||||||
|
from backend.indexer import index
|
||||||
|
from backend.schemas import HealthResponse
|
||||||
|
from backend.version import get_git_commit, get_git_describe, get_version
|
||||||
|
|
||||||
|
router = APIRouter(tags=["System"])
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/api/health", response_model=HealthResponse)
|
||||||
|
async def api_health():
|
||||||
|
"""Health check endpoint for Docker and monitoring.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Application status, version, vault count and total file count.
|
||||||
|
"""
|
||||||
|
total_files = sum(len(v["files"]) for v in index.values())
|
||||||
|
total_tokens = sum(len(v.get("files", [])) * 1000 for v in index.values()) # rough approx
|
||||||
|
import time
|
||||||
|
|
||||||
|
from backend.indexer import _last_full_index_ts
|
||||||
|
# `_SERVER_START_TIME` n'existe dans aucun module (comportement d'origine
|
||||||
|
# préservé : uptime toujours 0 — voir docstring du module).
|
||||||
|
uptime = int(time.time() - _SERVER_START_TIME) if '_SERVER_START_TIME' in globals() else 0 # noqa: F821
|
||||||
|
return {
|
||||||
|
"status": "ok",
|
||||||
|
"version": get_version(),
|
||||||
|
"vaults": len(index),
|
||||||
|
"total_files": total_files,
|
||||||
|
"total_tokens": total_tokens,
|
||||||
|
"last_full_index_ts": _last_full_index_ts,
|
||||||
|
"uptime_seconds": uptime,
|
||||||
|
"git_describe": get_git_describe(),
|
||||||
|
"git_commit": get_git_commit(),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/api/health/detailed", response_model=HealthResponse)
|
||||||
|
async def api_health_detailed(current_user=Depends(require_admin)):
|
||||||
|
"""Detailed health check — admin only.
|
||||||
|
|
||||||
|
Returns enriched metrics including memory, disk, SSE connections, and backup stats.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import psutil
|
||||||
|
|
||||||
|
from backend.admin import _count_active_sessions, _get_disk_stats
|
||||||
|
from backend.indexer import _last_full_index_ts, index
|
||||||
|
|
||||||
|
total_files = sum(len(v["files"]) for v in index.values())
|
||||||
|
total_tokens = sum(len(v.get("files", [])) * 1000 for v in index.values())
|
||||||
|
import time
|
||||||
|
uptime = int(time.time() - _SERVER_START_TIME) if '_SERVER_START_TIME' in globals() else 0 # noqa: F821 — voir ci-dessus
|
||||||
|
|
||||||
|
# Memory
|
||||||
|
vm = psutil.virtual_memory()
|
||||||
|
mem_used_mb = round(vm.used / (1024 ** 2), 1)
|
||||||
|
mem_total_mb = round(vm.total / (1024 ** 2), 1)
|
||||||
|
mem_pct = round(vm.percent, 1)
|
||||||
|
|
||||||
|
# CPU
|
||||||
|
cpu_pct = psutil.cpu_percent(interval=None)
|
||||||
|
|
||||||
|
# Disk
|
||||||
|
disk_used_gb, disk_total_gb = _get_disk_stats()
|
||||||
|
disk_free_gb = round(disk_total_gb - disk_used_gb, 2)
|
||||||
|
disk_pct = round((disk_used_gb / disk_total_gb * 100) if disk_total_gb > 0 else 0, 1)
|
||||||
|
|
||||||
|
# SSE connections (approximation)
|
||||||
|
active_sessions = _count_active_sessions()
|
||||||
|
|
||||||
|
# Backups
|
||||||
|
from backend.admin import _scan_backups
|
||||||
|
backup_rows = _scan_backups()
|
||||||
|
total_backups = len(backup_rows)
|
||||||
|
total_backup_size_mb = round(sum(r["size"] for r in backup_rows) / (1024 ** 2), 2)
|
||||||
|
oldest_backup_age_days = 0.0
|
||||||
|
if backup_rows:
|
||||||
|
now_ts = int(time.time())
|
||||||
|
oldest_ts = min(r["timestamp"] for r in backup_rows)
|
||||||
|
oldest_backup_age_days = round((now_ts - oldest_ts) / 86400, 2)
|
||||||
|
|
||||||
|
# Index details
|
||||||
|
index_detail = {}
|
||||||
|
for name, data in index.items():
|
||||||
|
index_detail[name] = {
|
||||||
|
"file_count": len(data["files"]),
|
||||||
|
"tag_count": len(data.get("tags", [])),
|
||||||
|
"token_count_approx": len(data.get("files", [])) * 1000,
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
"status": "ok",
|
||||||
|
"version": get_version(),
|
||||||
|
"vaults": len(index),
|
||||||
|
"total_files": total_files,
|
||||||
|
"total_tokens": total_tokens,
|
||||||
|
"last_full_index_ts": _last_full_index_ts,
|
||||||
|
"uptime_seconds": uptime,
|
||||||
|
"git_describe": get_git_describe(),
|
||||||
|
"git_commit": get_git_commit(),
|
||||||
|
# Enriched fields
|
||||||
|
"memory": {
|
||||||
|
"used_mb": mem_used_mb,
|
||||||
|
"total_mb": mem_total_mb,
|
||||||
|
"percent": mem_pct,
|
||||||
|
},
|
||||||
|
"cpu": {
|
||||||
|
"percent": cpu_pct,
|
||||||
|
},
|
||||||
|
"disk": {
|
||||||
|
"used_gb": disk_used_gb,
|
||||||
|
"total_gb": disk_total_gb,
|
||||||
|
"free_gb": disk_free_gb,
|
||||||
|
"percent": disk_pct,
|
||||||
|
},
|
||||||
|
"connections": {
|
||||||
|
"active_sse": active_sessions,
|
||||||
|
},
|
||||||
|
"backups": {
|
||||||
|
"total_count": total_backups,
|
||||||
|
"total_size_mb": total_backup_size_mb,
|
||||||
|
"oldest_age_days": oldest_backup_age_days,
|
||||||
|
},
|
||||||
|
"index": index_detail,
|
||||||
|
}
|
||||||
@@ -408,6 +408,31 @@ class DashboardResponse(BaseModel):
|
|||||||
total_images: int = 0
|
total_images: int = 0
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# System / health (#85 — extrait de backend.main, comportement inchangé)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
class HealthResponse(BaseModel):
|
||||||
|
"""Application health status.
|
||||||
|
|
||||||
|
Déplacé depuis :mod:`backend.main` sans modification : pas de
|
||||||
|
``extra="allow"`` ici, pour préserver la validation actuelle des
|
||||||
|
réponses (les champs enrichis de ``/api/health/detailed`` restent
|
||||||
|
filtrés comme avant).
|
||||||
|
"""
|
||||||
|
|
||||||
|
status: str = Field(description="Health status ('ok' or 'error')")
|
||||||
|
version: str = Field(description="Application version (x.y.z — latest release tag)")
|
||||||
|
vaults: int = Field(description="Number of configured vaults")
|
||||||
|
total_files: int = Field(description="Total indexed files across all vaults")
|
||||||
|
total_tokens: int = Field(description="Total indexed tokens (approx.) across all vaults", default=0)
|
||||||
|
last_full_index_ts: str = Field(description="ISO timestamp of last full index rebuild", default="")
|
||||||
|
uptime_seconds: int = Field(description="Server uptime in seconds", default=0)
|
||||||
|
git_describe: str = Field(default="", description="Full git describe string (commits beyond tag), empty if no git")
|
||||||
|
git_commit: str = Field(default="", description="Short HEAD commit hash, empty if no git")
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Webhooks, sharing & conflicts
|
# Webhooks, sharing & conflicts
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
Generated
+1
-1
@@ -2626,7 +2626,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "obsigate-desktop"
|
name = "obsigate-desktop"
|
||||||
version = "2.27.1"
|
version = "2.27.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "obsigate-desktop"
|
name = "obsigate-desktop"
|
||||||
version = "2.27.1"
|
version = "2.27.2"
|
||||||
description = "ObsiGate Desktop — Porte d'entrée native pour vos vaults Obsidian"
|
description = "ObsiGate Desktop — Porte d'entrée native pour vos vaults Obsidian"
|
||||||
authors = ["Bruno Charest"]
|
authors = ["Bruno Charest"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://raw.githubusercontent.com/nicedoc/obsigate/main/desktop/tauri.conf.schema.json",
|
"$schema": "https://raw.githubusercontent.com/nicedoc/obsigate/main/desktop/tauri.conf.schema.json",
|
||||||
"productName": "ObsiGate",
|
"productName": "ObsiGate",
|
||||||
"version": "2.27.1",
|
"version": "2.27.2",
|
||||||
"identifier": "com.obsigate.desktop",
|
"identifier": "com.obsigate.desktop",
|
||||||
"build": {
|
"build": {
|
||||||
"frontendDist": "../frontend",
|
"frontendDist": "../frontend",
|
||||||
|
|||||||
+4
-3
@@ -1,6 +1,6 @@
|
|||||||
# ObsiGate — Roadmap
|
# ObsiGate — Roadmap
|
||||||
|
|
||||||
> **Version :** 2.27.1 | **Dernière mise à jour :** 2026-09-26
|
> **Version :** 2.27.2 | **Dernière mise à jour :** 2026-09-26
|
||||||
> **Ce fichier ne contient que le travail à venir** (🔵 En cours + ⚪ Backlog) et un index compact
|
> **Ce fichier ne contient que le travail à venir** (🔵 En cours + ⚪ Backlog) et un index compact
|
||||||
> vers les fonctionnalités livrées.
|
> vers les fonctionnalités livrées.
|
||||||
> - **Méthode de livraison à appliquer pour toute tâche : [DELIVERY_WORKFLOW.md](./DELIVERY_WORKFLOW.md)**
|
> - **Méthode de livraison à appliquer pour toute tâche : [DELIVERY_WORKFLOW.md](./DELIVERY_WORKFLOW.md)**
|
||||||
@@ -67,10 +67,11 @@
|
|||||||
|
|
||||||
- **Effort :** 8-12 jours | **Impact :** 🟡 | **Zone :** backend
|
- **Effort :** 8-12 jours | **Impact :** 🟡 | **Zone :** backend
|
||||||
- **Décision 2026-09-26 : prioritaire (axe Dette & sécurité).**
|
- **Décision 2026-09-26 : prioritaire (axe Dette & sécurité).**
|
||||||
|
- **Statut :** 🔵 en cours depuis 2026-09-26 — découpe par tranches à impact minimal (comportement inchangé, un domaine par commit). **T1 livrée (v2.27.2) :** `health` (`/api/health`, `/api/health/detailed` → `backend/routers/health.py`, `HealthResponse` → `schemas.py`).
|
||||||
- **Description :** extraire le monolithe `backend/main.py` (~4 827 lignes au 2026-09-26, ~17 % du backend) en routers FastAPI par domaine et rendre persistant l'état qui ne l'est pas (index de recherche, JTI révoqués, compteurs de rate-limit) pour préparer le multi-nœuds. L'état mémoire actuel (index, inverted index, vecteurs sémantiques, `SSEManager`, collab) rend le multi-workers unsafe.
|
- **Description :** extraire le monolithe `backend/main.py` (~4 827 lignes au 2026-09-26, ~17 % du backend) en routers FastAPI par domaine et rendre persistant l'état qui ne l'est pas (index de recherche, JTI révoqués, compteurs de rate-limit) pour préparer le multi-nœuds. L'état mémoire actuel (index, inverted index, vecteurs sémantiques, `SSEManager`, collab) rend le multi-workers unsafe.
|
||||||
- **Sous-tâches :**
|
- **Sous-tâches :**
|
||||||
- [ ] Routers par domaine : files, search, share, webhooks, plugins, collab, admin, ai — `main.py` conservé comme assemblage (< 500 lignes) ; dédupliquer les modèles Pydantic vers `schemas.py`
|
- [ ] Routers par domaine : files, search, share, webhooks, plugins, collab, admin, ai — `main.py` conservé comme assemblage (< 500 lignes) ; dédupliquer les modèles Pydantic vers `schemas.py`. **Avancement :** `health` ✅ (T1, `backend/routers/health.py`) ; `tools/registry.py` existe déjà (permissions/quotas/redaction — à compléter, pas à créer)
|
||||||
- [ ] Créer `tools/registry.py` (inexistant) : contrat central des outils IA (permissions, quotas, redaction)
|
- [ ] Compléter `tools/registry.py` (existant : permissions/quotas/redaction) comme contrat central des outils IA si des manques sont constatés
|
||||||
- [ ] Persister index, JTI révoqués et compteurs de rate-limit (SQLite par défaut, Redis en option multi-nœuds ; le rate-limit actuel est in-memory mono-process)
|
- [ ] Persister index, JTI révoqués et compteurs de rate-limit (SQLite par défaut, Redis en option multi-nœuds ; le rate-limit actuel est in-memory mono-process)
|
||||||
- [ ] Verrous asyncio autour de l'index global et des stores JSON ; auditer les `except Exception` larges (> 100 occurrences) : best-effort (backup/audit) vs masquage d'erreur (erreurs typées 4xx/5xx + test)
|
- [ ] Verrous asyncio autour de l'index global et des stores JSON ; auditer les `except Exception` larges (> 100 occurrences) : best-effort (backup/audit) vs masquage d'erreur (erreurs typées 4xx/5xx + test)
|
||||||
- [ ] Extraire le service de partage public (expiration, révocation, quotas)
|
- [ ] Extraire le service de partage public (expiration, révocation, quotas)
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "obsigate",
|
"name": "obsigate",
|
||||||
"version": "2.27.1",
|
"version": "2.27.2",
|
||||||
"description": "**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.",
|
"description": "**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.",
|
||||||
"main": "patch.js",
|
"main": "patch.js",
|
||||||
"directories": {
|
"directories": {
|
||||||
|
|||||||
Reference in New Issue
Block a user