Changeset 7922694 in Klonkt
- Timestamp:
- 07/20/2026 11:03:52 PM (7 weeks ago)
- Branches:
- main
- Children:
- 5143ccf
- Parents:
- 155c24e
- git-author:
- Robin <roboburr@…> (07/20/2026 11:03:51 PM)
- git-committer:
- Robin <roboburr@…> (07/20/2026 11:03:52 PM)
- Files:
-
- 1 added
- 3 edited
-
src/config/database.js (modified) (1 diff)
-
src/routes/activitypub.js (modified) (2 diffs)
-
src/services/ActivityPubService.js (modified) (4 diffs)
-
test/c2s-contacts.test.js (added)
Legend:
- Unmodified
- Added
- Removed
-
src/config/database.js
r155c24e r7922694 444 444 ensureColumn('ap_outbox', 'to_actors', 'TEXT'); // JSON array of recipient actor URIs for direct notes 445 445 ensureColumn('ap_outbox', 'help_request', 'INTEGER'); // FEP-633c shaer:helpRequest (ward's call for help) 446 ensureColumn('ap_followers', 'name', 'TEXT'); // cached display name (shaer-aa3) 447 ensureColumn('ap_followers', 'handle', 'TEXT'); // @user@host 448 ensureColumn('ap_followers', 'icon', 'TEXT'); // avatar URL 446 449 } 447 450 -
src/routes/activitypub.js
r155c24e r7922694 175 175 if (!site) return res.status(404).end(); 176 176 if (owner) { 177 const items = db.prepare('SELECT actor_uri FROM ap_followers WHERE slug = ? ORDER BY created_at').all(site.slug).map((r) => r.actor_uri); 177 const uris = db.prepare('SELECT actor_uri FROM ap_followers WHERE slug = ? ORDER BY created_at').all(site.slug).map((r) => r.actor_uri); 178 const items = uris.map((u) => AP.buildActorRef(site.slug, u)); // name + avatar (shaer-aa3) 178 179 return AP.sendAP(res, AP.buildFollowers(baseUrl(req), site, items.length, items)); 179 180 } … … 190 191 if (owner) { 191 192 let items = []; 192 try { items = db.prepare("SELECT actor_uri FROM ap_following WHERE slug = ? AND status = 'accepted' ORDER BY created_at").all(site.slug).map((r) => r.actor_uri); } catch { /* table may not exist */ }193 try { items = db.prepare("SELECT actor_uri FROM ap_following WHERE slug = ? AND status = 'accepted' ORDER BY created_at").all(site.slug).map((r) => AP.buildActorRef(site.slug, r.actor_uri)); } catch { /* table may not exist */ } 193 194 return AP.sendAP(res, AP.buildFollowing(baseUrl(req), site, items.length, items)); 194 195 } -
src/services/ActivityPubService.js
r155c24e r7922694 621 621 622 622 // ── followers store (lazy stmts) ────────────────────────────────── 623 let _insF, _ delF, _listF, _cntF;623 let _insF, _updFDisp, _delF, _listF, _cntF; 624 624 function fStmts() { 625 625 if (!_insF) { 626 _insF = db.prepare('INSERT OR IGNORE INTO ap_followers (slug, actor_uri, inbox, shared_inbox, created_at) VALUES (?,?,?,?,CURRENT_TIMESTAMP)'); 626 _insF = db.prepare('INSERT OR IGNORE INTO ap_followers (slug, actor_uri, inbox, shared_inbox, name, handle, icon, created_at) VALUES (?,?,?,?,?,?,?,CURRENT_TIMESTAMP)'); 627 _updFDisp = db.prepare('UPDATE ap_followers SET name = COALESCE(?, name), handle = COALESCE(?, handle), icon = COALESCE(?, icon) WHERE slug = ? AND actor_uri = ?'); 627 628 _delF = db.prepare('DELETE FROM ap_followers WHERE slug = ? AND actor_uri = ?'); 628 629 _listF = db.prepare('SELECT inbox, shared_inbox FROM ap_followers WHERE slug = ?'); … … 646 647 const info = db.prepare('DELETE FROM ap_followers WHERE slug = ? AND id = ?').run(slug, id); 647 648 return info.changes > 0; 649 } 650 651 // Best cached display for an actor URI, across the caches Klonkt already fills: 652 // followers (now with name/icon), following, interactions, timeline, mentions. 653 // Falls back to a handle derived from the URI. Display info is not sensitive. 654 export function actorDisplay(slug, uri) { 655 const ok = (r) => r && (r.name || r.icon); 656 try { 657 let r = db.prepare('SELECT name, handle, icon FROM ap_followers WHERE slug = ? AND actor_uri = ?').get(slug, uri); 658 if (ok(r)) return { name: r.name, handle: r.handle || deriveHandle(uri), icon: r.icon }; 659 r = db.prepare('SELECT name, handle, icon FROM ap_following WHERE slug = ? AND actor_uri = ?').get(slug, uri); 660 if (ok(r)) return { name: r.name, handle: r.handle || deriveHandle(uri), icon: r.icon }; 661 r = db.prepare('SELECT actor_name AS name, actor_handle AS handle, actor_icon AS icon FROM ap_interactions WHERE actor_uri = ? AND (actor_name IS NOT NULL OR actor_icon IS NOT NULL) ORDER BY created_at DESC LIMIT 1').get(uri); 662 if (ok(r)) return { name: r.name, handle: r.handle || deriveHandle(uri), icon: r.icon }; 663 r = db.prepare('SELECT author_name AS name, author_handle AS handle, author_icon AS icon FROM ap_timeline WHERE author_uri = ? AND (author_name IS NOT NULL OR author_icon IS NOT NULL) LIMIT 1').get(uri); 664 if (ok(r)) return { name: r.name, handle: r.handle || deriveHandle(uri), icon: r.icon }; 665 r = db.prepare('SELECT actor_name AS name, actor_handle AS handle, actor_icon AS icon FROM ap_mentions WHERE actor_uri = ? AND (actor_name IS NOT NULL OR actor_icon IS NOT NULL) ORDER BY created_at DESC LIMIT 1').get(uri); 666 if (ok(r)) return { name: r.name, handle: r.handle || deriveHandle(uri), icon: r.icon }; 667 } catch { /* ignore */ } 668 return { name: null, handle: deriveHandle(uri), icon: null }; 669 } 670 671 // AS2 actor reference with display, for the owner C2S followers/following view. 672 // preferredUsername = the local part of the handle; name = the set display name. 673 export function buildActorRef(slug, uri) { 674 const d = actorDisplay(slug, uri); 675 const user = d.handle && d.handle[0] === '@' ? d.handle.slice(1).split('@')[0] : null; 676 const out = { id: uri, type: 'Person' }; 677 if (d.name) out.name = d.name; 678 if (user) out.preferredUsername = user; 679 if (d.icon) out.icon = { type: 'Image', url: d.icon }; 680 return out; 648 681 } 649 682 … … 1255 1288 if (!remote || !remote.inbox) return 202; // can't reach them → drop quietly 1256 1289 const sharedInbox = (remote.endpoints && remote.endpoints.sharedInbox) || null; 1257 fStmts().ins.run(slug, who, remote.inbox, sharedInbox); 1290 const fi = actorInfo(remote, who); // cache display for the friends list (shaer-aa3) 1291 fStmts().ins.run(slug, who, remote.inbox, sharedInbox, fi.name, fi.handle, fi.icon); 1292 try { _updFDisp.run(fi.name, fi.handle, fi.icon, slug, who); } catch { /* best effort */ } 1258 1293 const me = actorId(base, slug); 1259 1294 const keys = getOrCreateKeys(slug); … … 3035 3070 linkifyBody, bakePostContent, bakePostContentWithMentions, listFollowers, removeFollower, listConnections, 3036 3071 noteVisibility, isRejectedObject, rejectInteraction, interactionReportTarget, 3037 getMessages, notificationsSeenAt, ingestOutboxActivity, c2sVisibility, 3072 getMessages, notificationsSeenAt, ingestOutboxActivity, c2sVisibility, actorDisplay, buildActorRef, 3038 3073 };
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)