Inline federation publication in ghost_sync
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m2s

Replace emit_event("post.published/updated/unpublished") with direct
try_publish() calls. AP activities are now created at write time,
fixing the race condition where multiple EventProcessors competed
for federation events.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
giles
2026-02-22 07:55:48 +00:00
parent c3c878f781
commit fe3bc9d893
2 changed files with 43 additions and 38 deletions

View File

@@ -1023,37 +1023,39 @@ async def sync_single_post(sess: AsyncSession, ghost_id: str) -> None:
post, old_status = await _upsert_post(sess, gp, author_map, tag_map)
# Emit federation events for posts (not pages)
# Publish to federation inline (posts, not pages)
if not post.is_page and post.user_id:
from shared.events import emit_event
from shared.services.federation_publish import try_publish
from shared.infrastructure.urls import app_url
post_url = app_url("coop", f"/{post.slug}/")
if post.status == "published":
event_type = "post.published" if old_status != "published" else "post.updated"
await emit_event(
activity_type = "Create" if old_status != "published" else "Update"
await try_publish(
sess,
event_type=event_type,
aggregate_type="Post",
aggregate_id=post.id,
payload={
"user_id": post.user_id,
"title": post.title or "",
"excerpt": post.custom_excerpt or post.excerpt or "",
user_id=post.user_id,
activity_type=activity_type,
object_type="Article",
object_data={
"name": post.title or "",
"content": post.custom_excerpt or post.excerpt or "",
"url": post_url,
},
source_type="Post",
source_id=post.id,
)
elif old_status == "published" and post.status != "published":
# Unpublished — notify federation to send Delete
await emit_event(
await try_publish(
sess,
event_type="post.unpublished",
aggregate_type="Post",
aggregate_id=post.id,
payload={
"user_id": post.user_id,
"url": post_url,
user_id=post.user_id,
activity_type="Delete",
object_type="Tombstone",
object_data={
"id": post_url,
"formerType": "Article",
},
source_type="Post",
source_id=post.id,
)
@@ -1089,36 +1091,39 @@ async def sync_single_page(sess: AsyncSession, ghost_id: str) -> None:
post, old_status = await _upsert_post(sess, gp, author_map, tag_map)
# Emit federation events for pages
# Publish to federation inline (pages)
if post.user_id:
from shared.events import emit_event
from shared.services.federation_publish import try_publish
from shared.infrastructure.urls import app_url
post_url = app_url("coop", f"/{post.slug}/")
if post.status == "published":
event_type = "post.published" if old_status != "published" else "post.updated"
await emit_event(
activity_type = "Create" if old_status != "published" else "Update"
await try_publish(
sess,
event_type=event_type,
aggregate_type="Post",
aggregate_id=post.id,
payload={
"user_id": post.user_id,
"title": post.title or "",
"excerpt": post.custom_excerpt or post.excerpt or "",
user_id=post.user_id,
activity_type=activity_type,
object_type="Article",
object_data={
"name": post.title or "",
"content": post.custom_excerpt or post.excerpt or "",
"url": post_url,
},
source_type="Post",
source_id=post.id,
)
elif old_status == "published" and post.status != "published":
await emit_event(
await try_publish(
sess,
event_type="post.unpublished",
aggregate_type="Post",
aggregate_id=post.id,
payload={
"user_id": post.user_id,
"url": post_url,
user_id=post.user_id,
activity_type="Delete",
object_type="Tombstone",
object_data={
"id": post_url,
"formerType": "Article",
},
source_type="Post",
source_id=post.id,
)

2
shared

Submodule shared updated: 798fe56165...3bde451ce9