On fresh installs where the old wrong patch was never applied, Like was absent from the @fedify/fedify/vocab imports, causing a ReferenceError at startup. The dispatcher patch now adds Like to the import block if missing, making it self-contained and install-order independent. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
114 lines
3.7 KiB
JavaScript
114 lines
3.7 KiB
JavaScript
/**
|
|
* Patch: register a Fedify Like activity dispatcher in federation-setup.js.
|
|
*
|
|
* Per ActivityPub §3.1, objects with an `id` MUST be dereferenceable at that
|
|
* URI. The Like activities produced by jf2ToAS2Activity (after patch-ap-like-
|
|
* activity-id.mjs adds an id) need a corresponding Fedify object dispatcher so
|
|
* that fetching /activitypub/activities/like/{id} returns the Like activity.
|
|
*
|
|
* Fix:
|
|
* Add federation.setObjectDispatcher(Like, ...) after the Article dispatcher
|
|
* in setupObjectDispatchers(). The handler looks up the post, calls
|
|
* jf2ToAS2Activity, and returns the Like if that's what was produced.
|
|
*/
|
|
|
|
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 = "// ap-like-activity-dispatcher patch";
|
|
|
|
const OLD_SNIPPET = ` // Article dispatcher
|
|
federation.setObjectDispatcher(
|
|
Article,
|
|
\`\${mountPath}/objects/article/{+id}\`,
|
|
async (ctx, { id }) => {
|
|
const obj = await resolvePost(ctx, id);
|
|
return obj instanceof Article ? obj : null;
|
|
},
|
|
);
|
|
}`;
|
|
|
|
const NEW_SNIPPET = ` // Article dispatcher
|
|
federation.setObjectDispatcher(
|
|
Article,
|
|
\`\${mountPath}/objects/article/{+id}\`,
|
|
async (ctx, { id }) => {
|
|
const obj = await resolvePost(ctx, id);
|
|
return obj instanceof Article ? obj : null;
|
|
},
|
|
);
|
|
|
|
// Like activity dispatcher — makes AP-like activities dereferenceable (AP §3.1)
|
|
// ap-like-activity-dispatcher patch
|
|
federation.setObjectDispatcher(
|
|
Like,
|
|
\`\${mountPath}/activities/like/{+id}\`,
|
|
async (ctx, { id }) => {
|
|
if (!collections.posts || !publicationUrl) return null;
|
|
const postUrl = \`\${publicationUrl.replace(/\\/$/, "")}/\${id}\`;
|
|
const post = await collections.posts.findOne({
|
|
"properties.url": { $in: [postUrl, postUrl + "/"] },
|
|
});
|
|
if (!post) return null;
|
|
if (post?.properties?.["post-status"] === "draft") return null;
|
|
if (post?.properties?.visibility === "unlisted") return null;
|
|
if (post.properties?.deleted) return null;
|
|
const actorUrl = ctx.getActorUri(handle).href;
|
|
const activity = await jf2ToAS2Activity(post.properties, actorUrl, publicationUrl);
|
|
return activity instanceof Like ? activity : null;
|
|
},
|
|
);
|
|
}`;
|
|
|
|
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;
|
|
let source = await readFile(filePath, "utf8");
|
|
|
|
if (source.includes(MARKER)) {
|
|
continue; // already patched
|
|
}
|
|
|
|
if (!source.includes(OLD_SNIPPET)) {
|
|
console.log(`[postinstall] patch-ap-like-activity-dispatcher: snippet not found in ${filePath}`);
|
|
continue;
|
|
}
|
|
|
|
// Ensure Like is imported from @fedify/fedify/vocab (may be absent on fresh installs)
|
|
if (!source.includes(" Like,")) {
|
|
source = source.replace(" Note,", " Like,\n Note,");
|
|
}
|
|
|
|
source = source.replace(OLD_SNIPPET, NEW_SNIPPET);
|
|
await writeFile(filePath, source, "utf8");
|
|
patched += 1;
|
|
console.log(`[postinstall] Applied patch-ap-like-activity-dispatcher to ${filePath}`);
|
|
}
|
|
|
|
if (checked === 0) {
|
|
console.log("[postinstall] patch-ap-like-activity-dispatcher: no target files found");
|
|
} else if (patched === 0) {
|
|
console.log("[postinstall] patch-ap-like-activity-dispatcher: already up to date");
|
|
} else {
|
|
console.log(`[postinstall] patch-ap-like-activity-dispatcher: patched ${patched}/${checked} file(s)`);
|
|
}
|