Files
Dashboard-CAS/backend/modules/config_manager.py
I. Alcaraz Salazar 81bcdcd696 feat: add admin panel at /admin for full dashboard configuration
- Backend: ConfigManager write methods (atomic YAML save), admin CRUD
  router for settings (display/odoo/refresh) and nodes, WS broadcast
  on config changes, fix nmap scan blocking event loop with to_thread
- Frontend: admin UI with tab navigation, overview dashboard, node
  CRUD table with modal form, Odoo/display/refresh settings pages,
  typed API wrappers, active views filtering, config_changed WS handler
- Infra: nginx no-cache headers for HTML, cache-forever for hashed assets
- Fixes: WebSocket reconnect loop (ref pattern), rotation index OOB
  when views shrink, mutable node list cache

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 02:34:05 +00:00

67 lines
2.2 KiB
Python

import os
from pathlib import Path
from typing import Any
import yaml
class ConfigManager:
def __init__(self, settings_path: str, services_path: str):
self._settings_path = settings_path
self._services_path = services_path
self._settings: dict[str, Any] = {}
self._services: dict[str, Any] = {}
def _load_yaml(self, path: str) -> dict[str, Any]:
p = Path(path)
if not p.exists():
return {}
with open(p) as f:
return yaml.safe_load(f) or {}
def _save_yaml(self, path: str, data: dict[str, Any]) -> None:
tmp = path + ".tmp"
with open(tmp, "w") as f:
yaml.dump(data, f, default_flow_style=False, allow_unicode=True, sort_keys=False)
os.replace(tmp, path)
def get_settings(self) -> dict[str, Any]:
if not self._settings:
self._settings = self._load_yaml(self._settings_path)
return self._settings
def get_nodes(self) -> list[dict[str, Any]]:
if not self._services:
self._services = self._load_yaml(self._services_path)
return list(self._services.get("nodes", []))
def get_node_by_ip(self, ip: str) -> dict[str, Any] | None:
for node in self.get_nodes():
if node.get("ip") == ip:
return node
return None
def get_network_scan_config(self) -> dict[str, Any]:
if not self._services:
self._services = self._load_yaml(self._services_path)
return self._services.get("network_scan", {})
def update_settings(self, section: str, values: dict[str, Any]) -> dict[str, Any]:
settings = self._load_yaml(self._settings_path)
if section not in settings:
settings[section] = {}
settings[section].update(values)
self._save_yaml(self._settings_path, settings)
self._settings = {}
return settings[section]
def save_nodes(self, nodes: list[dict[str, Any]]) -> None:
services = self._load_yaml(self._services_path)
services["nodes"] = nodes
self._save_yaml(self._services_path, services)
self._services = {}
def reload(self) -> None:
self._settings = {}
self._services = {}