fix: guard against uid:"" corruption in remote search resolve; add 404 logging
Deploy Indiekit Server / deploy (push) Successful in 1m22s
Deploy Indiekit Server / deploy (push) Successful in 1m22s
- patch-ap-mastodon-misc: ap-search-remote-uid-guard — throw before addTimelineItem if extractObjectData returns empty uid, preventing a corrupted uid:"" document from being returned as a search result and causing TypeError on subsequent like attempts - patch-ap-mastodon-statuses: ap-status-not-found-log — add console.warn to findTimelineItemById for both invalid-ObjectId and genuine-not-found cases, making 404 root causes diagnosable Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -258,6 +258,41 @@ const SEARCH_REMOTE_NEW = [
|
|||||||
` } // [patch] ap-search-url-resolve`,
|
` } // [patch] ap-search-url-resolve`,
|
||||||
].join("\n");
|
].join("\n");
|
||||||
|
|
||||||
|
// ── patch-ap-search-remote-uid-guard: skip AP objects with no uid ─────────────
|
||||||
|
// When extractObjectData fails to resolve the AP object's id (e.g. network
|
||||||
|
// error or malformed response), it returns uid:"". addTimelineItem({uid:""})
|
||||||
|
// then matches/creates a single corrupted document shared by ALL failed lookups.
|
||||||
|
// Any client that receives this garbage status will get a 500 when liking it
|
||||||
|
// (likePost calls new URL("") which throws). Guard: reject rData with empty uid.
|
||||||
|
|
||||||
|
const UID_GUARD_MARKER = "// [patch] ap-search-remote-uid-guard";
|
||||||
|
// Anchor must be the two consecutive remote-marker lines that exist only here.
|
||||||
|
const UID_GUARD_OLD =
|
||||||
|
` const _rData = await extractObjectData(_rObj, { documentLoader: _rDl }); ${SEARCH_REMOTE_MARKER}\n` +
|
||||||
|
` const _rStored = await addTimelineItem(collections, _rData); ${SEARCH_REMOTE_MARKER}`;
|
||||||
|
const UID_GUARD_NEW =
|
||||||
|
` const _rData = await extractObjectData(_rObj, { documentLoader: _rDl }); ${SEARCH_REMOTE_MARKER}\n` +
|
||||||
|
` if (!_rData?.uid) throw new Error("remote AP object has no uid"); ${UID_GUARD_MARKER}\n` +
|
||||||
|
` const _rStored = await addTimelineItem(collections, _rData); ${SEARCH_REMOTE_MARKER}`;
|
||||||
|
|
||||||
|
let uidGuardDone = false;
|
||||||
|
for (const f of SEARCH_CANDIDATES) {
|
||||||
|
if (!(await fileExists(f))) continue;
|
||||||
|
const src = await readFile(f, "utf8");
|
||||||
|
if (src.includes(UID_GUARD_MARKER)) {
|
||||||
|
console.log(`[postinstall] ${SCRIPT}: ap-search-remote-uid-guard already applied in ${f}`);
|
||||||
|
uidGuardDone = true; break;
|
||||||
|
}
|
||||||
|
if (!src.includes(UID_GUARD_OLD)) {
|
||||||
|
console.warn(`[postinstall] ${SCRIPT}: ap-search-remote-uid-guard — anchor not found in ${f}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
await writeFile(f, src.replace(UID_GUARD_OLD, UID_GUARD_NEW), "utf8");
|
||||||
|
console.log(`[postinstall] ${SCRIPT}: applied ap-search-remote-uid-guard to ${f}`);
|
||||||
|
total++; uidGuardDone = true; break;
|
||||||
|
}
|
||||||
|
if (!uidGuardDone) console.log(`[postinstall] ${SCRIPT}: ap-search-remote-uid-guard — no target file found`);
|
||||||
|
|
||||||
let searchRemoteDone = false;
|
let searchRemoteDone = false;
|
||||||
for (const f of SEARCH_CANDIDATES) {
|
for (const f of SEARCH_CANDIDATES) {
|
||||||
if (!(await fileExists(f))) continue;
|
if (!(await fileExists(f))) continue;
|
||||||
|
|||||||
@@ -206,6 +206,33 @@ const NEW_CTX_STATE = ` // Serialize all items
|
|||||||
}
|
}
|
||||||
const serializeOpts = { baseUrl, favouritedIds: ctxFavouritedIds, rebloggedIds: ctxRebloggedIds, bookmarkedIds: ctxBookmarkedIds, pinnedIds: new Set(), replyIdMap, replyAccountIdMap };`;
|
const serializeOpts = { baseUrl, favouritedIds: ctxFavouritedIds, rebloggedIds: ctxRebloggedIds, bookmarkedIds: ctxBookmarkedIds, pinnedIds: new Set(), replyIdMap, replyAccountIdMap };`;
|
||||||
|
|
||||||
|
// ── Patch 6: ap-status-not-found-log ────────────────────────────────────────
|
||||||
|
// Add warn logging to findTimelineItemById so 404s on /favourite etc. produce
|
||||||
|
// a log line showing the requested id and whether it was a bad ObjectId or a
|
||||||
|
// genuine missing document. Helps diagnose "Record not found" recurrences.
|
||||||
|
|
||||||
|
const MARKER_NOT_FOUND_LOG = "// [patch] ap-status-not-found-log";
|
||||||
|
|
||||||
|
const OLD_FIND_BY_ID = `async function findTimelineItemById(collection, id) {
|
||||||
|
try {
|
||||||
|
return await collection.findOne({ _id: new ObjectId(id) });
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}`;
|
||||||
|
|
||||||
|
const NEW_FIND_BY_ID = `async function findTimelineItemById(collection, id) { ${MARKER_NOT_FOUND_LOG}
|
||||||
|
try { ${MARKER_NOT_FOUND_LOG}
|
||||||
|
const _oid = new ObjectId(id); ${MARKER_NOT_FOUND_LOG}
|
||||||
|
const _doc = await collection.findOne({ _id: _oid }); ${MARKER_NOT_FOUND_LOG}
|
||||||
|
if (!_doc) console.warn(\`[Mastodon API] findTimelineItemById: no item for id=\${id}\`); ${MARKER_NOT_FOUND_LOG}
|
||||||
|
return _doc; ${MARKER_NOT_FOUND_LOG}
|
||||||
|
} catch (_fErr) { ${MARKER_NOT_FOUND_LOG}
|
||||||
|
console.warn(\`[Mastodon API] findTimelineItemById: invalid id=\${id}: \${_fErr.message}\`); ${MARKER_NOT_FOUND_LOG}
|
||||||
|
return null; ${MARKER_NOT_FOUND_LOG}
|
||||||
|
} ${MARKER_NOT_FOUND_LOG}
|
||||||
|
}`;
|
||||||
|
|
||||||
// ── Runner ───────────────────────────────────────────────────────────────────
|
// ── Runner ───────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
const FILES = apPath("lib/mastodon/routes/statuses.js");
|
const FILES = apPath("lib/mastodon/routes/statuses.js");
|
||||||
@@ -216,6 +243,7 @@ const SINGLE_PATCHES = [
|
|||||||
{ name: "delete-fix-C", marker: MARKER_DELETE_FIX, oldSnippet: OLD_AFTER_DELETE, newSnippet: NEW_AFTER_DELETE },
|
{ name: "delete-fix-C", marker: MARKER_DELETE_FIX, oldSnippet: OLD_AFTER_DELETE, newSnippet: NEW_AFTER_DELETE },
|
||||||
{ name: "status-reply-id-B", marker: MARKER_REPLY_ID, oldSnippet: OLD_REPLY_INSERT, newSnippet: NEW_REPLY_INSERT },
|
{ name: "status-reply-id-B", marker: MARKER_REPLY_ID, oldSnippet: OLD_REPLY_INSERT, newSnippet: NEW_REPLY_INSERT },
|
||||||
{ name: "interactions-context", marker: MARKER_CTX_STATE, oldSnippet: OLD_CTX_STATE, newSnippet: NEW_CTX_STATE },
|
{ name: "interactions-context", marker: MARKER_CTX_STATE, oldSnippet: OLD_CTX_STATE, newSnippet: NEW_CTX_STATE },
|
||||||
|
{ name: "not-found-log", marker: MARKER_NOT_FOUND_LOG, oldSnippet: OLD_FIND_BY_ID, newSnippet: NEW_FIND_BY_ID },
|
||||||
];
|
];
|
||||||
|
|
||||||
// Patches that need multiple replacements in one pass (applyMultiPatch)
|
// Patches that need multiple replacements in one pass (applyMultiPatch)
|
||||||
|
|||||||
Reference in New Issue
Block a user