feat: vault DIR — exclusions par vault et lecture seule déclarable (vault Spark)
This commit is contained in:
+44
-4
@@ -97,6 +97,13 @@ def load_vault_config() -> dict[str, dict[str, Any]]:
|
||||
Also reads optional configuration:
|
||||
- VAULT_N_ATTACHMENTS_PATH: relative path to attachments folder
|
||||
- VAULT_N_SCAN_ATTACHMENTS: "true"/"false" to enable/disable scanning
|
||||
- DIR_N_READONLY: "true"/"false" — vault DIR déclaré en lecture seule
|
||||
(les mutations sont refusées en 403, l'UI masque les actions d'écriture)
|
||||
- DIR_N_IGNORED_DIRS: noms de dossiers ignorés pour ce vault, séparés
|
||||
par des virgules (toute profondeur, en plus du global IGNORED_DIRS)
|
||||
- DIR_N_IGNORED_PATHS: motifs fnmatch sur le chemin relatif POSIX dans
|
||||
le vault, séparés par des virgules (un dossier qui matche cache tout
|
||||
son sous-arbre) — navigation, lecture et indexation les respectent
|
||||
|
||||
Returns:
|
||||
Dict mapping vault names to configuration dicts with keys:
|
||||
@@ -104,6 +111,9 @@ def load_vault_config() -> dict[str, dict[str, Any]]:
|
||||
- attachmentsPath: relative attachments folder (optional)
|
||||
- scanAttachmentsOnStartup: boolean (default True)
|
||||
- type: "VAULT" or "DIR"
|
||||
- readonly: boolean (DIR only, default False)
|
||||
- ignoredDirs: set of directory names (DIR only, default empty)
|
||||
- ignoredPaths: list of fnmatch patterns (DIR only, default empty)
|
||||
"""
|
||||
vaults: dict[str, dict[str, Any]] = {}
|
||||
n = 1
|
||||
@@ -131,12 +141,23 @@ def load_vault_config() -> dict[str, dict[str, Any]]:
|
||||
path = os.environ.get(f"DIR_{n}_PATH")
|
||||
if not name or not path:
|
||||
break
|
||||
|
||||
|
||||
readonly = os.environ.get(f"DIR_{n}_READONLY", "false").lower() == "true"
|
||||
ignored_dirs = {
|
||||
d.strip() for d in os.environ.get(f"DIR_{n}_IGNORED_DIRS", "").split(",") if d.strip()
|
||||
}
|
||||
ignored_paths = [
|
||||
p.strip() for p in os.environ.get(f"DIR_{n}_IGNORED_PATHS", "").split(",") if p.strip()
|
||||
]
|
||||
|
||||
vaults[name] = {
|
||||
"path": path,
|
||||
"attachmentsPath": None,
|
||||
"scanAttachmentsOnStartup": False,
|
||||
"type": "DIR"
|
||||
"type": "DIR",
|
||||
"readonly": readonly,
|
||||
"ignoredDirs": ignored_dirs,
|
||||
"ignoredPaths": ignored_paths,
|
||||
}
|
||||
n += 1
|
||||
|
||||
@@ -509,7 +530,8 @@ def _scan_vault(
|
||||
Args:
|
||||
vault_name: Display name of the vault.
|
||||
vault_path: Absolute filesystem path to the vault root.
|
||||
vault_cfg: Optional vault configuration dict (unused for indexing, kept for compatibility).
|
||||
vault_cfg: Optional vault configuration dict — ses exclusions
|
||||
(``ignoredDirs`` / ``ignoredPaths``) sont appliquées au scan.
|
||||
previous_files: Optional ``{relative_path: file_info}`` snapshot from a
|
||||
previous scan used for differential reuse.
|
||||
|
||||
@@ -529,6 +551,16 @@ def _scan_vault(
|
||||
|
||||
root_resolved = vault_root.resolve(strict=False)
|
||||
|
||||
# Exclusions propres au vault (DIR_N_IGNORED_DIRS / DIR_N_IGNORED_PATHS) :
|
||||
# elles s'ajoutent au global IGNORED_DIRS, sans le modifier. Le cfg est
|
||||
# celui passé par l'appelant — l'index global ne contient pas encore ce
|
||||
# vault pendant son premier scan, chemin_exclu() (qui lit l'index) ne
|
||||
# peut donc pas servir ici.
|
||||
from backend.services.vaults import chemin_exclu_dans_config
|
||||
|
||||
cfg = vault_cfg or {}
|
||||
ignored_names = IGNORED_DIRS | set(cfg.get("ignoredDirs") or [])
|
||||
|
||||
# BUG-032: walk without following symlinks and refuse any symlink that
|
||||
# escapes the vault root, so external data can never be indexed/exposed.
|
||||
for dirpath, dirnames, filenames in os.walk(vault_root, followlinks=False):
|
||||
@@ -537,7 +569,11 @@ def _scan_vault(
|
||||
# Prune ignored and symlinked directories in place (no recursion).
|
||||
dirnames[:] = [
|
||||
d for d in dirnames
|
||||
if d not in IGNORED_DIRS and not (current_dir / d).is_symlink()
|
||||
if d not in ignored_names
|
||||
and not (current_dir / d).is_symlink()
|
||||
and not chemin_exclu_dans_config(
|
||||
cfg, str((current_dir / d).relative_to(vault_root)).replace("\\", "/")
|
||||
)
|
||||
]
|
||||
|
||||
for d in dirnames:
|
||||
@@ -565,6 +601,10 @@ def _scan_vault(
|
||||
|
||||
rel_path_str = str(fpath.relative_to(vault_root)).replace("\\", "/")
|
||||
|
||||
# Exclusion propre au vault (motif de chemin, ex. un .env ciblé).
|
||||
if chemin_exclu_dans_config(cfg, rel_path_str):
|
||||
continue
|
||||
|
||||
ext = fpath.suffix.lower()
|
||||
# Also match extensionless files named like Dockerfile, Makefile
|
||||
basename_lower = fpath.name.lower()
|
||||
|
||||
Reference in New Issue
Block a user