mirror of
https://github.com/svemagie/indiekit-endpoint-microsub.git
synced 2026-04-02 15:35:00 +02:00
Prevents dateString.split crash when Nunjucks | date filter receives Date objects from MongoDB. Audit timestamps (createdAt, updatedAt, lastFetchedAt, etc.) now use .toISOString(). Query-used fields (published, nextFetchAt) kept as Date objects for MongoDB compatibility. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
120 lines
3.1 KiB
JavaScript
120 lines
3.1 KiB
JavaScript
/**
|
|
* Notify blogroll plugin of Microsub follow/unfollow events
|
|
* @module utils/blogroll-notify
|
|
*/
|
|
|
|
/**
|
|
* Notify blogroll of a feed subscription change
|
|
* Fire-and-forget — errors are logged but don't block the response
|
|
* @param {object} application - Application instance
|
|
* @param {string} action - "follow" or "unfollow"
|
|
* @param {object} data - Feed data
|
|
* @param {string} data.url - Feed URL
|
|
* @param {string} [data.title] - Feed title
|
|
* @param {string} [data.channelName] - Channel name
|
|
* @param {string} [data.feedId] - Microsub feed ID
|
|
* @param {string} [data.channelId] - Microsub channel ID
|
|
*/
|
|
export async function notifyBlogroll(application, action, data) {
|
|
// Check if blogroll plugin is installed
|
|
if (typeof application.getBlogrollDb !== "function") {
|
|
return;
|
|
}
|
|
|
|
const db = application.getBlogrollDb();
|
|
if (!db) {
|
|
return;
|
|
}
|
|
|
|
const collection = db.collection("blogrollBlogs");
|
|
const now = new Date();
|
|
|
|
if (action === "follow") {
|
|
// Skip if this feed was explicitly deleted by the user
|
|
const deleted = await collection.findOne({
|
|
feedUrl: data.url,
|
|
status: "deleted",
|
|
});
|
|
if (deleted) {
|
|
console.log(
|
|
`[Microsub→Blogroll] Skipping follow for ${data.url} — previously deleted by user`,
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Upsert the blog entry
|
|
await collection.updateOne(
|
|
{ feedUrl: data.url },
|
|
{
|
|
$set: {
|
|
title: data.title || extractDomain(data.url),
|
|
siteUrl: extractSiteUrl(data.url),
|
|
feedType: "rss",
|
|
category: data.channelName || "Microsub",
|
|
source: "microsub",
|
|
microsubFeedId: data.feedId || null,
|
|
microsubChannelId: data.channelId || null,
|
|
microsubChannelName: data.channelName || null,
|
|
skipItemFetch: true,
|
|
status: "active",
|
|
updatedAt: now,
|
|
},
|
|
$setOnInsert: {
|
|
description: null,
|
|
tags: [],
|
|
photo: null,
|
|
author: null,
|
|
lastFetchAt: null,
|
|
lastError: null,
|
|
itemCount: 0,
|
|
pinned: false,
|
|
hidden: false,
|
|
notes: null,
|
|
createdAt: now,
|
|
},
|
|
},
|
|
{ upsert: true },
|
|
);
|
|
|
|
console.log(`[Microsub→Blogroll] Added/updated feed ${data.url}`);
|
|
} else if (action === "unfollow") {
|
|
// Soft-delete the blog entry if it came from microsub
|
|
const result = await collection.updateOne(
|
|
{
|
|
feedUrl: data.url,
|
|
source: "microsub",
|
|
status: { $ne: "deleted" },
|
|
},
|
|
{
|
|
$set: {
|
|
status: "deleted",
|
|
hidden: true,
|
|
deletedAt: now,
|
|
updatedAt: now,
|
|
},
|
|
},
|
|
);
|
|
|
|
if (result.modifiedCount > 0) {
|
|
console.log(`[Microsub→Blogroll] Soft-deleted feed ${data.url}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
function extractDomain(url) {
|
|
try {
|
|
return new URL(url).hostname.replace(/^www\./, "");
|
|
} catch {
|
|
return url;
|
|
}
|
|
}
|
|
|
|
function extractSiteUrl(feedUrl) {
|
|
try {
|
|
const parsed = new URL(feedUrl);
|
|
return `${parsed.protocol}//${parsed.host}`;
|
|
} catch {
|
|
return "";
|
|
}
|
|
}
|