Changeset 5c373b8 in Klonkt for src/services/ActivityPubService.js
- Timestamp:
- 07/25/2026 07:51:28 AM (7 weeks ago)
- Branches:
- main
- Children:
- e62f65d
- Parents:
- 28267519
- File:
-
- 1 edited
-
src/services/ActivityPubService.js (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
src/services/ActivityPubService.js
r28267519 r5c373b8 1375 1375 const sharedInbox = (remote.endpoints && remote.endpoints.sharedInbox) || null; 1376 1376 const fi = actorInfo(remote, who); // cache display for the friends list (shaer-aa3) 1377 // FEP-633c §5.3: if the followed actor is a WARD (has guardians), the 1378 // follow is gated. A committed guardian's own Follow is auto-accepted 1379 // (it needs no gate); anyone else is held pending for guardian approval. 1380 // Free actors / normal sites have no guardians → fall through, unchanged. 1381 const wardGuardians = Guardianship.listGuardians(slug).map((g) => g.other_uri); 1382 if (wardGuardians.length && !wardGuardians.includes(who)) { 1383 const followId = (typeof act.id === 'string' && act.id) || `${who}#follow-${Date.now()}-${rid()}`; 1384 Guardianship.follows.recordPending(slug, { 1385 id: followId, follower: who, inbox: remote.inbox, sharedInbox, 1386 name: fi.name, handle: fi.handle, icon: fi.icon, activity: act, 1387 }); 1388 for (const g of wardGuardians) { 1389 const gslug = slugFromActorUrl(g); 1390 if (!gslug) continue; 1391 const L = pushLang(gslug); 1392 pushEvent(gslug, { type: 'guardian', title: i18nT(L, 'push.n_guard_cog_t'), body: i18nT(L, 'push.n_guard_cog_b', { who: fi.name || fi.handle || i18nT(L, 'notif.someone') }), url: `${pushPrefix(gslug)}/guardian2` }); 1393 } 1394 console.log('[AP] Follow', who, '→ ward', slug, '(gated, awaiting guardians)'); 1395 return 202; 1396 } 1377 1397 fStmts().ins.run(slug, who, remote.inbox, sharedInbox, fi.name, fi.handle, fi.icon); 1378 1398 try { _updFDisp.run(fi.name, fi.handle, fi.icon, slug, who); } catch { /* best effort */ } … … 2879 2899 } 2880 2900 2901 // FEP-633c §5.3: the guardians approved a gated follow of their ward. Send the 2902 // Accept to the follower and record them, so delivery (incl. followers-only) 2903 // begins. `pending` is a row from ap_pending_follows. 2904 export async function acceptGatedFollow(pending) { 2905 const base = (process.env.PUBLIC_BASE_URL || '').replace(/\/+$/, ''); 2906 const slug = pending.ward_slug; 2907 const me = actorId(base, slug); 2908 const keys = getOrCreateKeys(slug); 2909 fStmts().ins.run(slug, pending.follower_uri, pending.follower_inbox, pending.follower_shared_inbox, pending.follower_name, pending.follower_handle, pending.follower_icon); 2910 const original = pending.activity_json ? JSON.parse(pending.activity_json) : { type: 'Follow', actor: pending.follower_uri, object: me }; 2911 const accept = { '@context': AP_CONTEXT, id: `${me}#accept-${Date.now()}-${rid()}`, type: 'Accept', actor: me, object: original }; 2912 await deliverWithRetry(slug, pending.follower_inbox, accept, `${me}#main-key`, keys.private_pem); 2913 const filled = pending.follower_shared_inbox && 2914 db.prepare('SELECT 1 FROM ap_followers WHERE slug = ? AND shared_inbox = ? AND actor_uri != ? LIMIT 1').get(slug, pending.follower_shared_inbox, pending.follower_uri); 2915 if (!filled) backfillNewFollower(base, slug, pending.follower_shared_inbox || pending.follower_inbox).catch(() => {}); 2916 console.log('[AP] gated Follow accepted', pending.follower_uri, '→ ward', slug); 2917 return { ok: true }; 2918 } 2919 2920 // The guardians denied the follow: send a Reject so the follower's server clears 2921 // its pending state, then the caller drops the record. 2922 export async function rejectGatedFollow(pending) { 2923 const base = (process.env.PUBLIC_BASE_URL || '').replace(/\/+$/, ''); 2924 const slug = pending.ward_slug; 2925 const me = actorId(base, slug); 2926 const keys = getOrCreateKeys(slug); 2927 const original = pending.activity_json ? JSON.parse(pending.activity_json) : { type: 'Follow', actor: pending.follower_uri, object: me }; 2928 const reject = { '@context': AP_CONTEXT, id: `${me}#reject-${Date.now()}-${rid()}`, type: 'Reject', actor: me, object: original }; 2929 if (pending.follower_inbox) await deliverWithRetry(slug, pending.follower_inbox, reject, `${me}#main-key`, keys.private_pem).catch(() => {}); 2930 console.log('[AP] gated Follow rejected', pending.follower_uri, '→ ward', slug); 2931 return { ok: true }; 2932 } 2933 2881 2934 // Send a Like or Announce (boost) on a remote note FROM this site. 2882 2935 export async function sendInteraction(site, kind, targetNoteId, authorUri) { … … 3193 3246 listOutbox, deliverOutboxDelete, deliverOutboxUpdate, deliverDirectNote, 3194 3247 webfingerResolve, followActor, resolveRemoteActor, unfollowActor, listFollowing, setAutoBoost, backfillFromOutbox, getTimeline, timelineAttachments, sendInteraction, voteOnPoll, voteOnRemotePoll, 3248 acceptGatedFollow, rejectGatedFollow, 3195 3249 parseOwnPoll, pollTally, ownPollView, deliverPollUpdate, maybeCrawlThread, sendReport, localMentionSlugs, 3196 3250 autoBoostCount, boostedCount, markBoosted, unmarkBoosted, markLiked, unmarkLiked, getTimelineReaction, upsertBoostedNote, getCirkelPosts, getCirkelMembers, selfHealTimeline,
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)