Changeset 2bfe26c in Klonkt for src/services/ActivityPubService.js
- Timestamp:
- 08/08/2026 07:00:17 AM (5 weeks ago)
- Branches:
- main
- Children:
- 0b0cd54
- Parents:
- f3a2556
- git-author:
- Robin <roboburr@…> (08/08/2026 07:00:15 AM)
- git-committer:
- roboburr <roboburr@…> (08/08/2026 07:00:17 AM)
- File:
-
- 1 edited
-
src/services/ActivityPubService.js (modified) (6 diffs)
Legend:
- Unmodified
- Added
- Removed
-
src/services/ActivityPubService.js
rf3a2556 r2bfe26c 2632 2632 Guardianship.availability.declareAway(slug, actorUri, until); 2633 2633 console.log('[AP] guardian declared away (3.6.1):', actorUri, '→', slug, 'until', new Date(until).toISOString()); 2634 } 2635 } 2636 // Een kind dat zelf om een poort vraagt (shaer-8ru). Zelfde weg als de 2637 // afwezigheidsmelding: een gewone directe note met een shaer:-markering, 2638 // per genoemde ontvanger afgehandeld. 2639 // 2640 // ALLEEN VAN EEN EIGEN WARD. Een verzoek van een vreemde is geen vraag 2641 // maar een onbekende die iets over jouw instellingen wil zeggen -- dat 2642 // hoort in geen enkele lijst te belanden waar een guardian op afgaat. 2643 { 2644 const req = Guardianship.gatereq.parseRequest(o); 2645 if (req) { 2646 for (const slug of slugs) { 2647 const mijn = (() => { try { return Guardianship.listWards(slug).some((w) => w.other_uri === actorUri); } catch { return false; } })(); 2648 if (!mijn) { console.warn('[AP] gate request from someone who is not our ward, ignored:', actorUri, '→', slug); continue; } 2649 Guardianship.gatereq.record(slug, actorUri, req.feature, o.id); 2650 console.log('[AP] gate request', req.feature, actorUri, '→', slug); 2651 } 2634 2652 } 2635 2653 } … … 3237 3255 try { 3238 3256 switch (type) { 3257 // Een gate-voorstel uit de app (5.6, shaer-8ru). De PWA deed dit al over 3258 // een eigen route; hier komt het binnen als wat het in AP IS -- een Offer 3259 // van een shaer:GatedSetting. Dezelfde functie erachter, want twee wegen 3260 // naar hetzelfde besluit is precies wat we vandaag hebben rechtgezet. 3261 case 'Offer': { 3262 const gs = Guardianship.gated.parseGatedSetting(object); 3263 if (!gs) return { status: 400, error: 'unsupported_offer' }; 3264 const uit = proposeGate(site, gs.ward, gs.feature, gs.value); 3265 if (uit.status !== 200) return uit; 3266 return { ...uit, status: 201, id: uit.offerId }; 3267 } 3239 3268 case 'Create': { 3240 3269 if (!object || typeof object !== 'object') return { status: 400, error: 'missing_object' }; … … 3248 3277 const isWard = (() => { try { return Guardianship.listGuardians(site.slug).length > 0; } catch { return false; } })(); 3249 3278 const isHelp = object['shaer:helpRequest'] === true || object.helpRequest === true; 3279 // Een poortverzoek van het kind zelf (shaer-8ru) gaat langs de 3280 // messages-poort. Dat lijkt een gat en is het niet: het verzoek draagt 3281 // ALLEEN de naam van de feature, geen vrije tekst, dus er ontstaat geen 3282 // kanaal om omheen die poort te praten. Zonder deze uitzondering kan 3283 // een kind met berichten dicht nergens meer om vragen -- en dan is de 3284 // hele weg dood op precies het moment dat hij nodig is. 3285 const isGateReq = !!Guardianship.gatereq.parseRequest(object); 3250 3286 const direct = c2sVisibility(object) === 'direct'; 3251 if (!isHelp ) {3287 if (!isHelp && !isGateReq) { 3252 3288 if (direct && !Guardianship.wardGateAllowed(site.gate_messages, isWard)) { 3253 3289 return { status: 403, error: 'gated_messages' }; … … 3307 3343 // absence like it does for a ward anywhere else. One path. 3308 3344 } 3309 const r = await deliverDirectNote(site, { recipients, text: plain, language: object.language || null, inReplyTo: typeof object.inReplyTo === 'string' ? object.inReplyTo : null, attachments: atts, helpRequest: help, awayUntil }); 3345 const gateReq = Guardianship.gatereq.parseRequest(object); 3346 const r = await deliverDirectNote(site, { recipients, text: plain, language: object.language || null, inReplyTo: typeof object.inReplyTo === 'string' ? object.inReplyTo : null, attachments: atts, helpRequest: help, awayUntil, gateRequest: gateReq && gateReq.feature }); 3310 3347 if (!r || !r.id) return { status: 502, error: 'direct_failed' }; 3311 3348 return { status: 201, id: r.id, url: `${base}/ap/notes/${r.id}` }; … … 3538 3575 // Send a reply FROM this site to a remote actor (in reply to their inbound reply). 3539 3576 // `parent` = an ap_interactions row (actor_uri, actor_url, actor_handle, object_uri). 3577 /** 3578 * Een gate-voorstel de deur uit (FEP-633c 5.6, shaer-8ru). 3579 * 3580 * STOND IN routes/guardian.js en kon daar alleen door de PWA aangeroepen worden. 3581 * De apps moeten hetzelfde kunnen, en een tweede implementatie ernaast zou een 3582 * tweede weg naar hetzelfde besluit zijn -- precies de fout die we vandaag bij 3583 * de antwoordpoort hebben rechtgezet, toen de innamepoort alleen in C2S bleek te 3584 * zitten en het webpad eromheen liep. Een pad dus. 3585 * 3586 * EEN WEG, waar de ward ook woont (Robins regel, 29-7): voorstellen over de 3587 * lijn en de server van de ward laat tellen. Co-locatie verandert alleen het 3588 * transport -- deliverToActor lust een lokale ontvanger terug door dezelfde 3589 * inbox. De oude kortsluiting boekte de stem hier meteen, en zo bleef het 3590 * remote-pad een maand stuk zonder dat iemand het merkte. 3591 */ 3592 export function proposeGate(site, wardUri, feature, allow) { 3593 const uri = String(wardUri || '').trim(); 3594 if (!uri) return { status: 400, error: 'empty_uri' }; 3595 if (!Guardianship.gated.featureColumn(feature)) return { status: 400, error: 'unknown_feature' }; 3596 // Alleen een guardian van dit kind. Zonder deze regel zou iedereen met een 3597 // token een instelling van een vreemd kind kunnen aanvragen. 3598 if (!Guardianship.listWards(site.slug).some((w) => w.other_uri === uri)) { 3599 return { status: 403, error: 'not_your_ward' }; 3600 } 3601 const base = (process.env.PUBLIC_BASE_URL || '').replace(/\/+$/, ''); 3602 const me = actorId(base, site.slug); 3603 const offerId = `${me}/gated/${Date.now().toString(36)}${Math.floor(Math.random() * 1e4).toString(36)}`; 3604 const offer = Guardianship.gated.buildGatedOffer(offerId, me, uri, feature, allow); 3605 // Ons eigen spoor van wat we stuurden: de server van de ward antwoordt op deze 3606 // Offer zodra het besluit valt, en dat antwoord heeft een rij nodig om in te 3607 // landen. Het is ook het enige waardoor het scherm van de voorsteller meer kan 3608 // zeggen dan een knoptekst. 3609 Guardianship.gated.recordSent(offerId, site.slug, uri, feature, allow); 3610 deliverToActor(site, uri, offer).catch(() => { /* queued, best-effort */ }); 3611 const localSlug = (base && uri.startsWith(`${base}/`)) ? uri.replace(/\/+$/, '').split('/').pop() : null; 3612 const progress = localSlug ? Guardianship.gated.gatedProgress(localSlug, feature) : null; 3613 return { status: 200, ok: true, allow, state: 'open', offerId, ...(progress || { federated: true }) }; 3614 } 3615 3540 3616 export async function deliverReply(site, { postId, postSlug, parent, text, html, language, attachments, mentions, visibility }) { 3541 3617 const base = (process.env.PUBLIC_BASE_URL || '').replace(/\/+$/, ''); … … 6113 6189 getNotifications, listBlocks, isBlockedAny, blockTarget, unblock, 6114 6190 deliverWithRetry, enqueueDelivery, processDeliveryQueue, startDeliveryWorker, 6115 getReplyUris, getThread, filterThreadToCircle, gateAttachments, stripEmojiTags, markNotificationsSeen, countUnseenNotifications, hasPlayableAudio,6191 proposeGate, getReplyUris, getThread, filterThreadToCircle, gateAttachments, stripEmojiTags, markNotificationsSeen, countUnseenNotifications, hasPlayableAudio, 6116 6192 linkifyBody, bakePostContent, bakePostContentWithMentions, listFollowers, removeFollower, listConnections, 6117 6193 noteVisibility, belongsInTimeline, playerUrlFor, isRejectedObject, rejectInteraction, interactionReportTarget,
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)