Cache VM112/VM122 para lista e detalhe domínio, modal purge com loading animado, cards Escopo OPS clicáveis (camada + Spec + navegação), blocklist visível na UI, e documentação nas specs e anais de referência 20260625. Co-authored-by: Cursor <cursoragent@cursor.com>
75 lines
1.9 KiB
Python
75 lines
1.9 KiB
Python
"""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)
|