SPB Git

spb/toit-ka Public

Toit-Ka — louer ou acheter un toit au Québec, un seul endroit (fusion Lou-Ka × Immo-Ka) — www.toit-ka.com

Python 40.2% TypeScript 39% CSS 20.2% HTML 0.7%
2.7 KB · 89 lines python
Raw Blame History
1# Author: Simon-Pierre Boucher2# Contact: contact@spboucher.ai3# Project: Toit-Ka4# -----------------------------------------------------------------------------5# hubfav.py : favoris unifiés « Mon univers Ka » — Toit·Ka n'a AUCUN stockage6#   local de favoris : le hub Groupe KA (groupe-ka.com) est le magasin central.7#   Chaque ♥ est poussé au hub (POST signé, SYNCHRONE) et la liste est relue du8#   hub (GET signé, cache mémoire 30 s par ka_id, [] sur toute erreur).9#   sig = HMAC-SHA256(KA_SSO_SECRET, "toit-ka.<ka_id>.<ts>") hex (ts ±5 min).10# -----------------------------------------------------------------------------11from __future__ import annotations1213import hashlib14import hmac15import os16import threading17import time1819import requests2021CLIENT_ID = "toit-ka"22KA_HUB_URL = os.environ.get("KA_HUB_URL", "https://www.groupe-ka.com").rstrip("/")23LIST_TTL = 302425_cache: dict[str, tuple[float, list]] = {}26_lock = threading.Lock()272829def _sig(ka_id: str, ts: int) -> str | None:30    secret = os.environ.get("KA_SSO_SECRET")31    if not secret:32        return None33    return hmac.new(secret.encode(), f"{CLIENT_ID}.{ka_id}.{ts}".encode(),34                    hashlib.sha256).hexdigest()353637def hub_toggle(ka_id: str, action: str, item: dict) -> bool:38    """Pousse un ♥ au hub (« add » ou « remove ») — synchrone (action utilisateur)."""39    if not ka_id or not str(ka_id).startswith("ka-"):40        return False41    ts = int(time.time())42    sig = _sig(ka_id, ts)43    if not sig:44        return False45    try:46        r = requests.post(f"{KA_HUB_URL}/api/sso/favorites", timeout=6, json={47            "client_id": CLIENT_ID, "ka_id": ka_id,48            "ts": str(ts), "sig": sig, "action": action, "item": item,49        })50        return r.status_code == 20051    except Exception:52        return False535455def hub_list(ka_id: str) -> list:56    """Favoris toit-ka du membre, lus du hub (cache 30 s, [] sur erreur)."""57    if not ka_id:58        return []59    now = time.time()60    with _lock:61        hit = _cache.get(ka_id)62        if hit and now - hit[0] < LIST_TTL:63            return hit[1]64    ts = int(now)65    sig = _sig(ka_id, ts)66    if not sig:67        return []68    try:69        r = requests.get(70            f"{KA_HUB_URL}/api/sso/favorites",71            params={"client_id": CLIENT_ID, "ka_id": ka_id, "ts": ts, "sig": sig},72            timeout=5)73        if r.status_code != 200:74            return []75        favs = r.json().get("favorites")76        if not isinstance(favs, list):77            return []78    except Exception:79        return []80    with _lock:81        _cache[ka_id] = (now, favs)82    return favs838485def invalidate(ka_id: str) -> None:86    """Invalide le cache après un toggle (état frais au prochain GET)."""87    with _lock:88        _cache.pop(ka_id, None)89