Changeset 834bcc3 in Klonkt for src/services/CircleService.js
- Timestamp:
- 06/23/2026 06:14:27 PM (3 months ago)
- Branches:
- main
- Children:
- d774679
- Parents:
- bb42dfb
- File:
-
- 1 edited
-
src/services/CircleService.js (modified) (16 diffs)
Legend:
- Unmodified
- Added
- Removed
-
src/services/CircleService.js
rbb42dfb r834bcc3 1 // CircleService.js — pull -kant van Cirkels (v1).1 // CircleService.js — pull side of Circles (v1). 2 2 // 3 // Haalt per circle_link de remote actor + outbox op, verifieert de Ed25519-4 // handtekening, sanitiseert en cachet publiekeposts in remote_actors/remote_posts.5 // Alleen LEZEN van remote; nooit schrijven. Zie docs/cirkels-v1-spec.md §5b.3 // Per circle_link: fetches the remote actor + outbox, verifies the Ed25519 4 // signature, sanitizes, and caches public posts in remote_actors/remote_posts. 5 // READ ONLY from remote; never write. See docs/cirkels-v1-spec.md §5b. 6 6 7 7 import db from '../config/database.js'; … … 16 16 return String(s || '') 17 17 .replace(/<[^>]+>/g, ' ') 18 .replace(/\[\[[^\]]*\]\]/g, ' ') // [[playlist:..]]/[[track:..]]/[[album:..]]-shortcodes weg18 .replace(/\[\[[^\]]*\]\]/g, ' ') // strip [[playlist:..]] / [[track:..]] / [[album:..]] shortcodes 19 19 .replace(/\s+/g, ' ') 20 20 .trim(); … … 31 31 } 32 32 33 // AS Hashtag -array -> comma-separated tagnamen (zonder #), gesanitized.33 // AS Hashtag array -> comma-separated tag names (without #), sanitized. 34 34 function extractTags(tag) { 35 35 if (!Array.isArray(tag)) return null; … … 41 41 } 42 42 43 // Bron buiten de cirkel zetten met een leesbare reden (geen stille mislukking).44 // Aparte status 'outdated' zodat de Beheer-UI er een nette "update vereist"-45 // melding van kan maken i.p.v. een generieke fout.43 // Mark a source as outside the circle with a readable reason (no silent failure). 44 // Separate 'outdated' status so the admin UI can show a clean "update required" 45 // notice instead of a generic error. 46 46 function markOutdated(link, msg) { 47 // Gecachte posts van deze bron weghalen: we kunnen ze niet meer verifiëren of48 // verversen (proto-mismatch), dus ze horen niet meer in de cirkel-feed.47 // Remove cached posts from this source: we can no longer verify or refresh 48 // them (proto mismatch), so they no longer belong in the circle feed. 49 49 if (link.remote_actor_id) { 50 50 try { db.prepare('DELETE FROM remote_posts WHERE actor_id = ?').run(link.remote_actor_id); } catch {} … … 55 55 } 56 56 57 // Robu uste, defensieve fetch: alleen https, timeout, body-cap, redirect-follow.57 // Robust, defensive fetch: https only, timeout, body cap, redirect follow. 58 58 async function fetchText(url) { 59 59 if (!/^https:\/\//i.test(url)) throw new Error('alleen https toegestaan'); … … 66 66 headers: { 67 67 Accept: 'application/activity+json, application/json', 68 // Vertel de publisher onze proto → die kan ons met 426 weren als we te oud zijn.68 // Tell the publisher our proto → they can reject us with 426 if we are too old. 69 69 'Klonkt-Proto': String(KLONKT_PROTO), 70 70 }, … … 72 72 if (!res.ok) throw new Error(`HTTP ${res.status}`); 73 73 const buf = Buffer.from(await res.arrayBuffer()); 74 if (buf.length > MAX_BODY_BYTES) throw new Error('body t e groot');74 if (buf.length > MAX_BODY_BYTES) throw new Error('body too large'); 75 75 return { text: buf.toString('utf8'), headers: res.headers, finalUrl: res.url }; 76 76 } finally { … … 79 79 } 80 80 81 // Lazy prepares — de tabellen bestaan pas ná initializeDatabase(); dit module82 // wordt geïmporteerd vóór die call, dus niet op module-niveau prepare'n.81 // Lazy prepares — tables only exist after initializeDatabase(); this module is 82 // imported before that call, so do not prepare at module level. 83 83 let _stmts = null; 84 84 function stmts() { … … 107 107 const base = baseOf(link.remote_url); 108 108 109 // 1. Actor ophalen + valideren109 // 1. Fetch + validate actor 110 110 const actorUrl = `${base}/.klonkt/actor.json`; 111 111 const a = await fetchText(actorUrl); … … 117 117 if (originOf(actorId) !== originOf(actorUrl)) throw new Error('actor.id heeft andere origin dan de actor-URL'); 118 118 119 // Protocol -versie-gate. De proto zit óók in de outbox-handtekening-grondslag,120 // dus liegen in de (ongetekende) actor helpt niet: bij een echte mismatch faalt121 // de verificatie verderop alsnog. Hier vooral voor een DUIDELIJKE melding +122 // buitensluiten zonder stille mislukking.119 // Protocol version gate. The proto is also embedded in the outbox signing 120 // input, so lying in the (unsigned) actor does not help: a real mismatch 121 // will still fail verification later. This check is mainly for a CLEAR 122 // message + exclusion without silent failure. 123 123 const remoteProto = Number(actor.klonkt && actor.klonkt.proto) || 1; 124 124 if (remoteProto > KLONKT_PROTO) { … … 131 131 } 132 132 133 // TOFU: een sleutelwissel vereist expliciete herbevestiging(anti-hijack)133 // TOFU: a key change requires explicit re-confirmation (anti-hijack) 134 134 const existing = db.prepare('SELECT public_key FROM remote_actors WHERE id = ?').get(actorId); 135 135 if (existing && existing.public_key !== pubKey) { … … 146 146 }); 147 147 148 // 2. Outbox ophalen + handtekening verifiëren148 // 2. Fetch outbox + verify signature 149 149 const outboxUrl = actor.outbox || `${base}/.klonkt/outbox.json`; 150 150 const o = await fetchText(outboxUrl); … … 158 158 const items = Array.isArray(outbox.orderedItems) ? outbox.orderedItems.slice(0, MAX_ITEMS) : []; 159 159 160 // 3. Objecten sanitizen + cachen (same-origin als de actor = anti-impersonatie)160 // 3. Sanitize + cache objects (same origin as actor = anti-impersonation) 161 161 const actorOrigin = originOf(actorId); 162 162 const seen = new Set(); … … 186 186 } 187 187 188 // 4. Pruning: posts die niet meer in de outbox staan opruimen188 // 4. Pruning: remove posts that are no longer in the outbox 189 189 const known = db.prepare('SELECT id FROM remote_posts WHERE actor_id = ?').all(actorId).map((r) => r.id); 190 190 const stale = known.filter((id) => !seen.has(id)); … … 194 194 } 195 195 196 // Naam automatisch overnemen van de remote actor (geen handmatige invoer nodig).197 // COALESCE: heeft de actor geen naam, dan blijft een evt. bestaand label staan.196 // Automatically adopt the name from the remote actor (no manual entry needed). 197 // COALESCE: if the actor has no name, any existing label is preserved. 198 198 db.prepare( 199 199 "UPDATE circle_links SET remote_actor_id=?, label=COALESCE(?, label), last_synced=CURRENT_TIMESTAMP, status='active', last_error=NULL WHERE id=?" … … 221 221 222 222 let _timer = null; 223 /** Periodi eke achtergrond-sync (gated op tenancy='circle' binnensync()). */223 /** Periodic background sync (gated on tenancy='circle' inside sync()). */ 224 224 export function startCircleSyncLoop(intervalMs = 15 * 60 * 1000) { 225 225 if (_timer) return; 226 226 const run = () => { sync().catch((e) => console.error('[cirkels] sync-fout:', e.message)); }; 227 setTimeout(run, 30 * 1000); // korte delay naboot227 setTimeout(run, 30 * 1000); // short delay after boot 228 228 _timer = setInterval(run, intervalMs); 229 229 if (_timer.unref) _timer.unref();
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)