feat: vault DIR — exclusions par vault et lecture seule déclarable (vault Spark)

This commit is contained in:
Spark
2026-11-03 04:59:38 +00:00
parent b42a81018c
commit 4c7195de2c
13 changed files with 545 additions and 46 deletions
+79
View File
@@ -0,0 +1,79 @@
# Vault DIR « Spark » — exclusions par vault et lecture seule déclarable
## Objectif
Permettre d'exposer dans ObsiGate des répertoires techniques (code du
service d'agents, workspaces du compagnon, coquilles d'exécuteurs) sous un
vault `DIR` monté en lecture seule — le vault « Spark » — sans jamais
rendre visibles leurs zones sensibles (profils et clés des exécuteurs,
`.home` des workspaces, `.venv`, etc.).
Avant ce chantier, deux manques le bloquaient proprement :
1. Les exclusions n'existaient qu'au niveau de l'indexeur (`IGNORED_DIRS`,
global) : la navigation et la lecture montraient le disque tel quel.
2. La lecture seule d'un vault n'était ni déclarable ni affichée : les
menus contextuels proposaient renommer / déplacer / supprimer, qui
échouaient ensuite côté serveur (montage `:ro`).
## Contrat de configuration (vaults DIR seulement)
Lues par `load_vault_config()` (`backend/indexer.py`, boucle `DIR_N`),
rangées dans le dict `config` du vault ; les vaults `VAULT` ne sont pas
touchés et les défauts (vide / faux) conservent le comportement actuel :
| Variable | Forme | Effet |
|---|---|---|
| `DIR_N_READONLY` | `true` / `false` | Mutations refusées en 403 `read_only` ; `readonly: true` exposé par `/api/vaults` ; l'UI masque les actions d'écriture |
| `DIR_N_IGNORED_DIRS` | `.home,.venv` | Noms de dossiers cachés à toute profondeur, en plus du global |
| `DIR_N_IGNORED_PATHS` | `Executeurs/*/data,Executeurs/*/.env` | Motifs fnmatch sur le chemin relatif POSIX ; un dossier qui matche cache tout son sous-arbre |
Exemple visé pour le vault Spark :
```
DIR_1_NAME=Spark
DIR_1_PATH=/vaults/Spark
DIR_1_READONLY=true
DIR_1_IGNORED_DIRS=.home,.venv,__pycache__,node_modules,.git
DIR_1_IGNORED_PATHS=Executeurs/*/data,Executeurs/*/.env,*/compagnon.json
```
(le montage lui-même — volume `:ro`, arborescence assemblée sur l'hôte —
relève de la configuration de déploiement, pas d'ObsiGate.)
## Implémentation
- **Helper central** : `chemin_exclu(vault_name, rel_path)` et
`chemin_exclu_dans_config(config, rel_path)` dans
`backend/services/vaults.py` — segment présent dans `ignoredDirs`, ou
fnmatch du chemin ou d'un de ses préfixes contre `ignoredPaths`.
Tolérant si vault / config absents. `vault_readonly(vault_name)` au
même endroit.
- **Navigation** : `browse_directory` filtre les items exclus (et leurs
compteurs d'enfants) ; naviguer directement dans un chemin exclu
répond 404. `list_all_files` et `list_paths` filtrent aussi.
- **Lecture** : un chemin exclu est indiscernable d'un chemin inexistant
(404, sans détail sur la règle) — `read_raw_file` / `read_file_text`
(`backend/services/files.py`), route `GET /api/file/{vault}` et route
de téléchargement (`backend/routers/files_read.py`).
- **Indexation** : le scan (`_scan_vault`, qui reçoit le `vault_cfg`)
élague les dossiers exclus et saute les fichiers exclus, en plus du
global `IGNORED_DIRS` inchangé.
- **Lecture seule** : `_ensure_writable(root, vault_name)` dans
`backend/services/mutations.py` refuse d'abord sur la déclaration
(`DIR_N_READONLY`), avant le garde filesystem existant — toutes les
mutations du service (créer, éditer, renommer, déplacer, supprimer,
upload, xlsx/csv) passent par ce point.
- **API / UI** : `VaultInfo.readonly` (`backend/schemas.py`) rempli par
`list_accessible_vaults` ; côté frontend, `vaultIsReadonly(nom)` dans
`frontend/js/sidebar.js` alimente `ContextMenuManager`,
`attachTreeItemActionButton` et `attachTreeItemLongPress` pour le
vault et ses dossiers / fichiers (aucun autre fichier JS touché).
## Tests
`tests/test_spark_vault_dir.py` : parsing des trois variables, helper
`chemin_exclu` (noms et motifs), navigation filtrée, lectures exclues en
404 (vue fichier, raw, téléchargement), fichier non exclu lisible,
indexation filtrée, mutation refusée en 403 sur vault readonly et
`/api/vaults` exposant `readonly=true`.