105 lines
3.1 KiB
Python
105 lines
3.1 KiB
Python
"""Persistência e consulta de módulos activos."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
|
|
from app.modules.registry import MODULE_BY_ID, MODULES, role_module_defaults
|
|
|
|
MODULES_PATH = Path(os.getenv("DESK_MODULES_PATH", "/data/desk_modules.json"))
|
|
|
|
|
|
def _disabled_from_env() -> set[str]:
|
|
raw = os.getenv("DESK_MODULES_DISABLED", "").strip()
|
|
if not raw:
|
|
return set()
|
|
return {part.strip() for part in raw.split(",") if part.strip()}
|
|
|
|
|
|
def _load_overrides() -> dict[str, bool]:
|
|
if not MODULES_PATH.exists():
|
|
return {}
|
|
try:
|
|
data = json.loads(MODULES_PATH.read_text(encoding="utf-8"))
|
|
except (json.JSONDecodeError, OSError):
|
|
return {}
|
|
overrides: dict[str, bool] = {}
|
|
for key, val in data.items():
|
|
if isinstance(val, dict) and "enabled" in val:
|
|
overrides[key] = bool(val["enabled"])
|
|
elif isinstance(val, bool):
|
|
overrides[key] = val
|
|
return overrides
|
|
|
|
|
|
def _save_overrides(overrides: dict[str, bool]) -> None:
|
|
MODULES_PATH.parent.mkdir(parents=True, exist_ok=True)
|
|
payload = {mid: {"enabled": overrides[mid]} for mid in overrides if mid in MODULE_BY_ID}
|
|
MODULES_PATH.write_text(json.dumps(payload, indent=2), encoding="utf-8")
|
|
|
|
|
|
def is_module_enabled(module_id: str) -> bool:
|
|
mod = MODULE_BY_ID.get(module_id)
|
|
if not mod:
|
|
return False
|
|
if mod.locked:
|
|
return True
|
|
env_disabled = _disabled_from_env()
|
|
if module_id in env_disabled:
|
|
return False
|
|
overrides = _load_overrides()
|
|
if module_id in overrides:
|
|
return overrides[module_id]
|
|
return mod.default_enabled
|
|
|
|
|
|
def set_module_enabled(module_id: str, enabled: bool) -> None:
|
|
mod = MODULE_BY_ID.get(module_id)
|
|
if not mod:
|
|
raise KeyError(f"unknown module: {module_id}")
|
|
if mod.locked:
|
|
raise ValueError(f"module {module_id} is locked")
|
|
overrides = _load_overrides()
|
|
for m in MODULES:
|
|
if m.id not in overrides:
|
|
overrides[m.id] = is_module_enabled(m.id)
|
|
overrides[module_id] = enabled
|
|
_save_overrides(overrides)
|
|
|
|
|
|
def is_module_enabled_for_role(module_id: str, role: str) -> bool:
|
|
if not is_module_enabled(module_id):
|
|
return False
|
|
defaults = role_module_defaults(role)
|
|
if defaults is None:
|
|
return True
|
|
return module_id in defaults
|
|
|
|
|
|
def list_modules(role: str | None = None) -> list[dict]:
|
|
items = []
|
|
for mod in MODULES:
|
|
global_enabled = is_module_enabled(mod.id)
|
|
role_enabled = is_module_enabled_for_role(mod.id, role) if role else global_enabled
|
|
items.append(
|
|
{
|
|
"id": mod.id,
|
|
"label": mod.label,
|
|
"description": mod.description,
|
|
"locked": mod.locked,
|
|
"nav_views": list(mod.nav_views),
|
|
"enabled": global_enabled,
|
|
"enabled_for_role": role_enabled,
|
|
}
|
|
)
|
|
return items
|
|
|
|
|
|
def enabled_nav_views() -> set[str]:
|
|
views: set[str] = set()
|
|
for mod in MODULES:
|
|
if is_module_enabled(mod.id):
|
|
views.update(mod.nav_views)
|
|
return views
|