Route outbound Follow through EventProcessor for retry

send_follow now emits a Follow activity via emit_activity() instead of
inline HTTP POST. New ap_follow_handler delivers to the remote inbox;
EventProcessor retries on failure. Wildcard delivery handler skips
Follow type to avoid duplicate broadcast.

Also add /social/ index page to per-app social blueprint.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
giles
2026-02-25 09:25:08 +00:00
parent c6271931a6
commit 0ccf897f74
6 changed files with 157 additions and 35 deletions

View File

@@ -686,42 +686,21 @@ class SqlFederationService:
session.add(follow)
await session.flush()
# Send Follow activity
domain = _domain()
actor_url = f"https://{domain}/users/{local_username}"
follow_id = f"{actor_url}/activities/{uuid.uuid4()}"
follow_activity = {
"@context": "https://www.w3.org/ns/activitystreams",
"id": follow_id,
"type": "Follow",
"actor": actor_url,
"object": remote_actor_url,
}
import json
import httpx
from shared.utils.http_signatures import sign_request
from urllib.parse import urlparse
body_bytes = json.dumps(follow_activity).encode()
parsed = urlparse(remote.inbox_url)
headers = sign_request(
private_key_pem=actor.private_key_pem,
key_id=f"{actor_url}#main-key",
method="POST",
path=parsed.path,
host=parsed.netloc,
body=body_bytes,
# Emit Follow activity — EventProcessor delivers with retries
from shared.events.bus import emit_activity
await emit_activity(
session,
activity_type="Follow",
actor_uri=f"https://{_domain()}/users/{local_username}",
object_type="Actor",
object_data={
"target_inbox": remote.inbox_url,
"target_actor_url": remote_actor_url,
"following_id": follow.id,
},
visibility="public",
actor_profile_id=actor.id,
)
headers["Content-Type"] = "application/activity+json"
try:
async with httpx.AsyncClient(timeout=15) as client:
await client.post(remote.inbox_url, content=body_bytes, headers=headers)
except Exception:
import logging
logging.getLogger(__name__).exception("Failed to send Follow to %s", remote.inbox_url)
async def get_following(
self, session: AsyncSession, username: str,