from typing import Any, Dict, List from sqlmodel import Session, select from models.platform import PlatformSetting from schemas.platform import SystemSettingPayload from services.platform_settings_core import ( ACTIVITY_EVENT_RETENTION_SETTING_KEY, PROTECTED_SETTING_KEYS, SYSTEM_SETTING_DEFINITIONS, _normalize_setting_key, _read_setting_value, _setting_item_from_row, _upsert_setting_row, ) REQUIRED_SYSTEM_SETTING_KEYS = tuple(SYSTEM_SETTING_DEFINITIONS.keys()) DEPRECATED_SYSTEM_SETTING_KEYS = ("command_auto_unlock_seconds",) def _prune_deprecated_system_settings(session: Session) -> None: removed = False for key in DEPRECATED_SYSTEM_SETTING_KEYS: row = session.get(PlatformSetting, key) if row is None: continue session.delete(row) removed = True if removed: session.commit() def validate_required_system_settings(session: Session) -> None: _prune_deprecated_system_settings(session) stmt = select(PlatformSetting.key).where(PlatformSetting.key.in_(REQUIRED_SYSTEM_SETTING_KEYS)) present = { str(key or "").strip() for key in session.exec(stmt).all() if str(key or "").strip() } missing = [key for key in REQUIRED_SYSTEM_SETTING_KEYS if key not in present] if missing: raise RuntimeError( "Database seed data is not initialized. " f"Missing sys_setting keys: {', '.join(missing)}. " "Run scripts/init-full-db.sh or apply scripts/sql/init-data.sql before starting the backend." ) def list_system_settings(session: Session, search: str = "") -> List[Dict[str, Any]]: validate_required_system_settings(session) stmt = select(PlatformSetting).order_by(PlatformSetting.sort_order.asc(), PlatformSetting.key.asc()) rows = session.exec(stmt).all() keyword = str(search or "").strip().lower() items = [_setting_item_from_row(row) for row in rows] if not keyword: return items return [ item for item in items if keyword in str(item["key"]).lower() or keyword in str(item["name"]).lower() or keyword in str(item["category"]).lower() or keyword in str(item["description"]).lower() ] def create_or_update_system_setting(session: Session, payload: SystemSettingPayload) -> Dict[str, Any]: normalized_key = _normalize_setting_key(payload.key) if normalized_key in DEPRECATED_SYSTEM_SETTING_KEYS: raise ValueError("Setting key has been removed") definition = SYSTEM_SETTING_DEFINITIONS.get(normalized_key, {}) row = _upsert_setting_row( session, payload.key, name=payload.name or str(definition.get("name") or payload.key), category=payload.category or str(definition.get("category") or "general"), description=payload.description or str(definition.get("description") or ""), value_type=payload.value_type or str(definition.get("value_type") or "json"), value=payload.value if payload.value is not None else definition.get("value"), is_public=payload.is_public, sort_order=payload.sort_order or int(definition.get("sort_order") or 100), ) if normalized_key == ACTIVITY_EVENT_RETENTION_SETTING_KEY: from services.platform_activity_service import prune_expired_activity_events prune_expired_activity_events(session, force=True) session.commit() session.refresh(row) return _setting_item_from_row(row) def delete_system_setting(session: Session, key: str) -> None: normalized_key = _normalize_setting_key(key) if normalized_key in PROTECTED_SETTING_KEYS: raise ValueError("Core platform settings cannot be deleted") row = session.get(PlatformSetting, normalized_key) if row is None: raise ValueError("Setting not found") session.delete(row) session.commit() def get_activity_event_retention_days(session: Session) -> int: validate_required_system_settings(session) row = session.get(PlatformSetting, ACTIVITY_EVENT_RETENTION_SETTING_KEY) if row is None: raise RuntimeError( "Database seed data is not initialized. " f"Missing sys_setting key: {ACTIVITY_EVENT_RETENTION_SETTING_KEY}. " "Run scripts/init-full-db.sh or apply scripts/sql/init-data.sql before starting the backend." ) try: value = int(_read_setting_value(row)) except Exception as exc: raise RuntimeError( f"sys_setting value is invalid for key: {ACTIVITY_EVENT_RETENTION_SETTING_KEY}. " "Fix the row manually or reapply scripts/sql/init-data.sql." ) from exc return max(1, min(3650, value))