import os from typing import Any import requests from .errors import EntitlementServiceError from .models import EntitlementResult class EntitlementClient: def __init__( self, base_url: str | None = None, api_key: str | None = None, timeout_seconds: int | None = None, ) -> None: self.base_url = (base_url or os.getenv("JIANGCHANG_AUTH_BASE_URL", "")).rstrip("/") self.api_key = api_key or os.getenv("JIANGCHANG_AUTH_API_KEY", "") self.timeout_seconds = timeout_seconds or int( os.getenv("JIANGCHANG_AUTH_TIMEOUT_SECONDS", "5") ) if not self.base_url: raise EntitlementServiceError("missing JIANGCHANG_AUTH_BASE_URL") def check_entitlement( self, user_id: str, skill_slug: str, trace_id: str = "", context: dict[str, Any] | None = None, ) -> EntitlementResult: url = f"{self.base_url}/api/entitlements/check" payload = { "user_id": user_id, "skill_slug": skill_slug, "trace_id": trace_id, "context": context or {}, } headers = {"Content-Type": "application/json"} if self.api_key: headers["Authorization"] = f"Bearer {self.api_key}" try: res = requests.post( url, json=payload, headers=headers, timeout=self.timeout_seconds, ) except requests.RequestException as exc: raise EntitlementServiceError(f"entitlement request failed: {exc}") from exc if res.status_code != 200: raise EntitlementServiceError(f"entitlement http status {res.status_code}") try: body = res.json() except ValueError as exc: raise EntitlementServiceError("entitlement response is not json") from exc data = body.get("data") or {} allow = bool(data.get("allow", False)) reason = str(data.get("reason") or body.get("msg") or "") expire_at = str(data.get("expire_at") or "") return EntitlementResult(allow=allow, reason=reason, expire_at=expire_at, raw=body)