feat: propagate Micropub deletes to ActivityPub and Bluesky
All checks were successful
Deploy Indiekit Server / deploy (push) Successful in 1m13s
All checks were successful
Deploy Indiekit Server / deploy (push) Successful in 1m13s
When a post is deleted from the web backend (Micropub action=delete), call each registered syndicator's delete() method so the post is also removed from the Fediverse (AP Delete/Tombstone) and Bluesky (com.atproto.repo.deleteRecord). - patch-bluesky-syndicator-delete: adds Bluesky#deletePost(bskyUrl) to lib/bluesky.js and BlueskySyndicator#delete(url, syndication) to index.js; the bsky.app URL is resolved from the syndication array that postData.delete() preserves in _deletedProperties - patch-micropub-delete-propagation: patches action.js case "delete" to iterate publication.syndicationTargets after postContent.delete() and fire syndicator.delete() fire-and-forget for any syndicator that exposes the method (errors logged, never break the 200 response) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
85
scripts/patch-micropub-delete-propagation.mjs
Normal file
85
scripts/patch-micropub-delete-propagation.mjs
Normal file
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* Patch: propagate Micropub deletes to registered syndicators.
|
||||
*
|
||||
* Bug:
|
||||
* action.js case "delete" only calls postData.delete() + postContent.delete().
|
||||
* It never notifies syndicators, so deleting a post from the web backend
|
||||
* leaves orphaned copies on ActivityPub (Fediverse) and Bluesky.
|
||||
*
|
||||
* Fix:
|
||||
* After postContent.delete() returns, iterate publication.syndicationTargets
|
||||
* and call syndicator.delete(url, syndication) on any syndicator that exposes
|
||||
* the method. The syndication URLs are read from data._deletedProperties
|
||||
* (preserved by postData.delete() before stripping the properties).
|
||||
*
|
||||
* Errors from individual syndicators are caught and logged — a failed remote
|
||||
* delete must never break the 200 response for the local delete.
|
||||
*/
|
||||
|
||||
import { access, readFile, writeFile } from "node:fs/promises";
|
||||
|
||||
const MARKER = "// [patch] micropub-delete-propagation";
|
||||
|
||||
const candidates = [
|
||||
"node_modules/@indiekit/endpoint-micropub/lib/controllers/action.js",
|
||||
];
|
||||
|
||||
const OLD_DELETE_CASE = ` case "delete": {
|
||||
data = await postData.delete(application, publication, url);
|
||||
content = await postContent.delete(publication, data);
|
||||
break;
|
||||
}`;
|
||||
|
||||
const NEW_DELETE_CASE = ` case "delete": { ${MARKER}
|
||||
data = await postData.delete(application, publication, url);
|
||||
content = await postContent.delete(publication, data);
|
||||
// Propagate delete to syndicators (AP, Bluesky, …) ${MARKER}
|
||||
const _deletedSyndication = data?._deletedProperties?.syndication
|
||||
? [data._deletedProperties.syndication].flat()
|
||||
: [];
|
||||
if (_deletedSyndication.length > 0 && publication.syndicationTargets?.length > 0) {
|
||||
for (const _syndicator of publication.syndicationTargets) {
|
||||
if (typeof _syndicator.delete === "function") {
|
||||
_syndicator.delete(url, _deletedSyndication).catch((err) =>
|
||||
console.warn(\`[Micropub] Syndicator delete failed (\${_syndicator.name}): \${err.message}\`),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}`;
|
||||
|
||||
async function exists(p) {
|
||||
try { await access(p); return true; } catch { return false; }
|
||||
}
|
||||
|
||||
let checked = 0;
|
||||
let patched = 0;
|
||||
|
||||
for (const filePath of candidates) {
|
||||
if (!(await exists(filePath))) continue;
|
||||
checked++;
|
||||
|
||||
const source = await readFile(filePath, "utf8");
|
||||
if (source.includes(MARKER)) {
|
||||
console.log(`[postinstall] patch-micropub-delete-propagation: already applied to ${filePath}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!source.includes(OLD_DELETE_CASE)) {
|
||||
console.warn(`[postinstall] patch-micropub-delete-propagation: snippet not found in ${filePath}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
await writeFile(filePath, source.replace(OLD_DELETE_CASE, NEW_DELETE_CASE), "utf8");
|
||||
patched++;
|
||||
console.log(`[postinstall] Applied patch-micropub-delete-propagation to ${filePath}`);
|
||||
}
|
||||
|
||||
if (checked === 0) {
|
||||
console.log("[postinstall] patch-micropub-delete-propagation: no target files found");
|
||||
} else if (patched === 0) {
|
||||
console.log("[postinstall] patch-micropub-delete-propagation: already up to date");
|
||||
} else {
|
||||
console.log(`[postinstall] patch-micropub-delete-propagation: patched ${patched} file(s)`);
|
||||
}
|
||||
Reference in New Issue
Block a user