Python 61.6%
TypeScript 20.9%
CSS 11.4%
JavaScript 5.1%
HTML 1.1%
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