Files
indiekit-server/scripts/patch-endpoint-blogroll-feeds-alias.mjs
Sven 3979e12e8b fix: use express.Router() not require() in ESM patch
The blogroll index.js is an ES module so require() is not defined.
Replace `const { Router } = require("express")` with `express.Router()`
which is already in scope from the module's own top-level import.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 19:20:53 +01:00

135 lines
4.2 KiB
JavaScript

/**
* Patch: dual-mount blogroll public API at /blogrollapi AND /rssapi,
* and add a /api/feeds alias for /api/blogs.
*
* Problem: two static pages call different base paths:
* /blogroll → fetches /blogrollapi/api/blogs, /api/categories, /api/items, /api/status
* /news → fetches /rssapi/api/items, /rssapi/api/feeds, /rssapi/api/status
*
* Solution:
* 1. Keep mountPath "/blogrollapi" (serves the /blogroll page as-is).
* 2. In init(), register a thin second endpoint at "/rssapi" pointing to the
* same publicRouter so the /news page's fetches also resolve.
* 3. Add /api/feeds as an alias of /api/blogs on both routers.
*/
import { access, readFile, writeFile } from "node:fs/promises";
const patchSpecs = [
{
name: "blogroll-feeds-alias",
marker: "feeds alias for /api/blogs",
candidates: [
"node_modules/@rmdes/indiekit-endpoint-blogroll/index.js",
"node_modules/@indiekit/indiekit/node_modules/@rmdes/indiekit-endpoint-blogroll/index.js",
],
oldSnippet: ` publicRouter.get("/api/blogs", apiController.listBlogs);`,
newSnippet: ` publicRouter.get("/api/blogs", apiController.listBlogs);
// feeds alias for /api/blogs (used by the /news/ static page)
publicRouter.get("/api/feeds", apiController.listBlogs);`,
},
{
name: "blogroll-rssapi-dual-mount",
marker: "rssapi dual-mount alias",
candidates: [
"node_modules/@rmdes/indiekit-endpoint-blogroll/index.js",
"node_modules/@indiekit/indiekit/node_modules/@rmdes/indiekit-endpoint-blogroll/index.js",
],
oldSnippet: ` init(Indiekit) {
Indiekit.addEndpoint(this);`,
newSnippet: ` init(Indiekit) {
Indiekit.addEndpoint(this);
// rssapi dual-mount alias: register the same public routes at /rssapi
// with a response-shape transformer so the /news static page works.
// The /news page expects: item.link (not .url), item.feedId (not .blog.id),
// item.sourceUrl (not .blog.siteUrl), and feedsRes.feeds (not .items).
const rssapiRouter = express.Router();
rssapiRouter.use((req, res, next) => {
const originalJson = res.json.bind(res);
res.json = function (data) {
if (data && Array.isArray(data.items)) {
if (req.path.startsWith("/api/items")) {
// Map url->link, blog.id->feedId, blog.siteUrl->sourceUrl
data = {
...data,
items: data.items.map((item) => ({
...item,
link: item.url,
feedId: item.blog?.id ?? null,
sourceUrl: item.blog?.siteUrl ?? null,
})),
};
} else if (req.path === "/api/feeds") {
// Rename items->feeds so feedsRes.feeds resolves correctly
const { items, ...rest } = data;
data = { ...rest, feeds: items };
}
}
return originalJson(data);
};
next();
});
rssapiRouter.use(publicRouter);
Indiekit.addEndpoint({
name: "Blogroll /rssapi alias",
mountPath: "/rssapi",
get routesPublic() {
return rssapiRouter;
},
});`,
},
];
async function exists(filePath) {
try {
await access(filePath);
return true;
} catch {
return false;
}
}
let filesChecked = 0;
let filesPatched = 0;
for (const spec of patchSpecs) {
let foundAnyTarget = false;
for (const filePath of spec.candidates) {
if (!(await exists(filePath))) {
continue;
}
foundAnyTarget = true;
filesChecked += 1;
const source = await readFile(filePath, "utf8");
if (source.includes(spec.marker)) {
continue;
}
if (!source.includes(spec.oldSnippet)) {
continue;
}
const updated = source.replace(spec.oldSnippet, spec.newSnippet);
await writeFile(filePath, updated, "utf8");
filesPatched += 1;
}
if (!foundAnyTarget) {
console.log(`[postinstall] ${spec.name}: no target files found`);
}
}
if (filesChecked === 0) {
console.log("[postinstall] No blogroll endpoint files found");
} else if (filesPatched === 0) {
console.log("[postinstall] blogroll feeds alias + rssapi dual-mount already patched");
} else {
console.log(
`[postinstall] Patched blogroll feeds alias + rssapi dual-mount in ${filesPatched}/${filesChecked} file(s)`,
);
}