refactor: Actors.getLocalFollowers to Actors.getFollowers, can pass in both local and remote ids

This commit is contained in:
Julian Lam
2026-01-21 14:42:16 -05:00
parent eb27b96430
commit fac3185974
5 changed files with 32 additions and 16 deletions

View File

@@ -21,6 +21,11 @@ const failedWebfingerCache = TTLCache({
const activitypub = module.parent.exports; const activitypub = module.parent.exports;
const Actors = module.exports; const Actors = module.exports;
Actors._followerCache = TTLCache({
name: 'ap-follower-cache',
max: 5000,
ttl: 1000 * 60 * 60, // 1 hour
});
Actors.qualify = async (ids, options = {}) => { Actors.qualify = async (ids, options = {}) => {
/** /**
@@ -420,17 +425,22 @@ Actors.assertGroup = async (ids, options = {}) => {
return categoryObjs; return categoryObjs;
}; };
Actors.getLocalFollowers = async (id) => { Actors.getFollowers = async (id) => {
// Returns local uids and cids that follow a remote actor (by id) /**
const response = { * Returns followers by local or remote id. Pass in a...
* - Remote id: returns local uids/cids that follow
* - Local id: returns remote uids that follow
*/
let response = Actors._followerCache.get(id);
if (response) {
return response;
}
response = {
uids: new Set(), uids: new Set(),
cids: new Set(), cids: new Set(),
}; };
if (!activitypub.helpers.isUri(id)) {
return response;
}
const [isUser, isCategory] = await Promise.all([ const [isUser, isCategory] = await Promise.all([
user.exists(id), user.exists(id),
categories.exists(id), categories.exists(id),
@@ -440,10 +450,10 @@ Actors.getLocalFollowers = async (id) => {
const members = await db.getSortedSetMembers(`followersRemote:${id}`); const members = await db.getSortedSetMembers(`followersRemote:${id}`);
members.forEach((id) => { members.forEach((id) => {
if (utils.isNumber(id)) { if (id.startsWith('cid|') && utils.isNumber(id.slice(4))) {
response.uids.add(parseInt(id, 10));
} else if (id.startsWith('cid|') && utils.isNumber(id.slice(4))) {
response.cids.add(parseInt(id.slice(4), 10)); response.cids.add(parseInt(id.slice(4), 10));
} else {
response.uids.add(utils.isNumber(id) ? parseInt(id, 10) : id);
} }
}); });
} else if (isCategory) { } else if (isCategory) {
@@ -462,6 +472,7 @@ Actors.getLocalFollowers = async (id) => {
}); });
} }
Actors._followerCache.set(id, response);
return response; return response;
}; };

View File

@@ -47,7 +47,7 @@ inbox.create = async (req) => {
} }
// Category sync, remove when cross-posting available // Category sync, remove when cross-posting available
const { cids } = await activitypub.actors.getLocalFollowers(actor); const { cids } = await activitypub.actors.getFollowers(actor);
let cid = null; let cid = null;
if (cids.size > 0) { if (cids.size > 0) {
cid = Array.from(cids)[0]; cid = Array.from(cids)[0];
@@ -202,7 +202,7 @@ inbox.update = async (req) => {
return await activitypub.notes.assertPrivate(object); return await activitypub.notes.assertPrivate(object);
} }
const { cids } = await activitypub.actors.getLocalFollowers(actor); const { cids } = await activitypub.actors.getFollowers(actor);
let cid = null; let cid = null;
if (cids.size > 0) { if (cids.size > 0) {
cid = Array.from(cids)[0]; cid = Array.from(cids)[0];
@@ -387,7 +387,7 @@ inbox.announce = async (req) => {
let pid; let pid;
// Category sync, remove when cross-posting available // Category sync, remove when cross-posting available
const { cids } = await activitypub.actors.getLocalFollowers(actor); const { cids } = await activitypub.actors.getFollowers(actor);
const syncedCids = Array.from(cids); const syncedCids = Array.from(cids);
// 1b12 announce // 1b12 announce
@@ -522,6 +522,7 @@ inbox.follow = async (req) => {
const followerRemoteCount = await db.sortedSetCard(`followersRemote:${id}`); const followerRemoteCount = await db.sortedSetCard(`followersRemote:${id}`);
await user.setUserField(id, 'followerRemoteCount', followerRemoteCount); await user.setUserField(id, 'followerRemoteCount', followerRemoteCount);
activitypub.actors._followerCache.del(id);
await user.onFollow(actor, id); await user.onFollow(actor, id);
activitypub.send('uid', id, actor, { activitypub.send('uid', id, actor, {
@@ -613,6 +614,8 @@ inbox.accept = async (req) => {
db.sortedSetAdd(`followersRemote:${actor}`, timestamp, `cid|${id}`), // for notes assertion checking db.sortedSetAdd(`followersRemote:${actor}`, timestamp, `cid|${id}`), // for notes assertion checking
]); ]);
} }
activitypub.actors._followerCache.del(actor);
} }
}; };
@@ -650,6 +653,7 @@ inbox.undo = async (req) => {
const followerRemoteCount = await db.sortedSetCard(`followerRemote:${id}`); const followerRemoteCount = await db.sortedSetCard(`followerRemote:${id}`);
await user.setUserField(id, 'followerRemoteCount', followerRemoteCount); await user.setUserField(id, 'followerRemoteCount', followerRemoteCount);
notifications.rescind(`follow:${id}:uid:${actor}`); notifications.rescind(`follow:${id}:uid:${actor}`);
activitypub.actors._followerCache.del(id);
break; break;
} }

View File

@@ -506,7 +506,7 @@ Notes.updateLocalRecipients = async (id, { to, cc }) => {
const followedUid = await db.getObjectField('followersUrl:uid', recipient); const followedUid = await db.getObjectField('followersUrl:uid', recipient);
if (followedUid) { if (followedUid) {
const { uids: followers } = await activitypub.actors.getLocalFollowers(followedUid); const { uids: followers } = await activitypub.actors.getFollowers(followedUid);
if (followers.size > 0) { if (followers.size > 0) {
followers.forEach((uid) => { followers.forEach((uid) => {
uids.add(uid); uids.add(uid);
@@ -590,7 +590,7 @@ Notes.syncUserInboxes = async function (tid, uid) {
}); });
// Category followers // Category followers
const categoryFollowers = await activitypub.actors.getLocalFollowers(cid); const categoryFollowers = await activitypub.actors.getFollowers(cid);
categoryFollowers.uids.forEach((uid) => { categoryFollowers.uids.forEach((uid) => {
uids.add(uid); uids.add(uid);
}); });

View File

@@ -513,6 +513,7 @@ Out.undo.follow = enabledCheck(async (type, id, actor) => {
db.sortedSetRemove(`followersRemote:${actor}`, `cid|${id}`), db.sortedSetRemove(`followersRemote:${actor}`, `cid|${id}`),
]); ]);
} }
activitypub.actors._followerCache.del(actor);
}); });
Out.undo.like = enabledCheck(async (uid, pid) => { Out.undo.like = enabledCheck(async (uid, pid) => {

View File

@@ -58,7 +58,7 @@ Categories.getCategoryById = async function (data) {
Categories.getTopicCount(data), Categories.getTopicCount(data),
Categories.getWatchState([data.cid], data.uid), Categories.getWatchState([data.cid], data.uid),
getChildrenTree(category, data.uid), getChildrenTree(category, data.uid),
!utils.isNumber(data.cid) ? activitypub.actors.getLocalFollowers(data.cid) : null, !utils.isNumber(data.cid) ? activitypub.actors.getFollowers(data.cid) : null,
]; ];
if (category.parentCid) { if (category.parentCid) {