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

View File

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

View File

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

View File

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

View File

@@ -58,7 +58,7 @@ Categories.getCategoryById = async function (data) {
Categories.getTopicCount(data),
Categories.getWatchState([data.cid], 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) {