import time from . import audit from .crypto import SecretBox from .db import Database MASK = "***" class SettingsStore: def __init__(self, db: Database, box: SecretBox): self._db = db self._box = box def get(self, key: str, default: str | None = None) -> str | None: rows = self._db.query("SELECT value, is_secret FROM settings WHERE key=?", (key,)) if not rows: return default value, is_secret = rows[0]["value"], rows[0]["is_secret"] return self._box.decrypt(value, key) if is_secret else value def get_int(self, key: str, default: int, minimum: int = 0) -> int: try: value = int(float(self.get(key) or default)) except ValueError: value = default return max(value, minimum) def is_set(self, key: str) -> bool: return bool(self._db.query("SELECT 1 FROM settings WHERE key=?", (key,))) def set(self, key: str, value: str, user: str, secret: bool = False) -> None: existed = self.is_set(key) old = self.get(key) stored = self._box.encrypt(value, key) if secret else value self._db.execute( "INSERT INTO settings(key, value, is_secret, updated_at) VALUES (?,?,?,?) " "ON CONFLICT(key) DO UPDATE SET value=excluded.value, " "is_secret=excluded.is_secret, updated_at=excluded.updated_at", (key, stored, int(secret), int(time.time())), ) if secret: audit.record(self._db, user, "setting.set", key, MASK if existed else None, MASK) else: audit.record(self._db, user, "setting.set", key, old, value)