101 lines
3.1 KiB
Python
101 lines
3.1 KiB
Python
import time
|
|
|
|
import requests
|
|
from django.conf import settings
|
|
from django.core.cache import cache
|
|
|
|
from utils.clients.gooyal_accounts_client import AuthenticatedClient
|
|
from utils.clients.gooyal_accounts_client.models import PublicUser
|
|
from utils.clients.gooyal_accounts_client.api.users import users_api_users_retrieve
|
|
|
|
ACCESS_TOKEN_CACHE_KEY = "chat_account_access_token"
|
|
USER_PUBLIC_INFO_CACHE_KEY = "chat_account_public_info_{uuid}"
|
|
USER_PUBLIC_INFO_CACHE_TIMEOUT_SECONDS = 300
|
|
|
|
|
|
def login_as_client_credentials() -> dict:
|
|
"""
|
|
Client-credentials OAuth2 grant against the gooyal accounts service,
|
|
reusing a cached access token until it's within 5s of expiry.
|
|
"""
|
|
cached = cache.get(ACCESS_TOKEN_CACHE_KEY)
|
|
if cached:
|
|
return cached
|
|
|
|
data = {
|
|
"grant_type": "client_credentials",
|
|
"scope": settings.OAUTH2_PROVIDER_SCOPES,
|
|
}
|
|
auth = (settings.OAUTH2_PROVIDER_CLIENT_ID, settings.OAUTH2_PROVIDER_CLIENT_SECRET)
|
|
|
|
response = requests.post(
|
|
f"{settings.OAUTH2_PROVIDER_BASE_PUBLIC_URL}/token/", data=data, auth=auth
|
|
)
|
|
response.raise_for_status()
|
|
|
|
auth_data = response.json()
|
|
if "access_token" in auth_data:
|
|
auth_data["created_at"] = time.time()
|
|
cache.set(
|
|
ACCESS_TOKEN_CACHE_KEY, auth_data, timeout=auth_data.get("expires_in", 300) - 5
|
|
)
|
|
|
|
return auth_data
|
|
|
|
|
|
def get_client() -> AuthenticatedClient:
|
|
access_token = login_as_client_credentials()["access_token"]
|
|
return AuthenticatedClient(
|
|
base_url=settings.ACCOUNTS_BASE_PUBLIC_URL, token=access_token
|
|
)
|
|
|
|
|
|
def get_user_public_info(uuid) -> PublicUser | None:
|
|
"""Fetch a single user's public profile (name, avatar) — not cached."""
|
|
client = get_client()
|
|
return users_api_users_retrieve.sync(uuid=uuid, client=client)
|
|
|
|
|
|
def get_users_public_info_bulk(uuids: list) -> dict[str, dict]:
|
|
"""
|
|
Fetch public profiles (name, avatar) for multiple users in one call.
|
|
Checks the cache per UUID first; only uncached UUIDs hit the network.
|
|
Returns {str(uuid): profile_dict} for every user found.
|
|
"""
|
|
if not uuids:
|
|
return {}
|
|
|
|
result = {}
|
|
uncached = []
|
|
|
|
for uid in uuids:
|
|
cache_key = USER_PUBLIC_INFO_CACHE_KEY.format(uuid=uid)
|
|
cached = cache.get(cache_key)
|
|
if cached is not None:
|
|
result[str(uid)] = cached
|
|
else:
|
|
uncached.append(uid)
|
|
|
|
if not uncached:
|
|
return result
|
|
|
|
try:
|
|
client = get_client()
|
|
uuid_in = ",".join(str(u) for u in uncached)
|
|
response = client.get_httpx_client().request(
|
|
method="get",
|
|
url=f"/users/api/users/?uuid__in={uuid_in}",
|
|
)
|
|
if response.status_code == 200:
|
|
for profile in response.json().get("results", []):
|
|
uid_str = profile.get("uuid")
|
|
if uid_str:
|
|
cache_key = USER_PUBLIC_INFO_CACHE_KEY.format(uuid=uid_str)
|
|
cache.set(
|
|
cache_key, profile, timeout=USER_PUBLIC_INFO_CACHE_TIMEOUT_SECONDS
|
|
)
|
|
result[uid_str] = profile
|
|
except Exception:
|
|
pass
|
|
|
|
return result
|