Files
indiekit-server/scripts/patch-micropub-swarm-loc-guard.mjs
Sven ba760047ce
Deploy Indiekit Server / deploy (push) Successful in 1m29s
fix(swarm-loc-guard): correct content extraction and IndiekitError constructor call
- rawContent extraction now handles normalized {html,text} object (content is
  already normalized by the time the guard runs, so String({...}) was '[object Object]')
- IndiekitError constructor is (message, options) not (status, message) — was
  passing 422 as message causing error.message=422 (number) which crashed the
  Nunjucks error template with 'Input data should be a String'
2026-05-13 22:52:48 +02:00

71 lines
2.5 KiB
JavaScript

/**
* Patch: swarm-loc-guard
*
* Only accept OwnYourSwarm Micropub posts that contain the text "Loc"
* in their content. Check-ins without "Loc" are rejected with 422.
*
* Workflow: Sven adds "Loc" to Swarm check-ins he wants published.
* Check-ins without that text (stealth, accidental, no-content) are rejected.
*/
import { readFile, writeFile, access } from "node:fs/promises";
const MARKER = "// [patch] swarm-loc-guard";
const candidates = [
"node_modules/@indiekit/endpoint-micropub/lib/post-data.js",
"node_modules/@indiekit/indiekit/node_modules/@indiekit/endpoint-micropub/lib/post-data.js",
];
const oldSnippet = ` if (
type === "note" &&
(hasCheckinProperty || hasSwarmSyndication)
) {
properties.visibility = "unlisted";
}`;
const newSnippet = ` if (
type === "note" &&
(hasCheckinProperty || hasSwarmSyndication)
) {
// Guard: only accept OwnYourSwarm posts that contain "Loc" in content. ${MARKER}
// Extract raw text before normaliseProperties converts content to {html,text} object.
// After normalisation, String(properties.content) yields "[object Object]" — not useful.
// We read .text/.html from the normalised object, then fall back to raw string.
const rawContent = (() => {
const c = properties.content;
if (!c) return "";
if (typeof c === "string") return c;
if (typeof c === "object") return String(c.text || c.html || "");
return String(c);
})();
if (hasSwarmSyndication && !rawContent.includes("Loc")) {
throw new IndiekitError("OwnYourSwarm post without 'Loc' in content rejected", { status: 422, code: "invalid_request" });
}
properties.visibility = "unlisted";
}`;
async function exists(path) {
try { await access(path); return true; } catch { return false; }
}
let patched = 0;
for (const filePath of candidates) {
if (!(await exists(filePath))) continue;
const source = await readFile(filePath, "utf8");
if (source.includes(MARKER)) {
console.log(`[postinstall] swarm-loc-guard: already patched (${filePath})`);
continue;
}
if (!source.includes(oldSnippet)) {
console.log(`[postinstall] swarm-loc-guard: target snippet not found in ${filePath} — skipping`);
continue;
}
await writeFile(filePath, source.replace(oldSnippet, newSnippet), "utf8");
console.log(`[postinstall] swarm-loc-guard: patched ${filePath}`);
patched++;
}
if (patched === 0) {
console.log("[postinstall] swarm-loc-guard: nothing patched");
}