""" Telemetry service (4A). Single entry-point for writing canonical product events. """ from __future__ import annotations import hashlib import json from typing import Any, Optional from fastapi import Request from sqlalchemy.ext.asyncio import AsyncSession from app.config import get_settings from app.models.telemetry import TelemetryEvent settings = get_settings() def _hash_ip(ip: str) -> str: return hashlib.sha256(f"{ip}|{settings.secret_key}".encode()).hexdigest()[:32] def _get_client_ip(request: Request) -> Optional[str]: xff = request.headers.get("x-forwarded-for") if xff: ip = xff.split(",")[0].strip() if ip: return ip cf_ip = request.headers.get("cf-connecting-ip") if cf_ip: return cf_ip.strip() return request.client.host if request.client else None async def track_event( db: AsyncSession, *, event_name: str, request: Optional[Request] = None, user_id: Optional[int] = None, is_authenticated: Optional[bool] = None, source: Optional[str] = None, domain: Optional[str] = None, listing_id: Optional[int] = None, inquiry_id: Optional[int] = None, yield_domain_id: Optional[int] = None, click_id: Optional[str] = None, referrer: Optional[str] = None, user_agent: Optional[str] = None, metadata: Optional[dict[str, Any]] = None, ) -> None: ip_hash = None if request is not None: ip = _get_client_ip(request) ip_hash = _hash_ip(ip) if ip else None user_agent = user_agent or request.headers.get("user-agent") referrer = referrer or request.headers.get("referer") row = TelemetryEvent( user_id=user_id, event_name=event_name, listing_id=listing_id, inquiry_id=inquiry_id, yield_domain_id=yield_domain_id, click_id=click_id[:64] if click_id else None, domain=domain, source=source, ip_hash=ip_hash, user_agent=user_agent[:500] if user_agent else None, referrer=referrer[:500] if referrer else None, metadata_json=json.dumps(metadata or {}, ensure_ascii=False) if metadata else None, is_authenticated=is_authenticated, ) db.add(row)