"""Thin wrapper around httpx that logs every outbound API call to api_logs.""" import json import time import urllib.parse from typing import Any import httpx from web.database import SessionLocal from web.models.connections import ApiLog _MAX_BODY = 8000 # truncate stored bodies beyond this def _service_from_url(url: str) -> str: host = urllib.parse.urlparse(url).netloc if "evotor" in host: return "evotor" if "vk.com" in host: return "vk" return "other" def _truncate(text: str | None) -> str | None: if text and len(text) > _MAX_BODY: return text[:_MAX_BODY] + "…" return text def _record( user_id: int | None, method: str, url: str, request_body: str | None, response_status: int | None, response_body: str | None, duration_ms: int, ) -> None: try: db = SessionLocal() db.add(ApiLog( user_id=user_id, service=_service_from_url(url), method=method.upper(), url=url, request_body=_truncate(request_body), response_status=response_status, response_body=_truncate(response_body), duration_ms=duration_ms, )) db.commit() db.close() except Exception: pass # never let logging crash the caller def get(url: str, *, user_id: int | None = None, **kwargs) -> httpx.Response: t0 = time.monotonic() resp = httpx.get(url, **kwargs) ms = int((time.monotonic() - t0) * 1000) try: body = resp.text except Exception: body = None _record(user_id, "GET", url, None, resp.status_code, body, ms) return resp def post(url: str, *, user_id: int | None = None, data: Any = None, json: Any = None, **kwargs) -> httpx.Response: t0 = time.monotonic() resp = httpx.post(url, data=data, json=json, **kwargs) ms = int((time.monotonic() - t0) * 1000) try: req_body = resp.request.content.decode("utf-8", errors="replace") if resp.request.content else None except Exception: req_body = None try: body = resp.text except Exception: body = None _record(user_id, "POST", url, req_body, resp.status_code, body, ms) return resp