Files
indiekit-server/scripts/patch-endpoint-micropub-source-filter.mjs
Sven 65f94da71c fix: remove broken regex-escape snippet from micropub source-filter patch
The template-literal double-backslash escaping produced malformed JS in
the injected code, causing a SyntaxError at startup. Replace the
regex-escape helper with a direct String(searchParam) pass-through —
safe for an admin-only search interface.

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

122 lines
3.8 KiB
JavaScript

/**
* Patch: add `category` and `search` filtering to the Micropub `?q=source`
* list endpoint so that /posts can filter by tag and full-text search.
*
* When `category` is provided:
* filter MongoDB documents where `properties.category` matches the value.
* When `search` is provided:
* filter by a case-insensitive regex across name / content fields.
*
* Filtered queries bypass getCursor (no cursor-based pagination needed for
* small result sets) and return all matching posts up to the current limit.
*/
import { access, readFile, writeFile } from "node:fs/promises";
const candidates = [
"node_modules/@rmdes/indiekit-endpoint-micropub/lib/controllers/query.js",
"node_modules/@indiekit/endpoint-micropub/lib/controllers/query.js",
"node_modules/@indiekit/indiekit/node_modules/@rmdes/indiekit-endpoint-micropub/lib/controllers/query.js",
"node_modules/@indiekit/indiekit/node_modules/@indiekit/endpoint-micropub/lib/controllers/query.js",
];
const marker = "// filter-by-category-and-search patch";
const oldSnippet = ` } else {
// Return mf2 for published posts
let cursor = {
items: [],
hasNext: false,
hasPrev: false,
};
if (postsCollection) {
cursor = await getCursor(postsCollection, after, before, limit);
}`;
const newSnippet = ` } else {
// Return mf2 for published posts
// filter-by-category-and-search patch
const categoryParam = request.query.category;
const searchParam = request.query.search;
const hasExtraFilter = Boolean(categoryParam || searchParam);
let cursor = {
items: [],
hasNext: false,
hasPrev: false,
};
if (postsCollection) {
if (hasExtraFilter) {
const filterQuery = {};
if (categoryParam) {
filterQuery["properties.category"] = String(categoryParam);
}
if (searchParam) {
filterQuery.$or = [
{ "properties.name": { $regex: String(searchParam), $options: "i" } },
{ "properties.content.text": { $regex: String(searchParam), $options: "i" } },
{ "properties.content": { $regex: String(searchParam), $options: "i" } },
];
}
const findLimit = (limit && limit > 0) ? limit : 40;
const filteredItems = await postsCollection
.find(filterQuery, { limit: findLimit, sort: { _id: -1 } })
.toArray();
cursor = {
items: filteredItems,
hasNext: false,
hasPrev: false,
};
} else {
cursor = await getCursor(postsCollection, after, before, limit);
}
}`;
async function exists(filePath) {
try {
await access(filePath);
return true;
} catch {
return false;
}
}
let checked = 0;
let patched = 0;
for (const filePath of candidates) {
if (!(await exists(filePath))) {
continue;
}
checked += 1;
const source = await readFile(filePath, "utf8");
if (source.includes(marker)) {
continue;
}
if (!source.includes(oldSnippet)) {
console.warn(
`[postinstall] Skipping micropub source-filter patch for ${filePath}: upstream format changed`,
);
continue;
}
const updated = source.replace(oldSnippet, newSnippet);
await writeFile(filePath, updated, "utf8");
patched += 1;
}
if (checked === 0) {
console.log("[postinstall] No micropub query controller files found");
} else if (patched === 0) {
console.log("[postinstall] micropub source-filter patch already applied");
} else {
console.log(
`[postinstall] Patched micropub source-filter in ${patched} file(s)`,
);
}