Files
indiekit-server/scripts/patch-ap-object-url-trailing-slash.mjs
Sven 02919b6e37 fix: patch resolvePost to match post URLs with or without trailing slash
The Fedify object dispatcher constructs the post lookup URL from the
{+id} path variable (e.g. "replies/bd78a"), which has no trailing slash.
Posts in MongoDB store their URL with a trailing slash, so the exact
findOne() match was silently returning null → Fedify serving 404 →
mountains.social showing "Could not connect to the given address".

Fix uses $in to try both variants so the dispatcher works regardless
of whether the request URL has a trailing slash or not.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 15:17:27 +01:00

82 lines
2.6 KiB
JavaScript

/**
* Patch: make the Fedify object dispatcher's post lookup tolerate trailing-slash
* differences between the AP object URL and the stored post URL.
*
* Root cause:
* setupObjectDispatchers resolvePost() builds postUrl from the {+id} template
* variable (e.g. "replies/bd78a") and does an exact findOne() match against
* posts.properties.url. Posts in MongoDB are stored with a trailing slash
* ("https://blog.giersig.eu/replies/bd78a/"), but the AP object URL returned
* by the /api/ap-url lookup endpoint has no trailing slash. The exact match
* fails → Fedify returns 404 → remote instance shows "Could not connect".
*
* Fix:
* Replace the single-value findOne() with a $in query that tries both the
* bare URL and the URL with a trailing slash appended.
*/
import { access, readFile, writeFile } from "node:fs/promises";
const candidates = [
"node_modules/@rmdes/indiekit-endpoint-activitypub/lib/federation-setup.js",
"node_modules/@indiekit/indiekit/node_modules/@rmdes/indiekit-endpoint-activitypub/lib/federation-setup.js",
];
const MARKER = "// trailing-slash url fix";
const OLD_SNIPPET = ` const postUrl = \`\${publicationUrl.replace(/\\/$/, "")}/\${id}\`;
const post = await collections.posts.findOne({ "properties.url": postUrl });`;
const NEW_SNIPPET = ` const postUrl = \`\${publicationUrl.replace(/\\/$/, "")}/\${id}\`; // trailing-slash url fix
const post = await collections.posts.findOne({
"properties.url": { $in: [postUrl, postUrl + "/"] },
});`;
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)) {
continue;
}
if (!source.includes(OLD_SNIPPET)) {
console.log(`[postinstall] patch-ap-object-url-trailing-slash: snippet not found in ${filePath}`);
continue;
}
const updated = source.replace(OLD_SNIPPET, NEW_SNIPPET);
if (updated === source) {
continue;
}
await writeFile(filePath, updated, "utf8");
patched += 1;
console.log(`[postinstall] Applied patch-ap-object-url-trailing-slash to ${filePath}`);
}
if (checked === 0) {
console.log("[postinstall] patch-ap-object-url-trailing-slash: no target files found");
} else if (patched === 0) {
console.log("[postinstall] patch-ap-object-url-trailing-slash: already up to date");
} else {
console.log(`[postinstall] patch-ap-object-url-trailing-slash: patched ${patched}/${checked} file(s)`);
}