import json import os from pathlib import Path from backend.nodes.roles import NodeRole # Where node role overrides are persisted. Allow override via env var; # otherwise default to a user-writable path under /var/lib/fester (or # fall back to a tmp dir if neither is writable). def _resolve_role_db() -> Path: env = os.environ.get("FESTER_ROLE_DB") if env: return Path(env) candidates = [ Path("/etc/fester/node_roles.json"), Path("/var/lib/fester/node_roles.json"), Path.home() / ".fester" / "node_roles.json", Path("/tmp/fester_node_roles.json"), ] for c in candidates: try: c.parent.mkdir(parents=True, exist_ok=True) # touch to verify writability if not c.exists(): c.write_text("{}") else: # verify we can write to it with open(c, "a"): pass return c except (OSError, PermissionError): continue # Last resort: in-memory only (caller must handle None) return None ROLE_DB = _resolve_role_db() DEFAULT_ROLE = NodeRole(name="default") def load_roles(): if ROLE_DB is None or not ROLE_DB.exists(): return {} try: raw = json.loads(ROLE_DB.read_text()) except Exception: return {} roles = {} for node, cfg in raw.items(): try: roles[node] = NodeRole(**cfg) except TypeError: # Schema drift — skip malformed entries continue return roles def save_roles(roles): if ROLE_DB is None: return False raw = { node: role.__dict__ for node, role in roles.items() } try: ROLE_DB.write_text(json.dumps(raw, indent=2)) return True except (OSError, PermissionError): return False def get_role(node_name, roles): return roles.get(node_name, DEFAULT_ROLE)