Files
indiekit-endpoint-blogroll/lib/bookmark-import.js
svemagie 381b0397a5 feat: guard bookmark hook when microsub is available, update category on tag change
- index: skip direct bookmark import when microsub plugin is present;
  microsub handles the flow and notifies blogroll via notifyBlogroll()
  to avoid duplicate entries
- bookmark-import: when blog already exists and category differs, update it
  instead of skipping (handles tag changes on existing bookmark posts)

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

91 lines
3.0 KiB
JavaScript

/**
* Bookmark → blogroll import
* Called when a micropub bookmark post is created.
* Discovers feeds for the bookmarked site's origin and adds them to the blogroll.
* @module lib/bookmark-import
*/
import { discoverFeeds } from "./utils/feed-discovery.js";
import { upsertBlog } from "./storage/blogs.js";
/**
* Import a bookmarked URL's site into the blogroll.
* Extracts the origin URL, discovers feeds, and upserts the first feed as a blog entry.
*
* @param {object} application - Indiekit application object
* @param {string} bookmarkUrl - The URL that was bookmarked (bookmark-of value)
* @param {string} [category="bookmarks"] - Category to assign in the blogroll
* @returns {Promise<object>} Result { added, alreadyExists, noFeeds, error }
*/
export async function importBookmarkUrl(application, bookmarkUrl, category = "bookmarks") {
// Normalise: bookmark-of may be an array in some micropub clients
const url = Array.isArray(bookmarkUrl) ? bookmarkUrl[0] : bookmarkUrl;
let siteUrl;
try {
siteUrl = new URL(url).origin;
} catch {
return { error: `Invalid bookmark URL: ${url}` };
}
// Guard: blogroll DB must be available
if (typeof application.getBlogrollDb !== "function") {
console.warn("[Blogroll] bookmark-import: getBlogrollDb not available");
return { error: "blogroll not initialised" };
}
const db = application.getBlogrollDb();
// Check if any active blog with this siteUrl is already in the blogroll
const existing = await db.collection("blogrollBlogs").findOne({
siteUrl,
status: { $ne: "deleted" },
});
if (existing) {
// If the category differs, update it (tag changed on the bookmark post)
if (existing.category !== category) {
await db.collection("blogrollBlogs").updateOne(
{ _id: existing._id },
{ $set: { category, updatedAt: new Date().toISOString() } },
);
console.log(
`[Blogroll] bookmark-import: updated category for "${existing.title}" → "${category}"`,
);
return { updated: true, siteUrl };
}
console.log(
`[Blogroll] bookmark-import: ${siteUrl} already in blogroll ("${existing.title}")`,
);
return { alreadyExists: true, siteUrl };
}
// Discover feeds from the origin
const discovery = await discoverFeeds(siteUrl);
if (!discovery.success || discovery.feeds.length === 0) {
console.log(`[Blogroll] bookmark-import: no feeds found for ${siteUrl}`);
return { noFeeds: true, siteUrl };
}
// Add the first discovered feed
const feed = discovery.feeds[0];
const result = await upsertBlog(application, {
title: discovery.pageTitle || siteUrl,
feedUrl: feed.url,
siteUrl,
feedType: feed.type || "rss",
category,
source: "bookmark",
sourceId: null,
status: "active",
});
if (result.upserted) {
console.log(
`[Blogroll] bookmark-import: added ${feed.url} ("${discovery.pageTitle || siteUrl}") → category "${category}"`
);
}
return { added: result.upserted ? 1 : 0, siteUrl };
}