remove automatic bookmark-to-blogroll import
The hook was causing duplicate blog entries. Removed the contentNegotiationRoutes middleware, importBookmarkUrl function, and lib/bookmark-import.js entirely. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -7,71 +7,12 @@ import { blogsController } from "./lib/controllers/blogs.js";
|
|||||||
import { sourcesController } from "./lib/controllers/sources.js";
|
import { sourcesController } from "./lib/controllers/sources.js";
|
||||||
import { apiController } from "./lib/controllers/api.js";
|
import { apiController } from "./lib/controllers/api.js";
|
||||||
import { startSync, stopSync } from "./lib/sync/scheduler.js";
|
import { startSync, stopSync } from "./lib/sync/scheduler.js";
|
||||||
import { importBookmarkUrl } from "./lib/bookmark-import.js";
|
|
||||||
|
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
const protectedRouter = express.Router();
|
const protectedRouter = express.Router();
|
||||||
const publicRouter = express.Router();
|
const publicRouter = express.Router();
|
||||||
|
|
||||||
// Global hook router: intercepts POST requests site-wide to detect micropub
|
|
||||||
// bookmark creations and auto-import the bookmarked site into the blogroll.
|
|
||||||
// Mounted at "/" via contentNegotiationRoutes (runs before auth middleware).
|
|
||||||
//
|
|
||||||
// NOTE: When the Microsub plugin is installed it acts as the single source of
|
|
||||||
// truth for bookmarks — it creates the feed subscription AND notifies the
|
|
||||||
// blogroll via notifyBlogroll(). This hook therefore skips processing if
|
|
||||||
// Microsub is available, acting only as a standalone fallback.
|
|
||||||
const bookmarkHookRouter = express.Router();
|
|
||||||
bookmarkHookRouter.use((request, response, next) => {
|
|
||||||
response.on("finish", () => {
|
|
||||||
// Only act on successful POST creates (201 Created / 202 Accepted)
|
|
||||||
if (
|
|
||||||
request.method !== "POST" ||
|
|
||||||
(response.statusCode !== 201 && response.statusCode !== 202)
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore non-create actions (update, delete, undelete)
|
|
||||||
const action =
|
|
||||||
request.query?.action || request.body?.action || "create";
|
|
||||||
if (action !== "create") return;
|
|
||||||
|
|
||||||
// bookmark-of may be a top-level field (form-encoded / JF2 JSON)
|
|
||||||
// or nested inside properties (MF2 JSON format)
|
|
||||||
const bookmarkOf =
|
|
||||||
request.body?.["bookmark-of"] ||
|
|
||||||
request.body?.properties?.["bookmark-of"]?.[0];
|
|
||||||
if (!bookmarkOf) return;
|
|
||||||
|
|
||||||
const { application } = request.app.locals;
|
|
||||||
|
|
||||||
// Microsub plugin is installed → it will handle this bookmark and notify
|
|
||||||
// the blogroll. Skip direct import to avoid duplicate entries.
|
|
||||||
if (application.collections?.has("microsub_channels")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract category from any micropub body format:
|
|
||||||
// form-encoded: category=tech or category[]=tech&category[]=web
|
|
||||||
// JF2 JSON: { "category": ["tech", "web"] }
|
|
||||||
// MF2 JSON: { "properties": { "category": ["tech"] } }
|
|
||||||
const rawCategory =
|
|
||||||
request.body?.category ||
|
|
||||||
request.body?.properties?.category;
|
|
||||||
const category = Array.isArray(rawCategory)
|
|
||||||
? rawCategory[0] || "bookmarks"
|
|
||||||
: rawCategory || "bookmarks";
|
|
||||||
|
|
||||||
importBookmarkUrl(application, bookmarkOf, category).catch((err) =>
|
|
||||||
console.warn("[Blogroll] bookmark-import failed:", err.message)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
|
|
||||||
const defaults = {
|
const defaults = {
|
||||||
mountPath: "/blogrollapi",
|
mountPath: "/blogrollapi",
|
||||||
syncInterval: 3600000, // 1 hour
|
syncInterval: 3600000, // 1 hour
|
||||||
@@ -113,14 +54,6 @@ export default class BlogrollEndpoint {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Global middleware (mounted at "/") — intercepts micropub bookmark creations.
|
|
||||||
* Uses res.on("finish") so it never interferes with the request lifecycle.
|
|
||||||
*/
|
|
||||||
get contentNegotiationRoutes() {
|
|
||||||
return bookmarkHookRouter;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Protected routes (require authentication)
|
* Protected routes (require authentication)
|
||||||
* Admin dashboard and management
|
* Admin dashboard and management
|
||||||
|
|||||||
@@ -1,91 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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 };
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user