Changeset 55bc7f9 in Klonkt for src/routes/posts.js


Ignore:
Timestamp:
06/24/2026 12:09:08 PM (3 months ago)
Author:
Robin Genis <roboburr@…>
Branches:
main
Children:
3a2c9d3
Parents:
47a0d29
Message:

feat(activitypub): reply back to the fediverse (Phase 4 outbound)

Site owner can reply to an inbound fediverse interaction from the post page. The
reply is sent as a signed Create(Note) with inReplyTo + @Mention to the remote
actor's inbox + our followers, stored in ap_outbox, shown in the thread, and
resolvable at /ap/notes/<id>.

Co-Authored-By: Claude <noreply@…>

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/routes/posts.js

    r47a0d29 r55bc7f9  
    77import ejs from 'ejs';
    88import db from '../config/database.js';
    9 import { requireAuth } from '../middleware/auth.js';
     9import { requireAuth, requireSiteManager } from '../middleware/auth.js';
    1010import { renderPage } from '../middleware/render.js';
    1111import { recordPageview, recordPostView } from '../services/StatsService.js';
     
    749749
    750750  // Inbound fediverse activity (replies/likes/boosts) for this post.
    751   let fediverse = { replies: [], likeCount: 0, announceCount: 0, total: 0 };
     751  let fediverse = { replies: [], outReplies: [], likeCount: 0, announceCount: 0, total: 0 };
    752752  try { fediverse = ActivityPubService.getInteractions(post.id); } catch { /* non-fatal */ }
     753  // Owner/admin of this site may reply back to a fediverse interaction.
     754  const canManageSite = !!(req.session?.user && PermissionsService.canAdminSite(req.session.user, site));
    753755
    754756  renderPage(req, res, 'pages/post', {
     
    760762    totalComments,
    761763    fediverse,
     764    canManageSite,
    762765    likeCount,
    763766    likedByMe,
     
    769772});
    770773
     774// ── Reply back to a fediverse interaction (site owner/admin only) ──
     775router.post('/posts/:slug/fedi-reply', requireSiteManager, async (req, res) => {
     776  const site = res.locals.site;
     777  if (!site) return res.status(404).send('Site required');
     778  const post = db.prepare('SELECT id, slug FROM posts WHERE site_id = ? AND slug = ?').get(site.id, req.params.slug);
     779  if (!post) return res.status(404).send('Not found');
     780  const parent = ActivityPubService.getInteractionById(req.body.interaction_id);
     781  const text = (req.body.text || '').toString();
     782  if (parent && parent.post_id === post.id && text.trim()) {
     783    try {
     784      await ActivityPubService.deliverReply(site, { postId: post.id, postSlug: post.slug, parent, text });
     785    } catch (e) { console.warn('[AP] reply send failed:', e.message); }
     786  }
     787  res.redirect(`${res.locals.siteUrlBase || ''}/${post.slug}#fediverse`);
     788});
     789
    771790export default router;
    772791export { postNeighbors };
Note: See TracChangeset for help on using the changeset viewer.