Gregorfun's picture
Initial commit
32c5da4
from __future__ import annotations
import json
from datetime import datetime, timezone
from threading import Lock
from ..core.config import (
ADMIN_AUDIT_FILE,
CONTENT_PROFILE,
OUTPUT_RETENTION_DAYS,
RATE_LIMIT_PER_MINUTE,
SETTINGS_FILE,
)
DEFAULT_SETTINGS = {
"content_profile": CONTENT_PROFILE,
"rate_limit_per_minute": RATE_LIMIT_PER_MINUTE,
"output_retention_days": OUTPUT_RETENTION_DAYS,
"adult_enabled": False,
}
class SettingsStore:
def __init__(self) -> None:
self._lock = Lock()
def get(self) -> dict:
with self._lock:
return self._merged()
def update(self, patch: dict, actor: str) -> dict:
with self._lock:
current = self._merged()
current.update({k: v for k, v in patch.items() if v is not None})
self._write(current)
self._audit(actor=actor, patch=patch, current=current)
return current
def _merged(self) -> dict:
current = DEFAULT_SETTINGS.copy()
current.update(self._read())
return current
def _read(self) -> dict:
if not SETTINGS_FILE.exists():
return {}
try:
parsed = json.loads(SETTINGS_FILE.read_text(encoding="utf-8"))
return parsed if isinstance(parsed, dict) else {}
except json.JSONDecodeError:
return {}
def _write(self, data: dict) -> None:
SETTINGS_FILE.parent.mkdir(parents=True, exist_ok=True)
SETTINGS_FILE.write_text(json.dumps(data, indent=2), encoding="utf-8")
def _audit(self, actor: str, patch: dict, current: dict) -> None:
entry = {
"timestamp": datetime.now(timezone.utc).isoformat(),
"actor": actor,
"patch": patch,
"current": current,
}
ADMIN_AUDIT_FILE.parent.mkdir(parents=True, exist_ok=True)
with ADMIN_AUDIT_FILE.open("a", encoding="utf-8") as fp:
fp.write(json.dumps(entry, ensure_ascii=True) + "\n")