SPB Git forge

spb/auto-ka

Public
61commits 1branches 0releases
14.4 MBsize
maindefault branch
12 days agolast push
Python 61.6% TypeScript 20.9% CSS 11.4% JavaScript 5.1% HTML 1.1%
3.3 KB · 92 lines python
Raw Blame History
1# -----------------------------------------------------------------------------2# Auto-Ka — Agrégateur de voitures usagées à vendre (province de Québec)3# Auteur : Simon-Pierre Boucher — contact@spboucher.ai4# hubfav.py : favoris unifiés « Mon univers Ka » — le hub Groupe KA5#   (groupe-ka.com) est le MAGASIN CENTRAL des favoris du groupe : Auto·Ka ne6#   stocke RIEN localement, chaque ♥ est poussé (POST signé, synchrone) et7#   relu (GET signé) au hub. Signature HMAC-SHA256 du secret SSO partagé :8#   sig = HMAC(KA_SSO_SECRET, "auto-ka.<ka_id>.<ts>") en hex, ts ±5 min côté9#   hub. Lecture avec cache mémoire 30 s (invalidé à chaque toggle) ; liste10#   vide sur toute erreur — le hub reste la seule source de vérité.11#   Config .env : KA_SSO_SECRET, KA_HUB_URL (optionnel).12# -----------------------------------------------------------------------------13from __future__ import annotations1415import hashlib16import hmac17import os18import threading19import time2021import requests2223CLIENT_ID = "auto-ka"24KA_HUB_URL = os.environ.get("KA_HUB_URL", "https://www.groupe-ka.com").rstrip("/")25CACHE_TTL = 30      # secondes — lecture des favoris26TIMEOUT = 6         # secondes — chaque appel au hub2728_cache: dict[str, tuple[float, list[dict]]] = {}29_lock = threading.Lock()303132def _sig(ka_id: str, ts: int) -> str | None:33    secret = os.environ.get("KA_SSO_SECRET")34    if not secret:35        return None36    return hmac.new(secret.encode(),37                    f"{CLIENT_ID}.{ka_id}.{ts}".encode(),38                    hashlib.sha256).hexdigest()394041def invalidate(ka_id: str) -> None:42    """Oublie le cache de lecture de ce membre (après un toggle)."""43    with _lock:44        _cache.pop(ka_id, None)454647def hub_toggle(ka_id: str, action: str, item: dict) -> bool:48    """Pousse un ♥ au hub (action « add » ou « remove »), en SYNCHRONE :49    True si le hub confirme, False sinon (réseau, signature, membre inconnu).50    Le cache de lecture du membre est invalidé dans tous les cas."""51    ts = int(time.time())52    sig = _sig(ka_id, ts)53    if not sig or not ka_id:54        return False55    ok = False56    try:57        r = requests.post(f"{KA_HUB_URL}/api/sso/favorites", timeout=TIMEOUT,58                          json={"client_id": CLIENT_ID, "ka_id": ka_id,59                                "ts": str(ts), "sig": sig,60                                "action": action, "item": item})61        ok = r.status_code == 20062    except Exception:63        ok = False64    invalidate(ka_id)65    return ok666768def hub_list(ka_id: str) -> list[dict]:69    """Favoris Auto·Ka du membre, lus au hub (cache 30 s, [] sur erreur)."""70    ts_now = time.time()71    with _lock:72        hit = _cache.get(ka_id)73        if hit and ts_now - hit[0] < CACHE_TTL:74            return hit[1]75    sig = _sig(ka_id, int(ts_now))76    if not sig or not ka_id:77        return []78    try:79        r = requests.get(f"{KA_HUB_URL}/api/sso/favorites", timeout=TIMEOUT,80                         params={"client_id": CLIENT_ID, "ka_id": ka_id,81                                 "ts": int(ts_now), "sig": sig})82        if r.status_code != 200:83            return []84        favs = r.json().get("favorites")85        if not isinstance(favs, list):86            return []87    except Exception:88        return []89    with _lock:90        _cache[ka_id] = (ts_now, favs)91    return favs92