fix: Interlinking without proper Post Title
Deploy Indiekit Server / deploy (push) Successful in 1m21s
Deploy Indiekit Server / deploy (push) Successful in 1m21s
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* Patch: preserve liked/bookmarked/boosted items during timeline cleanup.
|
||||
*
|
||||
* Root cause:
|
||||
* cleanupTimeline() removes old remote posts from ap_timeline and also
|
||||
* deletes their ap_interactions entries. Any post the user has liked,
|
||||
* bookmarked, or boosted disappears from GET /api/v1/favourites and
|
||||
* GET /api/v1/bookmarks after the next daily cleanup run.
|
||||
*
|
||||
* Fix:
|
||||
* Before deleting, fetch the set of objectUrls that have ap_interactions
|
||||
* entries (likes, bookmarks, boosts). Filter those out of the deletion
|
||||
* candidate list so they are preserved in ap_timeline (and their
|
||||
* ap_interactions entries are never touched).
|
||||
*/
|
||||
|
||||
import { access, readFile, writeFile } from "node:fs/promises";
|
||||
|
||||
const MARKER = "// [patch] ap-interactions-cleanup-preserve";
|
||||
|
||||
const candidates = [
|
||||
"node_modules/@rmdes/indiekit-endpoint-activitypub/lib/timeline-cleanup.js",
|
||||
"node_modules/@indiekit/indiekit/node_modules/@rmdes/indiekit-endpoint-activitypub/lib/timeline-cleanup.js",
|
||||
];
|
||||
|
||||
const OLD_SNIPPET = ` const removedUids = toDelete.map((item) => item.uid).filter(Boolean);
|
||||
|
||||
// Delete old timeline items by UID
|
||||
const deleteResult = await collections.ap_timeline.deleteMany({
|
||||
_id: { $in: toDelete.map((item) => item._id) },
|
||||
});
|
||||
|
||||
// Clean up stale interactions for removed items
|
||||
let interactionsRemoved = 0;
|
||||
if (removedUids.length > 0 && collections.ap_interactions) {
|
||||
const interactionResult = await collections.ap_interactions.deleteMany({
|
||||
objectUrl: { $in: removedUids },
|
||||
});
|
||||
interactionsRemoved = interactionResult.deletedCount || 0;
|
||||
}`;
|
||||
|
||||
const NEW_SNIPPET = ` const removedUids = toDelete.map((item) => item.uid).filter(Boolean);
|
||||
|
||||
// Preserve items the user has interacted with (liked, bookmarked, boosted). ${MARKER}
|
||||
// Deleting them would silently remove entries from the Favourites/Bookmarks pages.
|
||||
let interactedUids = new Set();
|
||||
if (removedUids.length > 0 && collections.ap_interactions) {
|
||||
const interacted = await collections.ap_interactions.distinct("objectUrl");
|
||||
interactedUids = new Set(interacted);
|
||||
}
|
||||
const itemsToDelete = toDelete.filter((item) => !interactedUids.has(item.uid));
|
||||
const uidsToDelete = itemsToDelete.map((item) => item.uid).filter(Boolean);
|
||||
|
||||
if (!itemsToDelete.length) {
|
||||
return { removed: 0, interactionsRemoved: 0 };
|
||||
}
|
||||
|
||||
// Delete old timeline items by UID
|
||||
const deleteResult = await collections.ap_timeline.deleteMany({
|
||||
_id: { $in: itemsToDelete.map((item) => item._id) },
|
||||
});
|
||||
|
||||
// Clean up stale interactions for removed items
|
||||
let interactionsRemoved = 0;
|
||||
if (uidsToDelete.length > 0 && collections.ap_interactions) {
|
||||
const interactionResult = await collections.ap_interactions.deleteMany({
|
||||
objectUrl: { $in: uidsToDelete },
|
||||
});
|
||||
interactionsRemoved = interactionResult.deletedCount || 0;
|
||||
}`;
|
||||
|
||||
async function exists(filePath) {
|
||||
try {
|
||||
await access(filePath);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
let checked = 0;
|
||||
let patched = 0;
|
||||
|
||||
for (const filePath of candidates) {
|
||||
if (!(await exists(filePath))) continue;
|
||||
checked += 1;
|
||||
|
||||
const source = await readFile(filePath, "utf8");
|
||||
|
||||
if (source.includes(MARKER)) {
|
||||
console.log(`[postinstall] patch-ap-interactions-cleanup-preserve: already applied to ${filePath}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!source.includes(OLD_SNIPPET)) {
|
||||
console.warn(`[postinstall] patch-ap-interactions-cleanup-preserve: snippet not found in ${filePath}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const updated = source.replace(OLD_SNIPPET, NEW_SNIPPET);
|
||||
await writeFile(filePath, updated, "utf8");
|
||||
patched += 1;
|
||||
console.log(`[postinstall] Applied patch-ap-interactions-cleanup-preserve to ${filePath}`);
|
||||
}
|
||||
|
||||
if (checked === 0) {
|
||||
console.log("[postinstall] patch-ap-interactions-cleanup-preserve: no target files found");
|
||||
} else if (patched === 0) {
|
||||
console.log("[postinstall] patch-ap-interactions-cleanup-preserve: already up to date");
|
||||
} else {
|
||||
console.log(`[postinstall] patch-ap-interactions-cleanup-preserve: patched ${patched} file(s)`);
|
||||
}
|
||||
Reference in New Issue
Block a user