fix(webmention): livefetch v6 with diagnostic log + reset-stale v11
livefetch v6: - Adds console.log showing which property links were built per post (e.g. "in-reply-to" for replies) — makes it debuggable without server access - Fixes retryPatchedBlock to include the two comment lines the retry patch actually inserts (was missing them, causing "Target block not found" on fresh upstream → retry → livefetch path) - Adds v5 to priorMarkersNoContinue with contentToProcess-line end detection so v5 → v6 in-place upgrade works correctly reset-stale: bump to v11 to retry ca3d8 and any other posts stuck before v5/v6 deployment. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -22,7 +22,7 @@ import { access, readFile, writeFile } from "node:fs/promises";
|
|||||||
const filePath =
|
const filePath =
|
||||||
"node_modules/@rmdes/indiekit-endpoint-webmention-sender/lib/controllers/webmention-sender.js";
|
"node_modules/@rmdes/indiekit-endpoint-webmention-sender/lib/controllers/webmention-sender.js";
|
||||||
|
|
||||||
const patchMarker = "// [patched:livefetch:v5]";
|
const patchMarker = "// [patched:livefetch:v6]";
|
||||||
|
|
||||||
// Original upstream code
|
// Original upstream code
|
||||||
const originalBlock = ` // If no content, try fetching the published page
|
const originalBlock = ` // If no content, try fetching the published page
|
||||||
@@ -64,6 +64,8 @@ const retryPatchedBlock = ` // If no content, try fetching the published
|
|||||||
|
|
||||||
if (!contentToProcess) {
|
if (!contentToProcess) {
|
||||||
if (fetchFailed) {
|
if (fetchFailed) {
|
||||||
|
// Page not yet available — skip and retry on next poll rather than
|
||||||
|
// permanently marking this post as sent with zero webmentions.
|
||||||
console.log(\`[webmention] Page not yet available for \${postUrl}, will retry next poll\`);
|
console.log(\`[webmention] Page not yet available for \${postUrl}, will retry next poll\`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -72,7 +74,7 @@ const retryPatchedBlock = ` // If no content, try fetching the published
|
|||||||
continue;
|
continue;
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
const newBlock = ` // [patched:livefetch:v5] Build synthetic h-entry HTML from stored post properties.
|
const newBlock = ` // [patched:livefetch:v6] Build synthetic h-entry HTML from stored post properties.
|
||||||
// The stored properties already contain all microformat target URLs
|
// The stored properties already contain all microformat target URLs
|
||||||
// (in-reply-to, like-of, bookmark-of, repost-of) and content.html has inline
|
// (in-reply-to, like-of, bookmark-of, repost-of) and content.html has inline
|
||||||
// links — no live page fetch needed, and no exposure to internal DNS issues.
|
// links — no live page fetch needed, and no exposure to internal DNS issues.
|
||||||
@@ -95,7 +97,8 @@ const newBlock = ` // [patched:livefetch:v5] Build synthetic h-entry HTML
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const _bodyHtml = post.properties.content?.html || post.properties.content?.value || "";
|
const _bodyHtml = post.properties.content?.html || post.properties.content?.value || "";
|
||||||
const contentToProcess = \`<div class="h-entry">\${_anchors.join("")}\${_bodyHtml ? \`<div class="e-content">\${_bodyHtml}</div>\` : ""}</div>\`;`;
|
const contentToProcess = \`<div class="h-entry">\${_anchors.join("")}\${_bodyHtml ? \`<div class="e-content">\${_bodyHtml}</div>\` : ""}</div>\`;
|
||||||
|
console.log(\`[webmention] Built synthetic h-entry for \${postUrl}: \${_anchors.length} prop link(s) [\${Object.entries(_propLinks).filter(([p]) => post.properties[p]).map(([p]) => p).join(", ") || "none"}]\`);`;
|
||||||
|
|
||||||
async function exists(p) {
|
async function exists(p) {
|
||||||
try {
|
try {
|
||||||
@@ -114,21 +117,26 @@ if (!(await exists(filePath))) {
|
|||||||
const source = await readFile(filePath, "utf8");
|
const source = await readFile(filePath, "utf8");
|
||||||
|
|
||||||
if (source.includes(patchMarker)) {
|
if (source.includes(patchMarker)) {
|
||||||
console.log("[patch-webmention-sender-livefetch] Already patched (v5)");
|
console.log("[patch-webmention-sender-livefetch] Already patched (v6)");
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// For v1–v4: extract the old patched block by finding the marker and the
|
// Extract the old patched block by finding the marker and the end of the block.
|
||||||
// closing "continue;\n }" that ends the if (!contentToProcess) block.
|
// v1–v4 end with "continue;\n }" (the if (!contentToProcess) block).
|
||||||
const priorMarkers = [
|
// v5+ end with the contentToProcess assignment line (no continue block).
|
||||||
|
const priorMarkersWithContinue = [
|
||||||
"// [patched:livefetch:v4]",
|
"// [patched:livefetch:v4]",
|
||||||
"// [patched:livefetch:v3]",
|
"// [patched:livefetch:v3]",
|
||||||
"// [patched:livefetch:v2]",
|
"// [patched:livefetch:v2]",
|
||||||
"// [patched:livefetch]",
|
"// [patched:livefetch]",
|
||||||
];
|
];
|
||||||
|
const priorMarkersNoContinue = [
|
||||||
|
"// [patched:livefetch:v5]",
|
||||||
|
];
|
||||||
|
|
||||||
let oldPatchBlock = null;
|
let oldPatchBlock = null;
|
||||||
for (const marker of priorMarkers) {
|
|
||||||
|
for (const marker of priorMarkersWithContinue) {
|
||||||
if (!source.includes(marker)) continue;
|
if (!source.includes(marker)) continue;
|
||||||
const startIdx = source.lastIndexOf(` ${marker}`);
|
const startIdx = source.lastIndexOf(` ${marker}`);
|
||||||
const endMarker = " continue;\n }";
|
const endMarker = " continue;\n }";
|
||||||
@@ -139,6 +147,21 @@ for (const marker of priorMarkers) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!oldPatchBlock) {
|
||||||
|
for (const marker of priorMarkersNoContinue) {
|
||||||
|
if (!source.includes(marker)) continue;
|
||||||
|
const startIdx = source.lastIndexOf(` ${marker}`);
|
||||||
|
// v5 block ends with the contentToProcess = `...`; line
|
||||||
|
// Find the semicolon that closes the last template literal on that line
|
||||||
|
const endMarker = '""}</div>`;\n';
|
||||||
|
const endSearch = source.indexOf(endMarker, startIdx);
|
||||||
|
if (startIdx !== -1 && endSearch !== -1) {
|
||||||
|
oldPatchBlock = source.slice(startIdx, endSearch + endMarker.length);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const targetBlock = oldPatchBlock
|
const targetBlock = oldPatchBlock
|
||||||
? oldPatchBlock
|
? oldPatchBlock
|
||||||
: source.includes(originalBlock)
|
: source.includes(originalBlock)
|
||||||
@@ -162,4 +185,4 @@ if (!patched.includes(patchMarker)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await writeFile(filePath, patched, "utf8");
|
await writeFile(filePath, patched, "utf8");
|
||||||
console.log("[patch-webmention-sender-livefetch] Patched successfully (v5)");
|
console.log("[patch-webmention-sender-livefetch] Patched successfully (v6)");
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
import { MongoClient } from "mongodb";
|
import { MongoClient } from "mongodb";
|
||||||
import config from "../indiekit.config.mjs";
|
import config from "../indiekit.config.mjs";
|
||||||
|
|
||||||
const MIGRATION_ID = "webmention-sender-reset-stale-v10";
|
const MIGRATION_ID = "webmention-sender-reset-stale-v11";
|
||||||
|
|
||||||
const mongodbUrl = config.application?.mongodbUrl;
|
const mongodbUrl = config.application?.mongodbUrl;
|
||||||
if (!mongodbUrl) {
|
if (!mongodbUrl) {
|
||||||
|
|||||||
Reference in New Issue
Block a user