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>
122 lines
3.8 KiB
JavaScript
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)`,
|
|
);
|
|
}
|