6e63422c21
BREAKING: Status IDs are now _id.toString() instead of encodeCursor(published). This fixes the critical threading bug where multiple posts sharing the same published second produced identical IDs, causing findTimelineItemById to return the wrong document. Changes: - status.js: id = _id.toString() (unique, chronologically sortable) - notification.js: same - findTimelineItemById: ObjectId-only lookup (no cursor fallback) - pagination.js: _id-based cursor pagination ($lt/$gt on ObjectId) - resolve-reply-ids.js: returns _id.toString() for parent IDs - Removed all encodeCursor/decodeCursor usage from API layer ObjectIds have a 4-byte timestamp prefix so chronological sort via _id: -1 works correctly. Pagination cursors are now ObjectId hex strings in Link headers.
47 lines
1.6 KiB
JavaScript
47 lines
1.6 KiB
JavaScript
/**
|
|
* Batch-resolve inReplyTo URLs to ObjectId strings and account IDs.
|
|
*
|
|
* Looks up parent posts in ap_timeline by uid/url and returns two Maps:
|
|
* - replyIdMap: inReplyTo URL → parent _id.toString()
|
|
* - replyAccountIdMap: inReplyTo URL → parent author account ID
|
|
*
|
|
* @param {object} collection - ap_timeline MongoDB collection
|
|
* @param {Array<object>} items - Timeline items with optional inReplyTo
|
|
* @returns {Promise<{replyIdMap: Map<string, string>, replyAccountIdMap: Map<string, string>}>}
|
|
*/
|
|
import { remoteActorId } from "./id-mapping.js";
|
|
|
|
export async function resolveReplyIds(collection, items) {
|
|
const replyIdMap = new Map();
|
|
const replyAccountIdMap = new Map();
|
|
if (!collection || !items?.length) return { replyIdMap, replyAccountIdMap };
|
|
|
|
const urls = [
|
|
...new Set(
|
|
items.map((item) => item.inReplyTo).filter(Boolean),
|
|
),
|
|
];
|
|
if (urls.length === 0) return { replyIdMap, replyAccountIdMap };
|
|
|
|
const parents = await collection
|
|
.find({ $or: [{ uid: { $in: urls } }, { url: { $in: urls } }] })
|
|
.project({ uid: 1, url: 1, "author.url": 1 })
|
|
.toArray();
|
|
|
|
for (const parent of parents) {
|
|
const parentId = parent._id.toString();
|
|
const authorUrl = parent.author?.url;
|
|
const authorAccountId = authorUrl ? remoteActorId(authorUrl) : null;
|
|
|
|
const setMaps = (key) => {
|
|
replyIdMap.set(key, parentId);
|
|
if (authorAccountId) replyAccountIdMap.set(key, authorAccountId);
|
|
};
|
|
|
|
if (parent.uid) setMaps(parent.uid);
|
|
if (parent.url && parent.url !== parent.uid) setMaps(parent.url);
|
|
}
|
|
|
|
return { replyIdMap, replyAccountIdMap };
|
|
}
|