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.0 KB · 80 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# hubprofile.py : profil membre lu depuis le HUB Groupe KA (groupe-ka.com)5#   Le hub est LA source de vérité du profil (bio, ville, emploi, entreprise,6#   site web, réseaux sociaux, photo, statut, profil public) — l'édition se7#   fait sur groupe-ka.com/compte, Immo·Ka ne fait qu'afficher.8#   GET {hub}/api/sso/profile?client_id=immo-ka&ka_id=…&ts=…&sig=…9#   avec sig = HMAC-SHA256(KA_SSO_SECRET, "immo-ka.<ka_id>.<ts>") en hex10#   (même secret que le SSO). Cache mémoire 60 s ; None sur toute erreur11#   (réseau, 401, 404 : vieux compte non relié) -> l'appelant retombe sur12#   les données locales.13# -----------------------------------------------------------------------------14from __future__ import annotations1516import hashlib17import hmac18import os19import threading20import time21from datetime import datetime, timezone2223import requests2425KA_HUB_URL = os.environ.get("KA_HUB_URL", "https://www.groupe-ka.com").rstrip("/")26CLIENT_ID = "house-ka"27CACHE_TTL = 60      # secondes28TIMEOUT = 5         # secondes2930_cache: dict[str, tuple[float, dict | None]] = {}31_lock = threading.Lock()323334def fetch_hub_profile(ka_id: str) -> dict | None:35    """Profil du membre au hub Groupe KA, ou None (inconnu ou injoignable)."""36    secret = os.environ.get("KA_SSO_SECRET")37    if not secret or not ka_id:38        return None39    now = time.time()40    with _lock:41        hit = _cache.get(ka_id)42        if hit and now - hit[0] < CACHE_TTL:43            return hit[1]44    data: dict | None = None45    try:46        ts = int(now)47        sig = hmac.new(secret.encode(), f"{CLIENT_ID}.{ka_id}.{ts}".encode(),48                       hashlib.sha256).hexdigest()49        r = requests.get(50            f"{KA_HUB_URL}/api/sso/profile",51            params={"client_id": CLIENT_ID, "ka_id": ka_id,52                    "ts": ts, "sig": sig},53            timeout=TIMEOUT)54        if r.status_code == 200:55            data = r.json()56            if not isinstance(data, dict):57                return None58        elif r.status_code != 404:59            return None     # erreur transitoire (401, 5xx…) : pas de cache60    except Exception:61        return None         # réseau/JSON : pas de cache62    with _lock:63        _cache[ka_id] = (now, data)     # 200 -> data ; 404 -> None (négatif)64    return data656667def to_epoch(v) -> float | None:68    """created_at du hub (epoch OU chaîne ISO) -> epoch secondes, sinon None."""69    if isinstance(v, (int, float)):70        return float(v)71    if isinstance(v, str) and v:72        try:73            dt = datetime.fromisoformat(v.replace("Z", "+00:00"))74            if dt.tzinfo is None:       # chaîne naïve du hub = UTC75                dt = dt.replace(tzinfo=timezone.utc)76            return dt.timestamp()77        except ValueError:78            return None79    return None80