Changeset 1e172f3 in Klonkt for src/services
- Timestamp:
- 08/07/2026 12:00:22 PM (5 weeks ago)
- Branches:
- main
- Children:
- 4c1e327
- Parents:
- ea138c0
- git-author:
- Robin <roboburr@…> (08/07/2026 12:00:20 PM)
- git-committer:
- roboburr <roboburr@…> (08/07/2026 12:00:22 PM)
- Location:
- src/services
- Files:
-
- 3 edited
-
ActivityPubService.js (modified) (1 diff)
-
guardianship/follows.js (modified) (3 diffs)
-
guardianship/queues.js (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
src/services/ActivityPubService.js
rea138c0 r1e172f3 4902 4902 const wardDoc = await fetchActor(wardUri).catch(() => null); 4903 4903 const fai = actorInfo(await fetchActor(follower).catch(() => null), follower); 4904 Guardianship.follows.recordReview(gslug, { id: followId, wardUri, wardInbox: wardDoc && wardDoc.inbox, follower, followerHandle: fai.handle, followerIcon: fai.icon, followJson: JSON.stringify(fo) }); 4904 // De RICHTING bewaren (shaer-jdb). shaer:direction wordt sinds de uitgaande 4905 // gate meegestuurd maar werd nergens gelezen, dus een uitgaande belandde 4906 // hier als "deze ward wil deze ward volgen" met het doel weggegooid. 4907 // Terugval voor oudere afzenders: is de volger de ward zelf, dan is het 4908 // uitgaand -- dat volgt uit de vorm en hoeft niet geloofd te worden. 4909 const uitgaand = act['shaer:direction'] === 'outgoing' || follower === wardUri; 4910 const doel = uitgaand ? (typeof fo.object === 'string' ? fo.object : (fo.object && fo.object.id)) : null; 4911 const dai = uitgaand ? actorInfo(await fetchActor(doel).catch(() => null), doel) : null; 4912 Guardianship.follows.recordReview(gslug, { 4913 id: followId, wardUri, wardInbox: wardDoc && wardDoc.inbox, 4914 follower, followerHandle: fai.handle, followerIcon: fai.icon, followJson: JSON.stringify(fo), 4915 direction: uitgaand ? 'outgoing' : 'incoming', 4916 target: doel || null, targetHandle: dai ? dai.handle : null, 4917 }); 4905 4918 const L = pushLang(gslug); 4906 4919 pushEvent(gslug, { type: 'guardian', title: i18nT(L, 'push.n_guard_cog_t'), body: i18nT(L, 'push.n_guard_cog_b', { who: fai.name || fai.handle || i18nT(L, 'notif.someone') }), url: `${pushPrefix(gslug)}/guardian` }); -
src/services/guardianship/follows.js
rea138c0 r1e172f3 81 81 _r = { 82 82 ins: db.prepare(`INSERT OR IGNORE INTO ap_follow_reviews 83 (id, guardian_slug, ward_uri, ward_inbox, follower_uri, follower_handle, follower_icon, follow_json, created_at) 84 VALUES (?,?,?,?,?,?,?,?, CURRENT_TIMESTAMP)`), 83 (id, guardian_slug, ward_uri, ward_inbox, follower_uri, follower_handle, follower_icon, follow_json, 84 direction, target_uri, target_handle, created_at) 85 VALUES (?,?,?,?,?,?,?,?,?,?,?, CURRENT_TIMESTAMP)`), 85 86 get: db.prepare('SELECT * FROM ap_follow_reviews WHERE guardian_slug = ? AND id = ?'), 86 87 bySlug: db.prepare("SELECT * FROM ap_follow_reviews WHERE guardian_slug = ? AND status = 'pending' ORDER BY created_at DESC"), … … 91 92 } 92 93 94 /** 95 * De guardian-zijdige kopie van een gate-verzoek op een REMOTE ward. 96 * 97 * `direction` is niet cosmetisch (shaer-jdb). Bij een INKOMENDE is de follower 98 * iemand anders en de ward het doel. Bij een UITGAANDE is de ward zelf de 99 * follower en staat het doel in het Follow-object -- die werd hiervoor 100 * opgeslagen als "deze ward wil deze ward volgen", met het doel weggegooid. 101 */ 93 102 export function recordReview(guardianSlug, r) { 94 rstmts().ins.run(r.id, guardianSlug, r.wardUri, r.wardInbox || null, r.follower, r.followerHandle || null, r.followerIcon || null, r.followJson || null); 103 const richting = r.direction === 'outgoing' ? 'outgoing' : 'incoming'; 104 rstmts().ins.run(r.id, guardianSlug, r.wardUri, r.wardInbox || null, r.follower, r.followerHandle || null, 105 r.followerIcon || null, r.followJson || null, richting, r.target || null, r.targetHandle || null); 95 106 return rstmts().get.get(guardianSlug, r.id); 107 } 108 109 /** 110 * Een openstaande review als wachtrij-item, in dezelfde vorm die de clients al 111 * lezen (offers en outgoing-follows doen het net zo). 112 */ 113 export function reviewQueueItem(r, me, guardianCount) { 114 // guardianCount blijft WEG als we hem niet kennen. Bij een remote ward wordt 115 // de guardian-set op diens eigen server bijgehouden, en 0 sturen zou lezen als 116 // "dit kind heeft geen guardians" -- het tegenovergestelde van onbekend. 117 const stemmen = (() => { 118 try { return db.prepare('SELECT guardian_uri, decision FROM ap_pending_follow_approvals WHERE follow_id = ?').all(r.id); } 119 catch { return []; } 120 })(); 121 const uitgaand = r.direction === 'outgoing'; 122 return { 123 id: r.id, 124 type: 'Follow', 125 // Bij een uitgaande is de WARD de volger; bij een inkomende is dat de vreemde. 126 actor: uitgaand ? r.ward_uri : r.follower_uri, 127 object: uitgaand ? (r.target_uri || '') : r.ward_uri, 128 'shaer:direction': uitgaand ? 'outgoing' : 'incoming', 129 'shaer:ward': r.ward_uri, 130 'shaer:target': uitgaand ? (r.target_uri || undefined) : undefined, 131 'shaer:targetHandle': uitgaand ? (r.target_handle || undefined) : undefined, 132 'shaer:follower': uitgaand ? undefined : r.follower_uri, 133 'shaer:followerHandle': uitgaand ? undefined : (r.follower_handle || undefined), 134 'shaer:quorum': 'all', 135 'shaer:approvals': stemmen.filter((x) => x.decision === 'approve').length, 136 'shaer:guardianCount': guardianCount || undefined, 137 'shaer:myVote': stemmen.some((x) => x.guardian_uri === me), 138 published: r.created_at, 139 }; 140 } 141 142 /** De openstaande reviews van een guardian, per richting. */ 143 export function listReviewsByDirection(guardianSlug, direction) { 144 return listReviews(guardianSlug).filter((r) => (r.direction === 'outgoing' ? 'outgoing' : 'incoming') === direction); 96 145 } 97 146 export function getReview(guardianSlug, id) { return rstmts().get.get(guardianSlug, id); } … … 102 151 recordPending, getPending, listForWard, decide, remove, 103 152 recordReview, getReview, listReviews, removeReview, 153 listReviewsByDirection, reviewQueueItem, 104 154 }; -
src/services/guardianship/queues.js
rea138c0 r1e172f3 6 6 * - offers: pending handshake offers where I am a party (§3), with the full 7 7 * accept tally so the client shows the right action 8 * - follows: pending gated follows for my wards (§5.3) — Fase 2, empty for now8 * - follows: pending gated follows ON my wards (§5.3), Fase 2 (shaer-jdb) 9 9 * - wards: my committed wards 10 10 */ … … 13 13 import * as availability from './availability.js'; 14 14 import * as outgoing from './outgoing.js'; 15 import * as follows from './follows.js'; 15 16 import * as handshake from './handshake.js'; 16 17 … … 35 36 } 36 37 37 /** Gated follows awaiting guardian approval — not built in Klonkt yet (Fase 2). */ 38 export function followsCollection(id) { 39 return collection(id, []); 38 /** 39 * Gate-verzoeken OP mijn wards die op mijn antwoord wachten (Guardianship Fase 2, 40 * shaer-jdb). Dit was een lege stub: de gating zelf werkt sinds shaer-hxg, maar 41 * werd nooit aan een C2S-client doorgegeven omdat de koers toen op de PWA lag. 42 * 43 * Twee bronnen, want een guardian kan wards op andere servers hebben en (nog) 44 * op deze: 45 * - ap_follow_reviews: de doorgestuurde kopie van een REMOTE ward 46 * - ap_pending_follows: een ward op deze instance 47 * Zie shaer-h6u: die tweede hoort op termijn ook over de lijn te gaan. 48 */ 49 export function followsCollection(id, slug, me) { 50 const items = follows.listReviewsByDirection(slug, 'incoming') 51 .map((r) => follows.reviewQueueItem(r, me)); 52 for (const w of relations.listWards(slug)) { 53 const wardSlug = slugOf(w.other_uri); 54 if (!wardSlug) continue; 55 for (const p of follows.listForWard(wardSlug)) { 56 items.push({ 57 id: p.id, type: 'Follow', actor: p.follower_uri, object: w.other_uri, 58 'shaer:direction': 'incoming', 'shaer:ward': w.other_uri, 59 'shaer:follower': p.follower_uri, 'shaer:followerHandle': p.follower_handle || undefined, 60 'shaer:quorum': p.quorum || 'any', published: p.created_at, 61 }); 62 } 63 } 64 return collection(id, items); 40 65 } 41 66 42 /** §5.3 outbound: this ward's own follow requests, waiting for its guardians. */ 67 /** De slug van een actor-uri op DEZE instance, of null als hij elders woont. */ 68 function slugOf(uri) { 69 const base = (process.env.PUBLIC_BASE_URL || '').replace(/\/+$/, ''); 70 if (!base || !String(uri || '').startsWith(`${base}/ap/users/`)) return null; 71 return decodeURIComponent(String(uri).slice(`${base}/ap/users/`.length).split(/[/?#]/)[0]) || null; 72 } 73 74 /** 75 * §5.3 uitgaand. Twee lezers, een wachtrij, en dat kan omdat §1 een ward en een 76 * guardian wederzijds uitsluit: je bent het een of het ander. 77 * 78 * ALS WARD wat IK wil volgen en waar mijn guardians nog over moeten 79 * ALS GUARDIAN wat mijn WARDS willen volgen en waar IK over moet (shaer-jdb) 80 * 81 * Dat tweede ontbrak. De wachtrij serveerde alleen listForWard(slug), en voor 82 * een guardian is dat per definitie leeg -- dus het scherm "Your wards want to 83 * follow" kon nooit iets tonen. 84 */ 43 85 export function outgoingFollowsCollection(id, slug, me) { 44 return collection(id, outgoing.listForWard(slug).map((o) => outgoing.queueItem(o, me))); 86 const items = outgoing.listForWard(slug).map((o) => outgoing.queueItem(o, me)); 87 for (const r of follows.listReviewsByDirection(slug, 'outgoing')) { 88 items.push(follows.reviewQueueItem(r, me)); 89 } 90 return collection(id, items); 45 91 } 46 92
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)