Changeset d3b9f68 in Klonkt
- Timestamp:
- 06/30/2026 02:38:47 AM (2 months ago)
- Branches:
- main
- Children:
- 432831c
- Parents:
- e0a1ec1
- Location:
- src
- Files:
-
- 2 edited
-
routes/activitypub.js (modified) (3 diffs)
-
services/ActivityPubService.js (modified) (22 diffs)
Legend:
- Unmodified
- Added
- Removed
-
src/routes/activitypub.js
re0a1ec1 rd3b9f68 132 132 return res.redirect(302, src); 133 133 } 134 return AP.sendAP(res, { '@context': 'https://www.w3.org/ns/activitystreams', ...note });134 return AP.sendAP(res, { '@context': AP.AP_CONTEXT, ...note }); 135 135 } 136 136 const site = db.prepare('SELECT * FROM sites WHERE id = ?').get(post.site_id); … … 142 142 return res.redirect(302, note.url || (baseUrl(req) + '/')); 143 143 } 144 AP.sendAP(res, { '@context': 'https://www.w3.org/ns/activitystreams', ...note });144 AP.sendAP(res, { '@context': AP.AP_CONTEXT, ...note }); 145 145 }); 146 146 … … 150 150 const items = AP.getReplyUris(base, req.params.id); 151 151 AP.sendAP(res, { 152 '@context': 'https://www.w3.org/ns/activitystreams',152 '@context': AP.AP_CONTEXT, 153 153 id: `${base}/ap/notes/${req.params.id}/replies`, 154 154 type: 'OrderedCollection', -
src/services/ActivityPubService.js
re0a1ec1 rd3b9f68 23 23 24 24 const PUBLIC = 'https://www.w3.org/ns/activitystreams#Public'; 25 // Full JSON-LD context for every AP object we emit: AS2 core + security (publicKey) + the 26 // extension terms we actually use (Mastodon/toot + schema.org), each with a term definition 27 // so a strict JSON-LD processor resolves them instead of dropping them → valid AS2/JSON-LD. 28 // This is the same context shape Mastodon publishes, so Mastodon sees no change. 29 const AP_CONTEXT = [ 30 'https://www.w3.org/ns/activitystreams', 31 'https://w3id.org/security/v1', 32 { 33 toot: 'http://joinmastodon.org/ns#', 34 schema: 'http://schema.org#', 35 sensitive: 'as:sensitive', 36 Hashtag: 'as:Hashtag', 37 manuallyApprovesFollowers: 'as:manuallyApprovesFollowers', 38 discoverable: 'toot:discoverable', 39 featured: { '@id': 'toot:featured', '@type': '@id' }, 40 PropertyValue: 'schema:PropertyValue', 41 value: 'schema:value', 42 embedUrl: { '@id': 'schema:embedUrl', '@type': '@id' }, 43 }, 44 ]; 25 45 26 46 // Short random suffix so two activity ids minted in the same millisecond (e.g. … … 125 145 const keys = getOrCreateKeys(site.slug); 126 146 const actor = { 127 '@context': ['https://www.w3.org/ns/activitystreams', 'https://w3id.org/security/v1'],147 '@context': AP_CONTEXT, 128 148 id, 129 149 type: 'Person', … … 351 371 const note = buildNote(base, site, post); 352 372 return { 353 '@context': 'https://www.w3.org/ns/activitystreams',373 '@context': AP_CONTEXT, 354 374 id: note.id + '#create', 355 375 type: 'Create', … … 366 386 const items = (posts || []).slice(0, MAX_OUTBOX).map((p) => buildCreate(base, site, p)); 367 387 return { 368 '@context': 'https://www.w3.org/ns/activitystreams',388 '@context': AP_CONTEXT, 369 389 id, 370 390 type: 'OrderedCollection', … … 377 397 const id = `${actorId(base, site.slug)}/followers`; 378 398 return { 379 '@context': 'https://www.w3.org/ns/activitystreams',399 '@context': AP_CONTEXT, 380 400 id, 381 401 type: 'OrderedCollection', … … 390 410 const id = `${actorId(base, site.slug)}/following`; 391 411 return { 392 '@context': 'https://www.w3.org/ns/activitystreams',412 '@context': AP_CONTEXT, 393 413 id, 394 414 type: 'OrderedCollection', … … 405 425 const items = (posts || []).map((p) => buildNote(base, site, p)); 406 426 return { 407 '@context': 'https://www.w3.org/ns/activitystreams',427 '@context': AP_CONTEXT, 408 428 id, 409 429 type: 'OrderedCollection', … … 721 741 const me = actorId(base, slug); 722 742 const keys = getOrCreateKeys(slug); 723 const accept = { '@context': 'https://www.w3.org/ns/activitystreams', id: `${me}#accept-${Date.now()}-${rid()}`, type: 'Accept', actor: me, object: act };743 const accept = { '@context': AP_CONTEXT, id: `${me}#accept-${Date.now()}-${rid()}`, type: 'Accept', actor: me, object: act }; 724 744 deliver(remote.inbox, accept, `${me}#main-key`, keys.private_pem).catch((e) => console.warn('[AP] Accept delivery failed:', e.message)); 725 745 // Auto-backfill: send our recent posts as Create so the instance has our history … … 909 929 const nid = noteId(base, post.id); 910 930 const del = { 911 '@context': 'https://www.w3.org/ns/activitystreams',931 '@context': AP_CONTEXT, 912 932 id: `${nid}#delete-${Date.now()}-${rid()}`, 913 933 type: 'Delete', … … 932 952 note.updated = new Date().toISOString(); 933 953 const update = { 934 '@context': 'https://www.w3.org/ns/activitystreams',954 '@context': AP_CONTEXT, 935 955 id: `${noteId(base, post.id)}#update-${Date.now()}-${rid()}`, 936 956 type: 'Update', actor: me, to: [PUBLIC], cc: [`${me}/followers`], … … 952 972 const me = actorId(base, site.slug); 953 973 const update = { 954 '@context': ['https://www.w3.org/ns/activitystreams', 'https://w3id.org/security/v1'],974 '@context': AP_CONTEXT, 955 975 id: `${me}#update-${Date.now()}-${rid()}`, 956 976 type: 'Update', actor: me, to: [PUBLIC], cc: [`${me}/followers`], … … 976 996 const keyId = `${me}#main-key`; 977 997 const featured = `${me}/featured`; 978 const AS = 'https://www.w3.org/ns/activitystreams';979 998 const note = (id) => noteId(base, id); 980 999 const pinned = db.prepare( … … 986 1005 // 1. Remove every current pin so Mastodon can recreate them in order. 987 1006 for (const id of removeIds) { 988 const rm = { '@context': A S, id: `${me}#rm-${id}-${Date.now()}-${rid()}`, type: 'Remove', actor: me, object: note(id), target: featured, to: [PUBLIC] };1007 const rm = { '@context': AP_CONTEXT, id: `${me}#rm-${id}-${Date.now()}-${rid()}`, type: 'Remove', actor: me, object: note(id), target: featured, to: [PUBLIC] }; 989 1008 for (const inbox of inboxes) deliver(inbox, rm, keyId, keys.private_pem).catch(() => { /* best-effort */ }); 990 1009 } … … 993 1012 // 2. Add in rank-DESC order, gaps so each StatusPin gets an increasing created_at. 994 1013 for (const p of pinned) { 995 const add = { '@context': A S, id: `${me}#add-${p.id}-${Date.now()}-${rid()}`, type: 'Add', actor: me, object: note(p.id), target: featured, to: [PUBLIC], cc: [`${me}/followers`] };1014 const add = { '@context': AP_CONTEXT, id: `${me}#add-${p.id}-${Date.now()}-${rid()}`, type: 'Add', actor: me, object: note(p.id), target: featured, to: [PUBLIC], cc: [`${me}/followers`] }; 996 1015 for (const inbox of inboxes) deliver(inbox, add, keyId, keys.private_pem).catch(() => { /* best-effort */ }); 997 1016 await new Promise((r) => setTimeout(r, 2000)); … … 1146 1165 const note = buildReplyNote(base, site, row); 1147 1166 const create = { 1148 '@context': 'https://www.w3.org/ns/activitystreams',1167 '@context': AP_CONTEXT, 1149 1168 id: note.id + '#create', type: 'Create', actor: me, 1150 1169 published: note.published, to: note.to, cc: note.cc, object: note, … … 1267 1286 const me = actorId(base, site.slug); 1268 1287 const nid = noteId(base, row.id); 1269 const del = { '@context': 'https://www.w3.org/ns/activitystreams', id: `${nid}#delete-${Date.now()}-${rid()}`, type: 'Delete', actor: me, to: [PUBLIC], object: { id: nid, type: 'Tombstone' } };1288 const del = { '@context': AP_CONTEXT, id: `${nid}#delete-${Date.now()}-${rid()}`, type: 'Delete', actor: me, to: [PUBLIC], object: { id: nid, type: 'Tombstone' } }; 1270 1289 const keys = getOrCreateKeys(site.slug); 1271 1290 const inboxes = new Set(); … … 1300 1319 note.updated = new Date().toISOString(); 1301 1320 const update = { 1302 '@context': 'https://www.w3.org/ns/activitystreams',1321 '@context': AP_CONTEXT, 1303 1322 id: `${note.id}#update-${Date.now()}-${rid()}`, type: 'Update', actor: me, 1304 1323 published: note.published, updated: note.updated, to: note.to, cc: note.cc, object: note, … … 1516 1535 const followId = `${me}#follow-${Date.now()}-${rid()}`; 1517 1536 fwStmts().ins.run(site.slug, actor.id, ai.handle, ai.name, ai.icon, ai.url, actor.inbox, followId, 'pending', autoBoost ? 1 : 0); 1518 const follow = { '@context': 'https://www.w3.org/ns/activitystreams', id: followId, type: 'Follow', actor: me, object: actor.id };1537 const follow = { '@context': AP_CONTEXT, id: followId, type: 'Follow', actor: me, object: actor.id }; 1519 1538 try { await deliver(actor.inbox, follow, `${me}#main-key`, keys.private_pem); } 1520 1539 catch (e) { console.warn('[AP] follow deliver failed:', e.message); } … … 1546 1565 // rather than send an unmatchable one. Deliver durably via the retry queue. 1547 1566 if (row && row.inbox && row.follow_id) { 1548 const undo = { '@context': 'https://www.w3.org/ns/activitystreams', id: `${me}/undo/${Date.now()}-${rid()}`, type: 'Undo', actor: me, object: { id: row.follow_id, type: 'Follow', actor: me, object: actorUri } };1567 const undo = { '@context': AP_CONTEXT, id: `${me}/undo/${Date.now()}-${rid()}`, type: 'Undo', actor: me, object: { id: row.follow_id, type: 'Follow', actor: me, object: actorUri } }; 1549 1568 deliverWithRetry(site.slug, row.inbox, undo, `${me}#main-key`, keys.private_pem); 1550 1569 } else if (row && row.inbox) { … … 1576 1595 const inner = kind === 'unboost' ? 'Announce' : 'Like'; 1577 1596 act = { 1578 '@context': 'https://www.w3.org/ns/activitystreams',1597 '@context': AP_CONTEXT, 1579 1598 id: `${me}/undo/${Date.now()}-${rid()}`, type: 'Undo', actor: me, 1580 1599 object: { id: `${me}/${inner.toLowerCase()}/${Date.now()}-${rid()}`, type: inner, actor: me, object: targetNoteId }, … … 1584 1603 const type = kind === 'boost' ? 'Announce' : 'Like'; 1585 1604 act = { 1586 '@context': 'https://www.w3.org/ns/activitystreams',1605 '@context': AP_CONTEXT, 1587 1606 id: `${me}/${type.toLowerCase()}/${Date.now()}-${rid()}`, 1588 1607 type, actor: me, object: targetNoteId, … … 1684 1703 1685 1704 export default { 1686 getOrCreateKeys, apWants, sendAP, actorId, noteId,1705 AP_CONTEXT, getOrCreateKeys, apWants, sendAP, actorId, noteId, 1687 1706 buildActor, buildNote, buildCreate, buildOutbox, buildFollowers, buildFollowing, buildFeatured, 1688 1707 followerCount, deliver, fetchActor, verifyRequest, handleInbox, deliverCreate, deliverDelete, deliverUpdate, deliverActorUpdate, resyncFeaturedPins,
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)