"""Runtime platform settings with in-memory cache.""" import json import time import uuid from datetime import datetime, timezone from typing import Any, Optional from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select from app.models.platform_setting import PlatformSetting class SettingsManager: """Manage runtime platform settings with in-memory cache (60s TTL).""" _cache: dict[str, Any] = {} _cache_time: float = 0 CACHE_TTL = 60 @classmethod async def get(cls, key: str, db: AsyncSession, default: Any = None) -> Any: if time.time() - cls._cache_time < cls.CACHE_TTL and key in cls._cache: return cls._cache[key] result = await db.execute( select(PlatformSetting).where(PlatformSetting.setting_key == key) ) setting = result.scalar_one_or_none() if not setting: return default value = cls._parse_value(setting.setting_value, setting.data_type) cls._cache[key] = value cls._cache_time = time.time() return value @classmethod async def set(cls, key: str, value: Any, db: AsyncSession, user_id: uuid.UUID) -> None: result = await db.execute( select(PlatformSetting).where(PlatformSetting.setting_key == key) ) setting = result.scalar_one_or_none() str_value = json.dumps(value) if isinstance(value, (dict, list)) else str(value).lower() if isinstance(value, bool) else str(value) if setting: setting.setting_value = str_value setting.updated_by_id = user_id setting.updated_at = datetime.now(timezone.utc) else: setting = PlatformSetting( setting_key=key, setting_value=str_value, data_type=cls._infer_type(value), updated_by_id=user_id, ) db.add(setting) # Invalidate cache cls._cache.pop(key, None) cls._cache_time = 0 @classmethod async def get_all(cls, db: AsyncSession, include_sensitive: bool = False) -> dict[str, Any]: result = await db.execute(select(PlatformSetting)) settings = result.scalars().all() out = {} for s in settings: if s.is_sensitive and not include_sensitive: out[s.setting_key] = "***" else: out[s.setting_key] = cls._parse_value(s.setting_value, s.data_type) return out @staticmethod def _parse_value(value: Optional[str], data_type: str) -> Any: if value is None: return None if data_type == "boolean": return value.lower() == "true" if data_type == "integer": return int(value) if data_type == "json": return json.loads(value) return value @staticmethod def _infer_type(value: Any) -> str: if isinstance(value, bool): return "boolean" if isinstance(value, int): return "integer" if isinstance(value, (dict, list)): return "json" return "string"