feat: points 11a + 11b — Indicateur AI Actif & Page d'accueil voute
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é
This commit is contained in:
@@ -2162,6 +2162,21 @@ async def api_file_restore(
|
||||
raise HTTPException(status_code=500, detail=f"Error restoring file: {str(e)}")
|
||||
|
||||
|
||||
@app.get("/api/ai/status")
|
||||
async def api_ai_status(current_user=Depends(require_auth)):
|
||||
"""Return the AI configuration status.
|
||||
|
||||
Checks whether any AI provider API key is configured.
|
||||
Does NOT expose the key itself, only a boolean 'configured' flag.
|
||||
"""
|
||||
providers = ["DEEPSEEK_API_KEY", "OPENROUTER_API_KEY", "GEMINI_API_KEY"]
|
||||
configured = any(
|
||||
os.environ.get(env_var, "").strip()
|
||||
for env_var in providers
|
||||
)
|
||||
return {"configured": configured, "providers_checked": providers}
|
||||
|
||||
|
||||
@app.get("/api/file/{vault_name}/backlinks")
|
||||
async def api_file_backlinks(
|
||||
vault_name: str,
|
||||
@@ -3068,6 +3083,82 @@ async def api_update_vault_settings(vault_name: str, body: dict = Body(...), cur
|
||||
return updated
|
||||
|
||||
|
||||
@app.get("/api/vault/{vault_name}/files")
|
||||
async def api_vault_recent_files(
|
||||
vault_name: str,
|
||||
dir: str = Query("", description="Directory path within the vault (empty = root)"),
|
||||
limit: int = Query(100, description="Maximum number of files to return"),
|
||||
current_user=Depends(require_auth),
|
||||
):
|
||||
"""List files in a vault directory sorted by modification time (newest first).
|
||||
|
||||
Returns file metadata suitable for a vault home page display.
|
||||
Unlike /api/browse, this endpoint sorts by mtime and returns
|
||||
additional metadata (size, modified time, extension).
|
||||
|
||||
Args:
|
||||
vault_name: Name of the vault.
|
||||
dir: Relative directory path within the vault (empty for root).
|
||||
limit: Maximum files to return (default 100).
|
||||
|
||||
Returns:
|
||||
JSON with vault, directory, and list of file entries.
|
||||
"""
|
||||
if not check_vault_access(vault_name, current_user):
|
||||
raise HTTPException(status_code=403, detail=f"Accès refusé à la vault '{vault_name}'")
|
||||
|
||||
vault_data = get_vault_data(vault_name)
|
||||
if not vault_data:
|
||||
raise HTTPException(status_code=404, detail=f"Vault '{vault_name}' not found")
|
||||
|
||||
vault_root = Path(vault_data["path"])
|
||||
dir_path = _resolve_safe_path(vault_root, dir) if dir else vault_root
|
||||
|
||||
if not dir_path.exists() or not dir_path.is_dir():
|
||||
raise HTTPException(status_code=404, detail=f"Directory not found: {dir}")
|
||||
|
||||
files = []
|
||||
ignored = {'.obsidian', '.trash', '.git', '.obsigate-backup', '__pycache__', 'node_modules'}
|
||||
|
||||
try:
|
||||
for entry in dir_path.iterdir():
|
||||
if entry.name.startswith('.') and entry.name not in ('.', '..'):
|
||||
continue
|
||||
if entry.name in ignored:
|
||||
continue
|
||||
if not entry.is_file():
|
||||
continue
|
||||
|
||||
stat = entry.stat()
|
||||
rel_path = str(entry.relative_to(vault_root)).replace("\\", "/")
|
||||
ext = entry.suffix.lower() if entry.suffix else ""
|
||||
|
||||
files.append({
|
||||
"name": entry.name,
|
||||
"path": rel_path,
|
||||
"vault": vault_name,
|
||||
"size": stat.st_size,
|
||||
"modified": stat.st_mtime,
|
||||
"modified_iso": datetime.fromtimestamp(stat.st_mtime, tz=timezone.utc).isoformat(),
|
||||
"extension": ext.lstrip(".") if ext else "",
|
||||
})
|
||||
except PermissionError:
|
||||
raise HTTPException(status_code=403, detail="Permission denied reading directory")
|
||||
|
||||
# Sort by modification time, newest first
|
||||
files.sort(key=lambda f: f["modified"], reverse=True)
|
||||
|
||||
# Apply limit
|
||||
files = files[:limit]
|
||||
|
||||
return {
|
||||
"vault": vault_name,
|
||||
"directory": dir,
|
||||
"count": len(files),
|
||||
"files": files,
|
||||
}
|
||||
|
||||
|
||||
@app.get("/api/vaults/settings/all")
|
||||
async def api_get_all_vault_settings(current_user=Depends(require_auth)):
|
||||
"""Get UI display settings for all vaults.
|
||||
|
||||
Reference in New Issue
Block a user