mirror of
https://github.com/svemagie/indiekit-endpoint-blogroll.git
synced 2026-04-02 15:34:59 +02:00
Adds FeedLand (feedland.com or self-hosted) as a new source type alongside OPML and Microsub. Syncs subscriptions via FeedLand's public OPML endpoint with optional category filtering and AJAX category discovery in the admin UI.
153 lines
4.7 KiB
JavaScript
153 lines
4.7 KiB
JavaScript
import express from "express";
|
|
import { fileURLToPath } from "node:url";
|
|
import path from "node:path";
|
|
|
|
import { dashboardController } from "./lib/controllers/dashboard.js";
|
|
import { blogsController } from "./lib/controllers/blogs.js";
|
|
import { sourcesController } from "./lib/controllers/sources.js";
|
|
import { apiController } from "./lib/controllers/api.js";
|
|
import { startSync, stopSync } from "./lib/sync/scheduler.js";
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
|
const protectedRouter = express.Router();
|
|
const publicRouter = express.Router();
|
|
|
|
const defaults = {
|
|
mountPath: "/blogrollapi",
|
|
syncInterval: 3600000, // 1 hour
|
|
maxItemsPerBlog: 50,
|
|
maxItemAge: 30, // days
|
|
fetchTimeout: 15000,
|
|
};
|
|
|
|
export default class BlogrollEndpoint {
|
|
name = "Blogroll endpoint";
|
|
|
|
constructor(options = {}) {
|
|
this.options = { ...defaults, ...options };
|
|
this.mountPath = this.options.mountPath;
|
|
}
|
|
|
|
get localesDirectory() {
|
|
return path.join(__dirname, "locales");
|
|
}
|
|
|
|
get viewsDirectory() {
|
|
return path.join(__dirname, "views");
|
|
}
|
|
|
|
get navigationItems() {
|
|
return {
|
|
href: this.options.mountPath,
|
|
text: "blogroll.title",
|
|
requiresDatabase: true,
|
|
};
|
|
}
|
|
|
|
get shortcutItems() {
|
|
return {
|
|
url: this.options.mountPath,
|
|
name: "blogroll.title",
|
|
iconName: "bookmark",
|
|
requiresDatabase: true,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Protected routes (require authentication)
|
|
* Admin dashboard and management
|
|
*/
|
|
get routes() {
|
|
// Dashboard
|
|
protectedRouter.get("/", dashboardController.get);
|
|
|
|
// Manual sync trigger
|
|
protectedRouter.post("/sync", dashboardController.sync);
|
|
|
|
// Clear and re-sync
|
|
protectedRouter.post("/clear-resync", dashboardController.clearResync);
|
|
|
|
// Sources management
|
|
protectedRouter.get("/sources", sourcesController.list);
|
|
protectedRouter.get("/sources/new", sourcesController.newForm);
|
|
protectedRouter.post("/sources", sourcesController.create);
|
|
protectedRouter.get("/sources/:id", sourcesController.edit);
|
|
protectedRouter.post("/sources/:id", sourcesController.update);
|
|
protectedRouter.post("/sources/:id/delete", sourcesController.remove);
|
|
protectedRouter.post("/sources/:id/sync", sourcesController.sync);
|
|
|
|
// Blogs management
|
|
protectedRouter.get("/blogs", blogsController.list);
|
|
protectedRouter.get("/blogs/new", blogsController.newForm);
|
|
protectedRouter.post("/blogs", blogsController.create);
|
|
protectedRouter.get("/blogs/:id", blogsController.edit);
|
|
protectedRouter.post("/blogs/:id", blogsController.update);
|
|
protectedRouter.post("/blogs/:id/delete", blogsController.remove);
|
|
protectedRouter.post("/blogs/:id/refresh", blogsController.refresh);
|
|
|
|
// Feed discovery (protected to prevent abuse)
|
|
protectedRouter.get("/api/discover", apiController.discover);
|
|
|
|
// Microsub integration (protected - internal use)
|
|
protectedRouter.post("/api/microsub-webhook", apiController.microsubWebhook);
|
|
protectedRouter.get("/api/microsub-status", apiController.microsubStatus);
|
|
|
|
// FeedLand integration (protected - category discovery)
|
|
protectedRouter.get("/api/feedland-categories", sourcesController.feedlandCategories);
|
|
|
|
return protectedRouter;
|
|
}
|
|
|
|
/**
|
|
* Public routes (no authentication required)
|
|
* Read-only JSON API endpoints for frontend
|
|
*/
|
|
get routesPublic() {
|
|
// Blogs API (read-only)
|
|
publicRouter.get("/api/blogs", apiController.listBlogs);
|
|
publicRouter.get("/api/blogs/:id", apiController.getBlog);
|
|
|
|
// Items API (read-only)
|
|
publicRouter.get("/api/items", apiController.listItems);
|
|
|
|
// Categories API
|
|
publicRouter.get("/api/categories", apiController.listCategories);
|
|
|
|
// Status API
|
|
publicRouter.get("/api/status", apiController.status);
|
|
|
|
// OPML export
|
|
publicRouter.get("/api/opml", apiController.exportOpml);
|
|
publicRouter.get("/api/opml/:category", apiController.exportOpmlCategory);
|
|
|
|
return publicRouter;
|
|
}
|
|
|
|
init(Indiekit) {
|
|
Indiekit.addEndpoint(this);
|
|
|
|
// Add MongoDB collections
|
|
Indiekit.addCollection("blogrollSources");
|
|
Indiekit.addCollection("blogrollBlogs");
|
|
Indiekit.addCollection("blogrollItems");
|
|
Indiekit.addCollection("blogrollMeta");
|
|
|
|
// Store config in application for controller access
|
|
Indiekit.config.application.blogrollConfig = this.options;
|
|
Indiekit.config.application.blogrollEndpoint = this.mountPath;
|
|
|
|
// Store database getter for controller access
|
|
Indiekit.config.application.getBlogrollDb = () => Indiekit.database;
|
|
|
|
// Start background sync if database is available
|
|
if (Indiekit.config.application.mongodbUrl) {
|
|
startSync(Indiekit, this.options);
|
|
}
|
|
}
|
|
|
|
destroy() {
|
|
stopSync();
|
|
}
|
|
}
|