feat: add search and tag filtering to /posts list
- patch-endpoint-micropub-source-filter: support ?category= and ?search= query params in the Micropub ?q=source endpoint, filtering MongoDB documents by properties.category and a case-insensitive regex across name/content fields - patch-endpoint-posts-search-tags: forward category/search params from the posts controller to Micropub, expose tagLinks on each item, and replace the posts.njk cardGrid with a custom loop that renders clickable tag chips and a search form above the grid Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+2
-2
@@ -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-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-microsub-feed-discovery.mjs && node scripts/patch-conversations-bluesky-self-filter.mjs && node scripts/patch-conversations-bluesky-cursor-fix.mjs && node scripts/patch-inbox-ignore-view-activity.mjs && node scripts/patch-inbox-skip-view-activity-parse.mjs && node scripts/patch-webmention-sender-content-scope.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-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-microsub-feed-discovery.mjs && node scripts/patch-conversations-bluesky-self-filter.mjs && node scripts/patch-conversations-bluesky-cursor-fix.mjs && node scripts/patch-inbox-ignore-view-activity.mjs && node scripts/patch-inbox-skip-view-activity-parse.mjs && node scripts/patch-webmention-sender-content-scope.mjs && node scripts/patch-endpoint-micropub-source-filter.mjs && node scripts/patch-endpoint-posts-search-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-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-microsub-feed-discovery.mjs && node scripts/patch-conversations-bluesky-self-filter.mjs && node scripts/patch-conversations-bluesky-cursor-fix.mjs && node scripts/patch-webmention-sender-content-scope.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-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-microsub-feed-discovery.mjs && node scripts/patch-conversations-bluesky-self-filter.mjs && node scripts/patch-conversations-bluesky-cursor-fix.mjs && node scripts/patch-webmention-sender-content-scope.mjs && node scripts/patch-endpoint-micropub-source-filter.mjs && node scripts/patch-endpoint-posts-search-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": [],
|
||||||
|
|||||||
@@ -0,0 +1,130 @@
|
|||||||
|
/**
|
||||||
|
* 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) {
|
||||||
|
const re = String(searchParam).replace(
|
||||||
|
/[$()*+.?[\\\]^{|}]/g,
|
||||||
|
"\\$&",
|
||||||
|
);
|
||||||
|
filterQuery.$or = [
|
||||||
|
{ "properties.name": { $regex: re, $options: "i" } },
|
||||||
|
{
|
||||||
|
"properties.content.text": {
|
||||||
|
$regex: re,
|
||||||
|
$options: "i",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ "properties.content": { $regex: re, $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)`,
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,249 @@
|
|||||||
|
/**
|
||||||
|
* Patch: add search input and tag chips to the /posts list page.
|
||||||
|
*
|
||||||
|
* Part A – posts.js controller:
|
||||||
|
* - forward `category` and `search` query params to the Micropub source query
|
||||||
|
* - expose `item.tagLinks` (pre-encoded href + text) for each post
|
||||||
|
* - pass `activeCategory` and `activeSearch` to the template
|
||||||
|
*
|
||||||
|
* Part B – posts.njk view:
|
||||||
|
* - replace the cardGrid call with a custom loop that appends clickable
|
||||||
|
* tag chips under each card
|
||||||
|
* - add a search form above the grid
|
||||||
|
*/
|
||||||
|
import { access, readFile, writeFile } from "node:fs/promises";
|
||||||
|
|
||||||
|
async function exists(filePath) {
|
||||||
|
try {
|
||||||
|
await access(filePath);
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Part A: posts controller ────────────────────────────────────────────────
|
||||||
|
|
||||||
|
const controllerCandidates = [
|
||||||
|
"node_modules/@rmdes/indiekit-endpoint-posts/lib/controllers/posts.js",
|
||||||
|
"node_modules/@indiekit/endpoint-posts/lib/controllers/posts.js",
|
||||||
|
"node_modules/@indiekit/indiekit/node_modules/@rmdes/indiekit-endpoint-posts/lib/controllers/posts.js",
|
||||||
|
"node_modules/@indiekit/indiekit/node_modules/@indiekit/endpoint-posts/lib/controllers/posts.js",
|
||||||
|
];
|
||||||
|
|
||||||
|
const controllerMarker = "// search-tags controller patch";
|
||||||
|
|
||||||
|
// 1) forward category + search to the micropub URL
|
||||||
|
const oldForward = ` const { after, before, success } = request.query;
|
||||||
|
const limit = Number(request.query.limit) || 12;
|
||||||
|
|
||||||
|
const micropubUrl = new URL(application.micropubEndpoint);
|
||||||
|
micropubUrl.searchParams.append("q", "source");
|
||||||
|
micropubUrl.searchParams.append("limit", String(limit));
|
||||||
|
|
||||||
|
if (after) {
|
||||||
|
micropubUrl.searchParams.append("after", String(after));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (before) {
|
||||||
|
micropubUrl.searchParams.append("before", String(before));
|
||||||
|
}`;
|
||||||
|
|
||||||
|
const newForward = ` // search-tags controller patch
|
||||||
|
const { after, before, category, search, success } = request.query;
|
||||||
|
const limit = Number(request.query.limit) || 12;
|
||||||
|
|
||||||
|
const micropubUrl = new URL(application.micropubEndpoint);
|
||||||
|
micropubUrl.searchParams.append("q", "source");
|
||||||
|
micropubUrl.searchParams.append("limit", String(limit));
|
||||||
|
|
||||||
|
if (after) {
|
||||||
|
micropubUrl.searchParams.append("after", String(after));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (before) {
|
||||||
|
micropubUrl.searchParams.append("before", String(before));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (category) {
|
||||||
|
micropubUrl.searchParams.append("category", String(category));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (search) {
|
||||||
|
micropubUrl.searchParams.append("search", String(search));
|
||||||
|
}`;
|
||||||
|
|
||||||
|
// 2) add tagLinks to each post item
|
||||||
|
const oldBadges = ` item.badges = getPostStatusBadges(item, response);
|
||||||
|
|
||||||
|
return item;`;
|
||||||
|
|
||||||
|
const newBadges = ` item.badges = getPostStatusBadges(item, response);
|
||||||
|
const rawTags = Array.isArray(item.category)
|
||||||
|
? item.category
|
||||||
|
: item.category
|
||||||
|
? [item.category]
|
||||||
|
: [];
|
||||||
|
item.tagLinks = rawTags.map((t) => ({
|
||||||
|
text: t,
|
||||||
|
href: \`?category=\${encodeURIComponent(t)}\`,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return item;`;
|
||||||
|
|
||||||
|
// 3) pass activeCategory + activeSearch to the render call
|
||||||
|
const oldRender = ` statusTypes,
|
||||||
|
success,
|
||||||
|
});`;
|
||||||
|
|
||||||
|
const newRender = ` statusTypes,
|
||||||
|
success,
|
||||||
|
activeCategory: category || "",
|
||||||
|
activeSearch: search || "",
|
||||||
|
});`;
|
||||||
|
|
||||||
|
// ─── Part B: posts view ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
const viewCandidates = [
|
||||||
|
"node_modules/@rmdes/indiekit-endpoint-posts/views/posts.njk",
|
||||||
|
"node_modules/@indiekit/endpoint-posts/views/posts.njk",
|
||||||
|
"node_modules/@indiekit/indiekit/node_modules/@rmdes/indiekit-endpoint-posts/views/posts.njk",
|
||||||
|
"node_modules/@indiekit/indiekit/node_modules/@indiekit/endpoint-posts/views/posts.njk",
|
||||||
|
];
|
||||||
|
|
||||||
|
const viewMarker = "{# search-tags view patch #}";
|
||||||
|
|
||||||
|
const oldView = `{% extends "document.njk" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{%- if posts.length > 0 %}
|
||||||
|
{{ cardGrid({
|
||||||
|
cardSize: "16rem",
|
||||||
|
items: posts
|
||||||
|
}) }}
|
||||||
|
{{ pagination(cursor) }}
|
||||||
|
{%- else -%}
|
||||||
|
{{ prose({ text: __("posts.posts.none") }) }}
|
||||||
|
{%- endif %}
|
||||||
|
{% endblock %}`;
|
||||||
|
|
||||||
|
const newView = `{% extends "document.njk" %}
|
||||||
|
{# search-tags view patch #}
|
||||||
|
{% block content %}
|
||||||
|
<form method="get" action="">
|
||||||
|
<div style="display:flex;gap:0.5rem;align-items:flex-end;flex-wrap:wrap;margin-bottom:1.5rem">
|
||||||
|
{{ input({
|
||||||
|
name: "search",
|
||||||
|
label: "Search",
|
||||||
|
value: activeSearch,
|
||||||
|
type: "search",
|
||||||
|
optional: true,
|
||||||
|
field: { classes: "-!-flex-grow" }
|
||||||
|
}) }}
|
||||||
|
{{ button({ text: "Search", type: "submit" }) }}
|
||||||
|
{%- if activeSearch or activeCategory %}
|
||||||
|
<a href="{{ parentUrl }}" class="button" style="align-self:flex-end">Clear</a>
|
||||||
|
{%- endif %}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{%- if activeCategory %}
|
||||||
|
<p style="margin-bottom:1rem">Filtered by tag: <strong>{{ activeCategory }}</strong></p>
|
||||||
|
{%- endif %}
|
||||||
|
{%- if posts.length > 0 %}
|
||||||
|
<ol class="card-grid" role="list" style="--min-card-size: 16rem">
|
||||||
|
{%- for item in posts %}
|
||||||
|
<li class="card-grid__item">
|
||||||
|
{{ card(item) | indent(4) }}
|
||||||
|
{%- if item.tagLinks and item.tagLinks.length > 0 %}
|
||||||
|
<dl class="prose--caption" style="padding:0 1rem 0.75rem">
|
||||||
|
<dt class="-!-visually-hidden">Tags</dt>
|
||||||
|
{%- for tagLink in item.tagLinks %}
|
||||||
|
<dd class="tag"><a href="{{ tagLink.href }}">{{ tagLink.text }}</a></dd>
|
||||||
|
{%- endfor %}
|
||||||
|
</dl>
|
||||||
|
{%- endif %}
|
||||||
|
</li>
|
||||||
|
{%- endfor %}
|
||||||
|
</ol>
|
||||||
|
{{ pagination(cursor) }}
|
||||||
|
{%- else -%}
|
||||||
|
{{ prose({ text: __("posts.posts.none") }) }}
|
||||||
|
{%- endif %}
|
||||||
|
{% endblock %}`;
|
||||||
|
|
||||||
|
// ─── Apply patches ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
let controllerChecked = 0;
|
||||||
|
let controllerPatched = 0;
|
||||||
|
|
||||||
|
for (const filePath of controllerCandidates) {
|
||||||
|
if (!(await exists(filePath))) continue;
|
||||||
|
controllerChecked += 1;
|
||||||
|
|
||||||
|
let source = await readFile(filePath, "utf8");
|
||||||
|
if (source.includes(controllerMarker)) continue;
|
||||||
|
|
||||||
|
let changed = false;
|
||||||
|
|
||||||
|
for (const [oldSnip, newSnip] of [
|
||||||
|
[oldForward, newForward],
|
||||||
|
[oldBadges, newBadges],
|
||||||
|
[oldRender, newRender],
|
||||||
|
]) {
|
||||||
|
if (!source.includes(oldSnip)) {
|
||||||
|
console.warn(
|
||||||
|
`[postinstall] posts search-tags: snippet not found in ${filePath}, skipping`,
|
||||||
|
);
|
||||||
|
changed = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
source = source.replace(oldSnip, newSnip);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
await writeFile(filePath, source, "utf8");
|
||||||
|
controllerPatched += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let viewChecked = 0;
|
||||||
|
let viewPatched = 0;
|
||||||
|
|
||||||
|
for (const filePath of viewCandidates) {
|
||||||
|
if (!(await exists(filePath))) continue;
|
||||||
|
viewChecked += 1;
|
||||||
|
|
||||||
|
const source = await readFile(filePath, "utf8");
|
||||||
|
if (source.includes(viewMarker)) continue;
|
||||||
|
|
||||||
|
if (!source.includes(oldView)) {
|
||||||
|
console.warn(
|
||||||
|
`[postinstall] posts search-tags: view not found in ${filePath}, skipping`,
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const updated = source.replace(oldView, newView);
|
||||||
|
await writeFile(filePath, updated, "utf8");
|
||||||
|
viewPatched += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (controllerChecked === 0 && viewChecked === 0) {
|
||||||
|
console.log("[postinstall] No endpoint-posts files found");
|
||||||
|
} else {
|
||||||
|
if (controllerPatched === 0 && controllerChecked > 0) {
|
||||||
|
console.log("[postinstall] posts search-tags controller already patched");
|
||||||
|
} else if (controllerPatched > 0) {
|
||||||
|
console.log(
|
||||||
|
`[postinstall] Patched posts search-tags controller in ${controllerPatched} file(s)`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (viewPatched === 0 && viewChecked > 0) {
|
||||||
|
console.log("[postinstall] posts search-tags view already patched");
|
||||||
|
} else if (viewPatched > 0) {
|
||||||
|
console.log(
|
||||||
|
`[postinstall] Patched posts search-tags view in ${viewPatched} file(s)`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user