Changeset aae5881 in Klonkt
- Timestamp:
- 08/10/2026 08:14:13 AM (4 weeks ago)
- Branches:
- main
- Children:
- 86ec01b
- Parents:
- 75f2897
- Files:
-
- 3 edited
-
src/routes/activitypub.js (modified) (1 diff)
-
src/services/ActivityPubService.js (modified) (4 diffs)
-
test/thread.test.js (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
src/routes/activitypub.js
r75f2897 raae5881 938 938 const isWard = (() => { try { return Guardianship.listGuardians(auth.site.slug).length > 0; } catch { return false; } })(); 939 939 const uit = await AP.getThread(auth.site.slug, objectUri); 940 if (!uit.found) return res.status(404).json({ error: 'note not reachable' }); 940 if (!uit.found) { 941 // WIENS schuld is dit? De oude melding zei "jouw server kon het niet 942 // laden" terwijl onze server het prima deed en de BRON weigerde -- dat 943 // wees naar de verkeerde partij (Barts melding, 10-8: een post van een 944 // account dat hij vanochtend nog volgde, en dat nu niet meer). 945 // 401/403/404/410 is een besluit van die server; al het andere, inclusief 946 // een status die we niet eens kregen, is een storing. 947 const geweigerd = [401, 403, 404, 410].includes(uit.sourceStatus); 948 return res.status(geweigerd ? 404 : 502) 949 .json({ error: geweigerd ? 'not shared by source' : 'source unreachable', sourceStatus: uit.sourceStatus || undefined }); 950 } 941 951 // De poortstand komt uit de kolom (shaer-9y2): expliciete 0/1 van de 942 952 // guardians wint, de automatiek is dicht-voor-een-ward. Dicht is de KRING, -
src/services/ActivityPubService.js
r75f2897 raae5881 3698 3698 if (hit && Date.now() - hit.at < THREAD_VIEW_TTL_MS) return hit.out; 3699 3699 3700 const get = (u) => localNoteObject(u, slug) || signedGetJson(slug, u); 3700 // De status waarmee de BRON antwoordde op de note zelf. 401/403/404/410 is 3701 // een besluit van die server (niet gedeeld, of weg); alles daarbuiten -- ook 3702 // een stuk netwerk dat wegviel -- is een storing. De route moet dat verschil 3703 // kunnen zeggen, anders wijst de melding naar de verkeerde partij. 3704 let sourceStatus = 0; 3705 const get = (u) => localNoteObject(u, slug) || signedGetJson(slug, u, (st) => { sourceStatus = st; }); 3701 3706 const note = await get(objectUri); 3702 3707 const repliesRef = note && note.replies; … … 3781 3786 })).sort((a, b) => String(a.published || '').localeCompare(String(b.published || ''))); 3782 3787 3783 const out = { notes, found: !!note };3788 const out = { notes, found: !!note, sourceStatus }; 3784 3789 threadViewCache.set(key, { at: Date.now(), out }); 3785 3790 if (threadViewCache.size > THREAD_VIEW_CACHE_MAX) { … … 5001 5006 * (request-target) host date, the set verifyRequest checks. 5002 5007 */ 5003 async function signedGetJson(slug, url ) {5008 async function signedGetJson(slug, url, onStatus) { 5004 5009 try { 5005 5010 const base = (process.env.PUBLIC_BASE_URL || '').replace(/\/+$/, ''); … … 5014 5019 const sig = `keyId="${me}#main-key",algorithm="rsa-sha256",headers="(request-target) host date",signature="${signature}"`; 5015 5020 const r = await safeFetch(url, { headers: { Accept: 'application/activity+json', Date: date, Signature: sig } }); 5021 // De status doorgeven aan wie erom vroeg: null alleen zegt "het lukte 5022 // niet", en dat is te weinig om een WEIGERING van een STORING te 5023 // onderscheiden. Wie geen callback meegeeft merkt hier niets van. 5024 if (typeof onStatus === 'function') onStatus(r.status); 5016 5025 if (!r.ok) return null; 5017 5026 const len = Number(r.headers.get('content-length') || 0); -
test/thread.test.js
r75f2897 raae5881 193 193 assert.ok(!na.attributedTo.tag, 'ook niet in de byline'); 194 194 }); 195 196 // ── Wiens schuld is het? ───────────────────────────────────────────── 197 // 198 // "Replies could not be loaded from your server" wees naar de verkeerde 199 // partij: onze server deed het prima en de BRON weigerde. Een weigering 200 // (401/403/404/410 -- niet gedeeld, of weg) is iets anders dan een storing, en 201 // alleen de server weet welke van de twee het was. 202 test('een weigering van de bron is 404, een storing is 502', async (t) => { 203 const crypto = await import('crypto'); 204 const express = (await import('express')).default; 205 const routes = (await import('../src/routes/activitypub.js')).default; 206 207 const bearer = 'test-token-' + 'd'.repeat(24); 208 db.prepare('INSERT INTO oauth_tokens (token_hash, client_id, user_id, site_slug, scope) VALUES (?,?,?,?,?)') 209 .run(crypto.createHash('sha256').update(bearer).digest('base64url'), 'c', 'u1', 'kind', 'read write'); 210 211 const app = express(); 212 app.use(routes); 213 const server = app.listen(0); 214 t.after(() => server.close()); 215 await new Promise((r) => server.once('listening', r)); 216 217 const vorige = globalThis.fetch; 218 t.after(() => { globalThis.fetch = vorige; }); 219 const vraag = async (uri) => { 220 const r = await echteFetch( 221 `http://127.0.0.1:${server.address().port}/ap/users/kind/thread?object=${encodeURIComponent(uri)}`, 222 { headers: { Authorization: `Bearer ${bearer}` } }); 223 return { status: r.status, body: await r.json() }; 224 }; 225 226 // De bron zegt nee -- precies wat boiert.eu doet met een friends-only post 227 // voor iemand die de auteur niet (meer) volgt. 228 const DICHT = 'https://203.0.113.50/notes/dicht'; 229 globalThis.fetch = async () => new Response('nope', { status: 404 }); 230 const nee = await vraag(DICHT); 231 assert.equal(nee.status, 404); 232 assert.equal(nee.body.error, 'not shared by source'); 233 assert.equal(nee.body.sourceStatus, 404); 234 235 // De bron ligt eruit. Dezelfde lege uitkomst, een andere waarheid. 236 const STUK = 'https://203.0.113.51/notes/stuk'; 237 globalThis.fetch = async () => new Response('boem', { status: 500 }); 238 const stuk = await vraag(STUK); 239 assert.equal(stuk.status, 502); 240 assert.equal(stuk.body.error, 'source unreachable'); 241 });
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)