"""Cache em memória para reduzir chamadas lentas ao zmprov.""" from __future__ import annotations import threading import time from typing import Any _LOCK = threading.Lock() _STORE: dict[str, tuple[float, Any, float]] = {} TTL_ACCOUNTS_LIST = 45 TTL_DOMAIN_EXISTS = 120 TTL_ACCOUNT_SUMMARY = 60 TTL_ALL_DOMAINS = 90 TTL_ORCH_LIST = 60 TTL_DOMAIN_DETAIL = 120 TTL_INFRA_STATUS = 90 CACHE_KEY_ALL_DOMAINS = "gad_all_domains" CACHE_KEY_ORCH_LIST = "orchestrated_domains_list" def get(key: str) -> Any | None: with _LOCK: item = _STORE.get(key) if not item: return None expires, value, _set_at = item if time.monotonic() > expires: del _STORE[key] return None return value def set(key: str, value: Any, ttl: int) -> None: now = time.monotonic() with _LOCK: _STORE[key] = (now + ttl, value, now) def entry_age_sec(key: str) -> int | None: with _LOCK: item = _STORE.get(key) if not item: return None expires, _value, set_at = item now = time.monotonic() if now > expires: return None return int(now - set_at) def invalidate_domain(domain: str) -> None: domain = domain.lower().strip() with _LOCK: for key in list(_STORE): if domain in key or key.endswith(f":{domain}") or f":{domain}:" in key: del _STORE[key] def invalidate_account(email: str) -> None: email = email.lower().strip() domain = email.split("@", 1)[1] if "@" in email else "" with _LOCK: _STORE.pop(f"account_summary:{email}", None) if domain: invalidate_domain(domain) def invalidate_all_domains() -> None: """Após zmprov cd/dd/purge — lista gad + orquestração Desk.""" with _LOCK: _STORE.pop(CACHE_KEY_ALL_DOMAINS, None) _STORE.pop(CACHE_KEY_ORCH_LIST, None)