SPB Git forge

spb/house-ka

Public
18commits 1branches 0releases
1.9 MBsize
maindefault branch
19 days agolast push
Python 67% TypeScript 18.2% CSS 14.4%
3.4 KB · 101 lines python
Raw Blame History
1# -----------------------------------------------------------------------------2# Immo-Ka — Agrégateur de propriétés à vendre (province de Québec)3# Auteur : Simon-Pierre Boucher — contact@spboucher.ai4# hubfav.py : favoris unifiés « Mon univers Ka » — Immo·Ka n'a AUCUN stockage5#   local de favoris : le hub Groupe KA (groupe-ka.com) est le magasin central.6#   Chaque ♥ est poussé au hub (POST signé, SYNCHRONE : c'est l'action7#   utilisateur) et la liste est relue du hub (GET signé, cache mémoire 30 s8#   par ka_id, [] sur toute erreur). sig = HMAC-SHA256(KA_SSO_SECRET,9#   "immo-ka.<ka_id>.<ts>") hex — même secret que le SSO (ts ±5 min).10#   Config .env : KA_SSO_SECRET, KA_HUB_URL (optionnel).11# -----------------------------------------------------------------------------12from __future__ import annotations1314import hashlib15import hmac16import os17import threading18import time1920import requests2122CLIENT_ID = "house-ka"23KA_HUB_URL = os.environ.get("KA_HUB_URL", "https://www.groupe-ka.com").rstrip("/")24LIST_TTL = 30       # secondes — cache mémoire de hub_list par ka_id2526_cache: dict[str, tuple[float, list]] = {}27_lock = threading.Lock()282930def _sig(ka_id: str, ts: int) -> str | None:31    secret = os.environ.get("KA_SSO_SECRET")32    if not secret:33        return None34    return hmac.new(secret.encode(),35                    f"{CLIENT_ID}.{ka_id}.{ts}".encode(),36                    hashlib.sha256).hexdigest()373839def hub_toggle(ka_id: str, action: str, item: dict) -> bool:40    """Pousse un ♥ au hub (action « add » ou « remove ») — SYNCHRONE.4142    C'est l'action utilisateur : on attend la réponse du hub (timeout 6 s)43    pour que le GET qui suit reflète l'état réel. True si le hub a accepté.44    """45    if not ka_id or not str(ka_id).startswith("ka-"):46        return False    # compte legacy non relié au hub47    ts = int(time.time())48    sig = _sig(ka_id, ts)49    if not sig:50        return False51    try:52        r = requests.post(f"{KA_HUB_URL}/api/sso/favorites", timeout=6, json={53            "client_id": CLIENT_ID, "ka_id": ka_id,54            "ts": str(ts), "sig": sig,55            "action": action, "item": item,56        })57        return r.status_code == 20058    except Exception:59        return False606162def hub_list(ka_id: str) -> list:63    """Favoris immo-ka du membre, lus du hub (GET signé, timeout 5 s).6465    Cache mémoire 30 s par ka_id ; [] sur toute erreur (réseau, 401, 5xx…)66    — l'erreur n'est PAS mise en cache pour réessayer au prochain appel.67    """68    if not ka_id:69        return []70    now = time.time()71    with _lock:72        hit = _cache.get(ka_id)73        if hit and now - hit[0] < LIST_TTL:74            return hit[1]75    ts = int(now)76    sig = _sig(ka_id, ts)77    if not sig:78        return []79    try:80        r = requests.get(81            f"{KA_HUB_URL}/api/sso/favorites",82            params={"client_id": CLIENT_ID, "ka_id": ka_id,83                    "ts": ts, "sig": sig},84            timeout=5)85        if r.status_code != 200:86            return []87        favs = r.json().get("favorites")88        if not isinstance(favs, list):89            return []90    except Exception:91        return []92    with _lock:93        _cache[ka_id] = (now, favs)94    return favs959697def invalidate(ka_id: str) -> None:98    """Invalide le cache de hub_list après un toggle (état frais au prochain GET)."""99    with _lock:100        _cache.pop(ka_id, None)101