spb/ora-ka Public
Ora-Ka — cinq agrégateurs Ka, une barre de recherche hybride (exact + sémantique)
Python 80%
TypeScript 12.9%
CSS 6.8%
1# -----------------------------------------------------------------------------2# Ora-Ka — Plateforme unifiée des agrégateurs Ka3# Auteur : Simon-Pierre Boucher — contact@spboucher.ai4# hubfav.py : « Mon univers Ka » — lecture des favoris unifiés du membre depuis5# le magasin central du hub Groupe KA (toutes plateformes : Lou·Ka, Immo·Ka,6# Auto·Ka, Fabri·Ka, Food·Ka…). GET signé HMAC du secret SSO partagé,7# scope=all. Cache mémoire 30 s par ka_id ; toute erreur → liste vide8# (l'affichage est un bonus, jamais bloquant).9# Config .env : KA_SSO_SECRET, KA_HUB_URL (optionnel).10# -----------------------------------------------------------------------------11from __future__ import annotations1213import hashlib14import hmac15import os16import threading17import time1819import requests2021CLIENT_ID = "ora-ka"22KA_HUB_URL = os.environ.get("KA_HUB_URL", "https://www.groupe-ka.com").rstrip("/")23CACHE_TTL = 30.0 # secondes — le hub reste la source de vérité2425_cache: dict[str, tuple[float, list]] = {}26_cache_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(),34 f"{CLIENT_ID}.{ka_id}.{ts}".encode(),35 hashlib.sha256).hexdigest()363738def hub_list_all(ka_id: str | None) -> list:39 """Tous les favoris du membre (toutes plateformes du groupe), ou []."""40 if not ka_id or not str(ka_id).startswith("ka-"):41 return [] # compte legacy non relié au hub42 ka_id = str(ka_id)43 now = time.time()44 with _cache_lock:45 hit = _cache.get(ka_id)46 if hit and now - hit[0] < CACHE_TTL:47 return hit[1]48 ts = int(now)49 sig = _sig(ka_id, ts)50 if not sig:51 return []52 try:53 r = requests.get(f"{KA_HUB_URL}/api/sso/favorites", timeout=5, params={54 "client_id": CLIENT_ID, "ka_id": ka_id,55 "ts": str(ts), "sig": sig, "scope": "all",56 })57 if not r.ok:58 return []59 favs = r.json().get("favorites") or []60 if not isinstance(favs, list):61 return []62 except Exception:63 return [] # best-effort : la landing s'affiche simplement sans la section64 with _cache_lock:65 _cache[ka_id] = (now, favs)66 return favs67