mirror of
https://github.com/svemagie/indiekit-endpoint-activitypub.git
synced 2026-04-02 15:44:58 +02:00
fix: follow/unfollow fails for remotely resolved profiles
POST /accounts/:id/follow returned 404 for actors resolved via Fedify (like @_followback@tags.pub) because resolveActorUrl only checked local data (followers/following/timeline). These actors aren't in local collections — they were resolved on-demand via WebFinger. Fix: add reverse lookup map (accountId hash → actorUrl) to the account cache. When resolveRemoteAccount resolves a profile, the hash-to-URL mapping is stored alongside the stats. resolveActorUrl checks this cache before scanning local collections.
This commit is contained in:
@@ -7,12 +7,18 @@
|
||||
* LRU-style with TTL — entries expire after 1 hour.
|
||||
*/
|
||||
|
||||
import { remoteActorId } from "./id-mapping.js";
|
||||
|
||||
const CACHE_TTL_MS = 60 * 60 * 1000; // 1 hour
|
||||
const MAX_ENTRIES = 500;
|
||||
|
||||
// Map<actorUrl, { followersCount, followingCount, statusesCount, createdAt, cachedAt }>
|
||||
const cache = new Map();
|
||||
|
||||
// Reverse map: accountId (hash) → actorUrl
|
||||
// Populated alongside the stats cache for follow/unfollow lookups
|
||||
const idToUrl = new Map();
|
||||
|
||||
/**
|
||||
* Store account stats in cache.
|
||||
* @param {string} actorUrl - The actor's URL (cache key)
|
||||
@@ -28,6 +34,10 @@ export function cacheAccountStats(actorUrl, stats) {
|
||||
}
|
||||
|
||||
cache.set(actorUrl, { ...stats, cachedAt: Date.now() });
|
||||
|
||||
// Maintain reverse lookup
|
||||
const hashId = remoteActorId(actorUrl);
|
||||
if (hashId) idToUrl.set(hashId, actorUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,3 +59,12 @@ export function getCachedAccountStats(actorUrl) {
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse lookup: get actor URL from account hash ID.
|
||||
* @param {string} hashId - The 24-char hex account ID
|
||||
* @returns {string|null} Actor URL or null
|
||||
*/
|
||||
export function getActorUrlFromId(hashId) {
|
||||
return idToUrl.get(hashId) || null;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import { serializeStatus } from "../entities/status.js";
|
||||
import { accountId, remoteActorId } from "../helpers/id-mapping.js";
|
||||
import { buildPaginationQuery, parseLimit, setPaginationHeaders } from "../helpers/pagination.js";
|
||||
import { resolveRemoteAccount } from "../helpers/resolve-account.js";
|
||||
import { getActorUrlFromId } from "../helpers/account-cache.js";
|
||||
|
||||
const router = express.Router(); // eslint-disable-line new-cap
|
||||
|
||||
@@ -714,6 +715,10 @@ async function resolveActorUrl(id, collections) {
|
||||
return profile.url;
|
||||
}
|
||||
|
||||
// Check account cache reverse lookup (populated by resolveRemoteAccount)
|
||||
const cachedUrl = getActorUrlFromId(id);
|
||||
if (cachedUrl) return cachedUrl;
|
||||
|
||||
// Check followers
|
||||
const followers = await collections.ap_followers.find({}).toArray();
|
||||
for (const f of followers) {
|
||||
|
||||
Reference in New Issue
Block a user