All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m5s
Combines shared, blog, market, cart, events, federation, and account into a single repository. Eliminates submodule sync, sibling model copying at build time, and per-app CI orchestration. Changes: - Remove per-app .git, .gitmodules, .gitea, submodule shared/ dirs - Remove stale sibling model copies from each app - Update all 6 Dockerfiles for monorepo build context (root = .) - Add build directives to docker-compose.yml - Add single .gitea/workflows/ci.yml with change detection - Add .dockerignore for monorepo build context - Create __init__.py for federation and account (cross-app imports)
69 lines
2.1 KiB
Python
69 lines
2.1 KiB
Python
"""WebFinger client for resolving remote AP actor profiles."""
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
|
|
import httpx
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
AP_CONTENT_TYPE = "application/activity+json"
|
|
|
|
|
|
async def resolve_actor(acct: str) -> dict | None:
|
|
"""Resolve user@domain to actor JSON via WebFinger + actor fetch.
|
|
|
|
Args:
|
|
acct: Handle in the form ``user@domain`` (no leading ``@``).
|
|
|
|
Returns:
|
|
Actor JSON-LD dict, or None if resolution fails.
|
|
"""
|
|
acct = acct.lstrip("@")
|
|
if "@" not in acct:
|
|
return None
|
|
|
|
_, domain = acct.rsplit("@", 1)
|
|
webfinger_url = f"https://{domain}/.well-known/webfinger"
|
|
|
|
try:
|
|
async with httpx.AsyncClient(timeout=10, follow_redirects=True) as client:
|
|
# Step 1: WebFinger lookup
|
|
resp = await client.get(
|
|
webfinger_url,
|
|
params={"resource": f"acct:{acct}"},
|
|
headers={"Accept": "application/jrd+json, application/json"},
|
|
)
|
|
if resp.status_code != 200:
|
|
log.debug("WebFinger %s returned %d", webfinger_url, resp.status_code)
|
|
return None
|
|
|
|
data = resp.json()
|
|
# Find self link with AP content type
|
|
actor_url = None
|
|
for link in data.get("links", []):
|
|
if link.get("rel") == "self" and link.get("type") in (
|
|
AP_CONTENT_TYPE,
|
|
"application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"",
|
|
):
|
|
actor_url = link.get("href")
|
|
break
|
|
|
|
if not actor_url:
|
|
log.debug("No AP self link in WebFinger response for %s", acct)
|
|
return None
|
|
|
|
# Step 2: Fetch actor JSON
|
|
resp = await client.get(
|
|
actor_url,
|
|
headers={"Accept": AP_CONTENT_TYPE},
|
|
)
|
|
if resp.status_code == 200:
|
|
return resp.json()
|
|
|
|
log.debug("Actor fetch %s returned %d", actor_url, resp.status_code)
|
|
except Exception:
|
|
log.exception("WebFinger resolution failed for %s", acct)
|
|
|
|
return None
|