fix(patches): rewrite micropub self-fetch to localhost for jailed setup
Node can't reach its own public HTTPS URL (ECONNREFUSED 127.0.0.1:443) because port 443 only exists on the nginx jail. Rewrite self-referential fetch URLs to http://localhost:3000 in endpoint-posts, endpoint-syndicate, and endpoint-share. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,8 +4,8 @@
|
|||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"postinstall": "xattr -w com.apple.fileprovider.ignore#P 1 node_modules 2>/dev/null || true && node scripts/patch-lightningcss.mjs && node scripts/patch-endpoint-media-scope.mjs && node scripts/patch-endpoint-media-sharp-runtime.mjs && node scripts/patch-frontend-sharp-runtime.mjs && node scripts/patch-endpoint-files-upload-route.mjs && node scripts/patch-endpoint-files-upload-locales.mjs && node scripts/patch-endpoint-activitypub-locales.mjs && node scripts/patch-endpoint-homepage-locales.mjs && node scripts/patch-endpoint-homepage-identity-defaults.mjs && node scripts/patch-federation-unlisted-guards.mjs && node scripts/patch-endpoint-micropub-where-note-visibility.mjs && node scripts/patch-endpoint-posts-ai-fields.mjs && node scripts/patch-endpoint-posts-ai-cleanup.mjs && node scripts/patch-endpoint-podroll-opml-upload.mjs && node scripts/patch-preset-eleventy-ai-frontmatter.mjs && node scripts/patch-micropub-ai-block-resync.mjs && node scripts/patch-frontend-serviceworker-file.mjs && node scripts/patch-endpoint-comments-locales.mjs && node scripts/patch-endpoint-posts-locales.mjs && node scripts/patch-endpoint-conversations-locales.mjs && node scripts/patch-conversations-collection-guards.mjs && node scripts/patch-indiekit-routes-rate-limits.mjs && node scripts/patch-indiekit-error-production-stack.mjs && node scripts/patch-indieauth-devmode-guard.mjs && node scripts/patch-listening-endpoint-runtime-guards.mjs && node scripts/patch-endpoint-github-changelog-categories.mjs && node scripts/patch-endpoint-blogroll-feeds-alias.mjs && node scripts/patch-endpoint-posts-uid-lookup.mjs && node scripts/patch-endpoint-posts-prefill-url.mjs && node scripts/patch-conversations-bluesky-self-filter.mjs && node scripts/patch-conversations-bluesky-cursor-fix.mjs && node scripts/patch-endpoint-micropub-source-filter.mjs && node scripts/patch-endpoint-posts-search-tags.mjs && node scripts/patch-syndicate-force-checked-default.mjs && node scripts/patch-ap-url-lookup-api.mjs && node scripts/patch-ap-allow-private-address.mjs && node scripts/patch-inbox-skip-view-activity-parse.mjs && node scripts/patch-inbox-ignore-view-activity.mjs && node scripts/patch-endpoint-posts-fetch-diagnostic.mjs && node scripts/patch-ap-normalize-nested-tags.mjs",
|
"postinstall": "xattr -w com.apple.fileprovider.ignore#P 1 node_modules 2>/dev/null || true && node scripts/patch-lightningcss.mjs && node scripts/patch-endpoint-media-scope.mjs && node scripts/patch-endpoint-media-sharp-runtime.mjs && node scripts/patch-frontend-sharp-runtime.mjs && node scripts/patch-endpoint-files-upload-route.mjs && node scripts/patch-endpoint-files-upload-locales.mjs && node scripts/patch-endpoint-activitypub-locales.mjs && node scripts/patch-endpoint-homepage-locales.mjs && node scripts/patch-endpoint-homepage-identity-defaults.mjs && node scripts/patch-federation-unlisted-guards.mjs && node scripts/patch-endpoint-micropub-where-note-visibility.mjs && node scripts/patch-endpoint-posts-ai-fields.mjs && node scripts/patch-endpoint-posts-ai-cleanup.mjs && node scripts/patch-endpoint-podroll-opml-upload.mjs && node scripts/patch-preset-eleventy-ai-frontmatter.mjs && node scripts/patch-micropub-ai-block-resync.mjs && node scripts/patch-frontend-serviceworker-file.mjs && node scripts/patch-endpoint-comments-locales.mjs && node scripts/patch-endpoint-posts-locales.mjs && node scripts/patch-endpoint-conversations-locales.mjs && node scripts/patch-conversations-collection-guards.mjs && node scripts/patch-indiekit-routes-rate-limits.mjs && node scripts/patch-indiekit-error-production-stack.mjs && node scripts/patch-indieauth-devmode-guard.mjs && node scripts/patch-listening-endpoint-runtime-guards.mjs && node scripts/patch-endpoint-github-changelog-categories.mjs && node scripts/patch-endpoint-blogroll-feeds-alias.mjs && node scripts/patch-endpoint-posts-uid-lookup.mjs && node scripts/patch-endpoint-posts-prefill-url.mjs && node scripts/patch-conversations-bluesky-self-filter.mjs && node scripts/patch-conversations-bluesky-cursor-fix.mjs && node scripts/patch-endpoint-micropub-source-filter.mjs && node scripts/patch-endpoint-posts-search-tags.mjs && node scripts/patch-syndicate-force-checked-default.mjs && node scripts/patch-ap-url-lookup-api.mjs && node scripts/patch-ap-allow-private-address.mjs && node scripts/patch-inbox-skip-view-activity-parse.mjs && node scripts/patch-inbox-ignore-view-activity.mjs && node scripts/patch-endpoint-posts-fetch-diagnostic.mjs && node scripts/patch-micropub-fetch-internal-url.mjs && node scripts/patch-ap-normalize-nested-tags.mjs",
|
||||||
"serve":"export NODE_ENV=${NODE_ENV:-production} INDIEKIT_DEBUG=${INDIEKIT_DEBUG:-0} && node scripts/preflight-production-security.mjs && node scripts/preflight-mongo-connection.mjs && node scripts/preflight-activitypub-rsa-key.mjs && node scripts/preflight-activitypub-profile-urls.mjs && node scripts/patch-lightningcss.mjs && node scripts/patch-endpoint-media-scope.mjs && node scripts/patch-endpoint-media-sharp-runtime.mjs && node scripts/patch-frontend-sharp-runtime.mjs && node scripts/patch-endpoint-files-upload-route.mjs && node scripts/patch-endpoint-files-upload-locales.mjs && node scripts/patch-endpoint-activitypub-locales.mjs && node scripts/patch-endpoint-homepage-locales.mjs && node scripts/patch-endpoint-homepage-identity-defaults.mjs && node scripts/patch-federation-unlisted-guards.mjs && node scripts/patch-endpoint-micropub-where-note-visibility.mjs && node scripts/patch-endpoint-posts-ai-fields.mjs && node scripts/patch-endpoint-posts-ai-cleanup.mjs && node scripts/patch-endpoint-podroll-opml-upload.mjs && node scripts/patch-preset-eleventy-ai-frontmatter.mjs && node scripts/patch-micropub-ai-block-resync.mjs && node scripts/patch-frontend-serviceworker-file.mjs && node scripts/patch-endpoint-comments-locales.mjs && node scripts/patch-endpoint-posts-locales.mjs && node scripts/patch-endpoint-conversations-locales.mjs && node scripts/patch-conversations-collection-guards.mjs && node scripts/patch-indiekit-routes-rate-limits.mjs && node scripts/patch-indiekit-error-production-stack.mjs && node scripts/patch-indieauth-devmode-guard.mjs && node scripts/patch-listening-endpoint-runtime-guards.mjs && node scripts/patch-endpoint-github-changelog-categories.mjs && node scripts/patch-microsub-reader-ap-dispatch.mjs && node scripts/patch-endpoint-blogroll-feeds-alias.mjs && node scripts/patch-endpoint-posts-uid-lookup.mjs && node scripts/patch-endpoint-posts-prefill-url.mjs && node scripts/patch-conversations-bluesky-self-filter.mjs && node scripts/patch-conversations-bluesky-cursor-fix.mjs && node scripts/patch-endpoint-micropub-source-filter.mjs && node scripts/patch-endpoint-posts-search-tags.mjs && node scripts/patch-syndicate-force-checked-default.mjs && node scripts/patch-ap-url-lookup-api.mjs && node scripts/patch-ap-allow-private-address.mjs && node scripts/patch-inbox-skip-view-activity-parse.mjs && node scripts/patch-inbox-ignore-view-activity.mjs && node scripts/patch-endpoint-posts-fetch-diagnostic.mjs && node scripts/patch-ap-normalize-nested-tags.mjs && node node_modules/@indiekit/indiekit/bin/cli.js serve --config indiekit.config.mjs",
|
"serve":"export NODE_ENV=${NODE_ENV:-production} INDIEKIT_DEBUG=${INDIEKIT_DEBUG:-0} && node scripts/preflight-production-security.mjs && node scripts/preflight-mongo-connection.mjs && node scripts/preflight-activitypub-rsa-key.mjs && node scripts/preflight-activitypub-profile-urls.mjs && node scripts/patch-lightningcss.mjs && node scripts/patch-endpoint-media-scope.mjs && node scripts/patch-endpoint-media-sharp-runtime.mjs && node scripts/patch-frontend-sharp-runtime.mjs && node scripts/patch-endpoint-files-upload-route.mjs && node scripts/patch-endpoint-files-upload-locales.mjs && node scripts/patch-endpoint-activitypub-locales.mjs && node scripts/patch-endpoint-homepage-locales.mjs && node scripts/patch-endpoint-homepage-identity-defaults.mjs && node scripts/patch-federation-unlisted-guards.mjs && node scripts/patch-endpoint-micropub-where-note-visibility.mjs && node scripts/patch-endpoint-posts-ai-fields.mjs && node scripts/patch-endpoint-posts-ai-cleanup.mjs && node scripts/patch-endpoint-podroll-opml-upload.mjs && node scripts/patch-preset-eleventy-ai-frontmatter.mjs && node scripts/patch-micropub-ai-block-resync.mjs && node scripts/patch-frontend-serviceworker-file.mjs && node scripts/patch-endpoint-comments-locales.mjs && node scripts/patch-endpoint-posts-locales.mjs && node scripts/patch-endpoint-conversations-locales.mjs && node scripts/patch-conversations-collection-guards.mjs && node scripts/patch-indiekit-routes-rate-limits.mjs && node scripts/patch-indiekit-error-production-stack.mjs && node scripts/patch-indieauth-devmode-guard.mjs && node scripts/patch-listening-endpoint-runtime-guards.mjs && node scripts/patch-endpoint-github-changelog-categories.mjs && node scripts/patch-microsub-reader-ap-dispatch.mjs && node scripts/patch-endpoint-blogroll-feeds-alias.mjs && node scripts/patch-endpoint-posts-uid-lookup.mjs && node scripts/patch-endpoint-posts-prefill-url.mjs && node scripts/patch-conversations-bluesky-self-filter.mjs && node scripts/patch-conversations-bluesky-cursor-fix.mjs && node scripts/patch-endpoint-micropub-source-filter.mjs && node scripts/patch-endpoint-posts-search-tags.mjs && node scripts/patch-syndicate-force-checked-default.mjs && node scripts/patch-ap-url-lookup-api.mjs && node scripts/patch-ap-allow-private-address.mjs && node scripts/patch-inbox-skip-view-activity-parse.mjs && node scripts/patch-inbox-ignore-view-activity.mjs && node scripts/patch-endpoint-posts-fetch-diagnostic.mjs && node scripts/patch-micropub-fetch-internal-url.mjs && node scripts/patch-ap-normalize-nested-tags.mjs && node node_modules/@indiekit/indiekit/bin/cli.js serve --config indiekit.config.mjs",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
|
|||||||
@@ -1,8 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* Patch: rewrite self-referential fetch URLs to use localhost and add
|
||||||
|
* diagnostic logging for fetch failures.
|
||||||
|
*
|
||||||
|
* When behind a reverse proxy (e.g. nginx in a separate FreeBSD jail),
|
||||||
|
* the endpoint-posts form controller fetches the micropub endpoint via
|
||||||
|
* the public URL (https://...). But the Node process doesn't listen on
|
||||||
|
* 443 — only nginx does. This causes ECONNREFUSED on the Node jail.
|
||||||
|
*
|
||||||
|
* Fix: rewrite the URL to http://localhost:<PORT> before fetching, so
|
||||||
|
* the request stays inside the Node jail. The public URL is preserved
|
||||||
|
* for everything else (HTML link headers, external clients, etc.).
|
||||||
|
*
|
||||||
|
* Controlled by INTERNAL_FETCH_URL env var (e.g. "http://localhost:3000").
|
||||||
|
* Falls back to http://localhost:${PORT || 3000} automatically.
|
||||||
|
*/
|
||||||
|
|
||||||
import { access, readFile, writeFile } from "node:fs/promises";
|
import { access, readFile, writeFile } from "node:fs/promises";
|
||||||
|
|
||||||
const filePath = "node_modules/@indiekit/endpoint-posts/lib/endpoint.js";
|
const filePath = "node_modules/@indiekit/endpoint-posts/lib/endpoint.js";
|
||||||
|
|
||||||
const marker = "// [patch] fetch-diagnostic";
|
const marker = "// [patch] fetch-internal-rewrite";
|
||||||
|
|
||||||
async function exists(p) {
|
async function exists(p) {
|
||||||
try {
|
try {
|
||||||
@@ -14,28 +31,71 @@ async function exists(p) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!(await exists(filePath))) {
|
if (!(await exists(filePath))) {
|
||||||
console.log("[postinstall] endpoint-posts endpoint.js not found — skipping fetch-diagnostic patch");
|
console.log("[postinstall] endpoint-posts endpoint.js not found — skipping fetch-rewrite patch");
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
const source = await readFile(filePath, "utf8");
|
const source = await readFile(filePath, "utf8");
|
||||||
|
|
||||||
if (source.includes(marker)) {
|
if (source.includes(marker)) {
|
||||||
console.log("[postinstall] endpoint-posts fetch-diagnostic patch already applied");
|
console.log("[postinstall] endpoint-posts fetch-rewrite patch already applied");
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrap the fetch calls to log the underlying cause on failure
|
// Also handle the case where the old diagnostic-only patch was applied
|
||||||
const oldPost = ` async post(url, accessToken, jsonBody = false) {
|
const oldMarker = "// [patch] fetch-diagnostic";
|
||||||
const endpointResponse = await fetch(url, {`;
|
let cleanSource = source;
|
||||||
|
if (cleanSource.includes(oldMarker)) {
|
||||||
|
// Strip old patch — we'll re-apply from scratch on the original structure.
|
||||||
|
// Safest approach: bail and let the user re-run after npm install.
|
||||||
|
console.log("[postinstall] Old fetch-diagnostic patch detected — stripping before re-patching");
|
||||||
|
// We can't cleanly reverse the old patch, so we need to check if the
|
||||||
|
// original structure is still recognisable. If not, warn and skip.
|
||||||
|
}
|
||||||
|
|
||||||
const newPost = ` ${marker}
|
const original = `import { IndiekitError } from "@indiekit/error";
|
||||||
|
|
||||||
|
export const endpoint = {
|
||||||
|
/**
|
||||||
|
* Micropub query
|
||||||
|
* @param {string} url - URL
|
||||||
|
* @param {string} accessToken - Access token
|
||||||
|
* @returns {Promise<object>} Response data
|
||||||
|
*/
|
||||||
|
async get(url, accessToken) {
|
||||||
|
const endpointResponse = await fetch(url, {
|
||||||
|
headers: {
|
||||||
|
accept: "application/json",
|
||||||
|
authorization: \`Bearer \${accessToken}\`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!endpointResponse.ok) {
|
||||||
|
throw await IndiekitError.fromFetch(endpointResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
const body = await endpointResponse.json();
|
||||||
|
|
||||||
|
return body;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Micropub action
|
||||||
|
* @param {string} url - URL
|
||||||
|
* @param {string} accessToken - Access token
|
||||||
|
* @param {object} [jsonBody] - JSON body
|
||||||
|
* @returns {Promise<object>} Response data
|
||||||
|
*/
|
||||||
async post(url, accessToken, jsonBody = false) {
|
async post(url, accessToken, jsonBody = false) {
|
||||||
let endpointResponse;
|
const endpointResponse = await fetch(url, {
|
||||||
try {
|
method: "POST",
|
||||||
endpointResponse = await fetch(url, {`;
|
headers: {
|
||||||
|
accept: "application/json",
|
||||||
const oldPostEnd = ` });
|
authorization: \`Bearer \${accessToken}\`,
|
||||||
|
...(jsonBody && { "content-type": "application/json" }),
|
||||||
|
},
|
||||||
|
...(jsonBody && { body: JSON.stringify(jsonBody) }),
|
||||||
|
});
|
||||||
|
|
||||||
if (!endpointResponse.ok) {
|
if (!endpointResponse.ok) {
|
||||||
throw await IndiekitError.fromFetch(endpointResponse);
|
throw await IndiekitError.fromFetch(endpointResponse);
|
||||||
@@ -47,11 +107,78 @@ const oldPostEnd = ` });
|
|||||||
},
|
},
|
||||||
};`;
|
};`;
|
||||||
|
|
||||||
const newPostEnd = ` });
|
const patched = `import { IndiekitError } from "@indiekit/error";
|
||||||
|
|
||||||
|
${marker}
|
||||||
|
const _internalBase = (() => {
|
||||||
|
if (process.env.INTERNAL_FETCH_URL) return process.env.INTERNAL_FETCH_URL.replace(/\\/+$/, "");
|
||||||
|
const port = process.env.PORT || "3000";
|
||||||
|
return \`http://localhost:\${port}\`;
|
||||||
|
})();
|
||||||
|
const _publicBase = (
|
||||||
|
process.env.PUBLICATION_URL || process.env.SITE_URL || ""
|
||||||
|
).replace(/\\/+$/, "");
|
||||||
|
|
||||||
|
function _toInternalUrl(url) {
|
||||||
|
if (!_publicBase || !url.startsWith(_publicBase)) return url;
|
||||||
|
return _internalBase + url.slice(_publicBase.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const endpoint = {
|
||||||
|
/**
|
||||||
|
* Micropub query
|
||||||
|
* @param {string} url - URL
|
||||||
|
* @param {string} accessToken - Access token
|
||||||
|
* @returns {Promise<object>} Response data
|
||||||
|
*/
|
||||||
|
async get(url, accessToken) {
|
||||||
|
const fetchUrl = _toInternalUrl(url);
|
||||||
|
let endpointResponse;
|
||||||
|
try {
|
||||||
|
endpointResponse = await fetch(fetchUrl, {
|
||||||
|
headers: {
|
||||||
|
accept: "application/json",
|
||||||
|
authorization: \`Bearer \${accessToken}\`,
|
||||||
|
},
|
||||||
|
});
|
||||||
} catch (fetchError) {
|
} catch (fetchError) {
|
||||||
const cause = fetchError.cause || fetchError;
|
const cause = fetchError.cause || fetchError;
|
||||||
console.error("[endpoint-posts] fetch failed for POST %s — %s: %s", url, cause.code || cause.name, cause.message);
|
console.error("[endpoint-posts] fetch failed for GET %s (internal: %s) — %s: %s", url, fetchUrl, cause.code || cause.name, cause.message);
|
||||||
if (cause.cause) console.error("[endpoint-posts] nested cause: %s", cause.cause.message || cause.cause);
|
throw fetchError;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!endpointResponse.ok) {
|
||||||
|
throw await IndiekitError.fromFetch(endpointResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
const body = await endpointResponse.json();
|
||||||
|
|
||||||
|
return body;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Micropub action
|
||||||
|
* @param {string} url - URL
|
||||||
|
* @param {string} accessToken - Access token
|
||||||
|
* @param {object} [jsonBody] - JSON body
|
||||||
|
* @returns {Promise<object>} Response data
|
||||||
|
*/
|
||||||
|
async post(url, accessToken, jsonBody = false) {
|
||||||
|
const fetchUrl = _toInternalUrl(url);
|
||||||
|
let endpointResponse;
|
||||||
|
try {
|
||||||
|
endpointResponse = await fetch(fetchUrl, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
accept: "application/json",
|
||||||
|
authorization: \`Bearer \${accessToken}\`,
|
||||||
|
...(jsonBody && { "content-type": "application/json" }),
|
||||||
|
},
|
||||||
|
...(jsonBody && { body: JSON.stringify(jsonBody) }),
|
||||||
|
});
|
||||||
|
} catch (fetchError) {
|
||||||
|
const cause = fetchError.cause || fetchError;
|
||||||
|
console.error("[endpoint-posts] fetch failed for POST %s (internal: %s) — %s: %s", url, fetchUrl, cause.code || cause.name, cause.message);
|
||||||
throw fetchError;
|
throw fetchError;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,52 +192,20 @@ const newPostEnd = ` });
|
|||||||
},
|
},
|
||||||
};`;
|
};`;
|
||||||
|
|
||||||
const oldGet = ` async get(url, accessToken) {
|
// Try matching the original (unpatched) file first
|
||||||
const endpointResponse = await fetch(url, {`;
|
if (cleanSource.includes(original.trim())) {
|
||||||
|
const updated = cleanSource.replace(original.trim(), patched.trim());
|
||||||
const newGet = ` async get(url, accessToken) {
|
await writeFile(filePath, updated, "utf8");
|
||||||
let endpointResponse;
|
console.log("[postinstall] Patched endpoint-posts: fetch URL rewrite + diagnostic logging");
|
||||||
try {
|
|
||||||
endpointResponse = await fetch(url, {`;
|
|
||||||
|
|
||||||
const oldGetEnd = ` });
|
|
||||||
|
|
||||||
if (!endpointResponse.ok) {
|
|
||||||
throw await IndiekitError.fromFetch(endpointResponse);
|
|
||||||
}
|
|
||||||
|
|
||||||
const body = await endpointResponse.json();
|
|
||||||
|
|
||||||
return body;
|
|
||||||
},`;
|
|
||||||
|
|
||||||
const newGetEnd = ` });
|
|
||||||
} catch (fetchError) {
|
|
||||||
const cause = fetchError.cause || fetchError;
|
|
||||||
console.error("[endpoint-posts] fetch failed for GET %s — %s: %s", url, cause.code || cause.name, cause.message);
|
|
||||||
if (cause.cause) console.error("[endpoint-posts] nested cause: %s", cause.cause.message || cause.cause);
|
|
||||||
throw fetchError;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!endpointResponse.ok) {
|
|
||||||
throw await IndiekitError.fromFetch(endpointResponse);
|
|
||||||
}
|
|
||||||
|
|
||||||
const body = await endpointResponse.json();
|
|
||||||
|
|
||||||
return body;
|
|
||||||
},`;
|
|
||||||
|
|
||||||
let updated = source;
|
|
||||||
updated = updated.replace(oldPost, newPost);
|
|
||||||
updated = updated.replace(oldPostEnd, newPostEnd);
|
|
||||||
updated = updated.replace(oldGet, newGet);
|
|
||||||
updated = updated.replace(oldGetEnd, newGetEnd);
|
|
||||||
|
|
||||||
if (!updated.includes(marker)) {
|
|
||||||
console.warn("[postinstall] Skipping endpoint-posts fetch-diagnostic patch: upstream format changed");
|
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
await writeFile(filePath, updated, "utf8");
|
// If old diagnostic patch was applied, try matching that version
|
||||||
console.log("[postinstall] Patched endpoint-posts with fetch diagnostic logging");
|
if (cleanSource.includes(oldMarker)) {
|
||||||
|
// Overwrite the whole file with the new patched version
|
||||||
|
await writeFile(filePath, patched + "\n", "utf8");
|
||||||
|
console.log("[postinstall] Replaced old fetch-diagnostic patch with fetch-rewrite + diagnostic");
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.warn("[postinstall] Skipping endpoint-posts fetch-rewrite patch: upstream format changed");
|
||||||
|
|||||||
113
scripts/patch-micropub-fetch-internal-url.mjs
Normal file
113
scripts/patch-micropub-fetch-internal-url.mjs
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
/**
|
||||||
|
* Patch: rewrite micropub self-fetch URLs to localhost in endpoint-syndicate
|
||||||
|
* and endpoint-share.
|
||||||
|
*
|
||||||
|
* Same issue as endpoint-posts: behind a reverse proxy (nginx in a separate
|
||||||
|
* FreeBSD jail), Node can't reach its own public HTTPS URL because port 443
|
||||||
|
* only exists on the nginx jail.
|
||||||
|
*
|
||||||
|
* Rewrites fetch(application.micropubEndpoint, ...) to use
|
||||||
|
* http://localhost:<PORT> instead.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { access, readFile, writeFile } from "node:fs/promises";
|
||||||
|
|
||||||
|
const marker = "// [patch] micropub-fetch-internal-url";
|
||||||
|
|
||||||
|
const helperBlock = `${marker}
|
||||||
|
const _mpInternalBase = (() => {
|
||||||
|
if (process.env.INTERNAL_FETCH_URL) return process.env.INTERNAL_FETCH_URL.replace(/\\/+$/, "");
|
||||||
|
const port = process.env.PORT || "3000";
|
||||||
|
return \`http://localhost:\${port}\`;
|
||||||
|
})();
|
||||||
|
const _mpPublicBase = (
|
||||||
|
process.env.PUBLICATION_URL || process.env.SITE_URL || ""
|
||||||
|
).replace(/\\/+$/, "");
|
||||||
|
function _toInternalUrl(url) {
|
||||||
|
if (!_mpPublicBase || !url.startsWith(_mpPublicBase)) return url;
|
||||||
|
return _mpInternalBase + url.slice(_mpPublicBase.length);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const targets = [
|
||||||
|
{
|
||||||
|
paths: [
|
||||||
|
"node_modules/@indiekit/endpoint-syndicate/lib/controllers/syndicate.js",
|
||||||
|
],
|
||||||
|
oldSnippet: ` const micropubResponse = await fetch(application.micropubEndpoint, {`,
|
||||||
|
newSnippet: ` const micropubResponse = await fetch(_toInternalUrl(application.micropubEndpoint), {`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
paths: [
|
||||||
|
"node_modules/@indiekit/endpoint-share/lib/controllers/share.js",
|
||||||
|
],
|
||||||
|
oldSnippet: ` const micropubResponse = await fetch(application.micropubEndpoint, {`,
|
||||||
|
newSnippet: ` const micropubResponse = await fetch(_toInternalUrl(application.micropubEndpoint), {`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
async function exists(filePath) {
|
||||||
|
try {
|
||||||
|
await access(filePath);
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let totalPatched = 0;
|
||||||
|
|
||||||
|
for (const target of targets) {
|
||||||
|
for (const filePath of target.paths) {
|
||||||
|
if (!(await exists(filePath))) continue;
|
||||||
|
|
||||||
|
const source = await readFile(filePath, "utf8");
|
||||||
|
|
||||||
|
if (source.includes(marker)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!source.includes(target.oldSnippet)) {
|
||||||
|
console.warn(`[postinstall] micropub-fetch-internal-url: snippet not found in ${filePath} — skipping`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert helper block after the last import statement.
|
||||||
|
// Find the last "from" keyword followed by a string and semicolon,
|
||||||
|
// which marks the end of the last import.
|
||||||
|
const importEndPattern = /;\s*\n/g;
|
||||||
|
const allImportMatches = [...source.matchAll(/^import\s/gm)];
|
||||||
|
if (allImportMatches.length === 0) {
|
||||||
|
console.warn(`[postinstall] micropub-fetch-internal-url: no imports found in ${filePath} — skipping`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the semicolon+newline that ends the last import block
|
||||||
|
const lastImportStart = allImportMatches.at(-1).index;
|
||||||
|
const afterLastImport = source.slice(lastImportStart);
|
||||||
|
const fromMatch = afterLastImport.match(/from\s+["'][^"']+["']\s*;\s*\n/);
|
||||||
|
if (!fromMatch) {
|
||||||
|
console.warn(`[postinstall] micropub-fetch-internal-url: can't find end of last import in ${filePath} — skipping`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const insertAt = lastImportStart + fromMatch.index + fromMatch[0].length;
|
||||||
|
const beforeHelper = source.slice(0, insertAt);
|
||||||
|
const afterHelper = source.slice(insertAt);
|
||||||
|
|
||||||
|
let updated = beforeHelper + "\n" + helperBlock + "\n" + afterHelper;
|
||||||
|
|
||||||
|
// Now replace the fetch call
|
||||||
|
updated = updated.replace(target.oldSnippet, target.newSnippet);
|
||||||
|
|
||||||
|
await writeFile(filePath, updated, "utf8");
|
||||||
|
console.log(`[postinstall] Patched micropub-fetch-internal-url in ${filePath}`);
|
||||||
|
totalPatched++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totalPatched === 0) {
|
||||||
|
console.log("[postinstall] micropub-fetch-internal-url patches already applied or no targets found");
|
||||||
|
} else {
|
||||||
|
console.log(`[postinstall] micropub-fetch-internal-url: patched ${totalPatched} file(s)`);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user