mirror of
https://github.com/svemagie/indiekit-endpoint-microsub.git
synced 2026-04-02 15:35:00 +02:00
feat: restore full microsub implementation with reader UI
Restores complete implementation from feat/endpoint-microsub branch: - Reader UI with views (reader.njk, channel.njk, feeds.njk, etc.) - Feed polling, parsing, and normalization - WebSub subscriber - SSE realtime updates - Redis caching - Search indexing - Media proxy - Webmention processing
This commit is contained in:
113
index.js
113
index.js
@@ -1,12 +1,20 @@
|
||||
import path from "node:path";
|
||||
|
||||
import express from "express";
|
||||
|
||||
import { microsubController } from "./lib/controllers/microsub.js";
|
||||
import { readerController } from "./lib/controllers/reader.js";
|
||||
import { handleMediaProxy } from "./lib/media/proxy.js";
|
||||
import { startScheduler, stopScheduler } from "./lib/polling/scheduler.js";
|
||||
import { createIndexes } from "./lib/storage/items.js";
|
||||
import { webmentionReceiver } from "./lib/webmention/receiver.js";
|
||||
import { websubHandler } from "./lib/websub/handler.js";
|
||||
|
||||
const defaults = {
|
||||
mountPath: "/microsub",
|
||||
};
|
||||
const router = express.Router();
|
||||
const readerRouter = express.Router();
|
||||
|
||||
export default class MicrosubEndpoint {
|
||||
name = "Microsub endpoint";
|
||||
@@ -21,7 +29,32 @@ export default class MicrosubEndpoint {
|
||||
}
|
||||
|
||||
/**
|
||||
* Microsub API routes (authenticated)
|
||||
* Navigation items for Indiekit admin
|
||||
* @returns {object} Navigation item configuration
|
||||
*/
|
||||
get navigationItems() {
|
||||
return {
|
||||
href: path.join(this.options.mountPath, "reader"),
|
||||
text: "microsub.reader.title",
|
||||
requiresDatabase: true,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut items for quick actions
|
||||
* @returns {object} Shortcut item configuration
|
||||
*/
|
||||
get shortcutItems() {
|
||||
return {
|
||||
url: path.join(this.options.mountPath, "reader", "channels"),
|
||||
name: "microsub.channels.title",
|
||||
iconName: "feed",
|
||||
requiresDatabase: true,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Microsub API and reader UI routes (authenticated)
|
||||
* @returns {import("express").Router} Express router
|
||||
*/
|
||||
get routes() {
|
||||
@@ -29,9 +62,65 @@ export default class MicrosubEndpoint {
|
||||
router.get("/", microsubController.get);
|
||||
router.post("/", microsubController.post);
|
||||
|
||||
// WebSub callback endpoint
|
||||
router.get("/websub/:id", websubHandler.verify);
|
||||
router.post("/websub/:id", websubHandler.receive);
|
||||
|
||||
// Webmention receiving endpoint
|
||||
router.post("/webmention", webmentionReceiver.receive);
|
||||
|
||||
// Media proxy endpoint
|
||||
router.get("/media/:hash", handleMediaProxy);
|
||||
|
||||
// Reader UI routes (mounted as sub-router for correct baseUrl)
|
||||
readerRouter.get("/", readerController.index);
|
||||
readerRouter.get("/channels", readerController.channels);
|
||||
readerRouter.get("/channels/new", readerController.newChannel);
|
||||
readerRouter.post("/channels/new", readerController.createChannel);
|
||||
readerRouter.get("/channels/:uid", readerController.channel);
|
||||
readerRouter.get("/channels/:uid/settings", readerController.settings);
|
||||
readerRouter.post(
|
||||
"/channels/:uid/settings",
|
||||
readerController.updateSettings,
|
||||
);
|
||||
readerRouter.post("/channels/:uid/delete", readerController.deleteChannel);
|
||||
readerRouter.get("/channels/:uid/feeds", readerController.feeds);
|
||||
readerRouter.post("/channels/:uid/feeds", readerController.addFeed);
|
||||
readerRouter.post(
|
||||
"/channels/:uid/feeds/remove",
|
||||
readerController.removeFeed,
|
||||
);
|
||||
readerRouter.get("/item/:id", readerController.item);
|
||||
readerRouter.get("/compose", readerController.compose);
|
||||
readerRouter.post("/compose", readerController.submitCompose);
|
||||
readerRouter.get("/search", readerController.searchPage);
|
||||
readerRouter.post("/search", readerController.searchFeeds);
|
||||
readerRouter.post("/subscribe", readerController.subscribe);
|
||||
router.use("/reader", readerRouter);
|
||||
|
||||
return router;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public routes (no authentication required)
|
||||
* @returns {import("express").Router} Express router
|
||||
*/
|
||||
get routesPublic() {
|
||||
const publicRouter = express.Router();
|
||||
|
||||
// WebSub verification must be public for hubs to verify
|
||||
publicRouter.get("/websub/:id", websubHandler.verify);
|
||||
publicRouter.post("/websub/:id", websubHandler.receive);
|
||||
|
||||
// Webmention endpoint must be public
|
||||
publicRouter.post("/webmention", webmentionReceiver.receive);
|
||||
|
||||
// Media proxy must be public for images to load
|
||||
publicRouter.get("/media/:hash", handleMediaProxy);
|
||||
|
||||
return publicRouter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize plugin
|
||||
* @param {object} indiekit - Indiekit instance
|
||||
@@ -41,7 +130,11 @@ export default class MicrosubEndpoint {
|
||||
|
||||
// Register MongoDB collections
|
||||
indiekit.addCollection("microsub_channels");
|
||||
indiekit.addCollection("microsub_feeds");
|
||||
indiekit.addCollection("microsub_items");
|
||||
indiekit.addCollection("microsub_notifications");
|
||||
indiekit.addCollection("microsub_muted");
|
||||
indiekit.addCollection("microsub_blocked");
|
||||
|
||||
console.info("[Microsub] Registered MongoDB collections");
|
||||
|
||||
@@ -53,11 +146,27 @@ export default class MicrosubEndpoint {
|
||||
indiekit.config.application.microsubEndpoint = this.mountPath;
|
||||
}
|
||||
|
||||
// Create indexes for optimal performance (runs in background)
|
||||
// Start feed polling scheduler when server starts
|
||||
// This will be called after the server is ready
|
||||
if (indiekit.database) {
|
||||
console.info("[Microsub] Database available, starting scheduler");
|
||||
startScheduler(indiekit);
|
||||
|
||||
// Create indexes for optimal performance (runs in background)
|
||||
createIndexes(indiekit).catch((error) => {
|
||||
console.warn("[Microsub] Index creation failed:", error.message);
|
||||
});
|
||||
} else {
|
||||
console.warn(
|
||||
"[Microsub] Database not available at init, scheduler not started",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup on shutdown
|
||||
*/
|
||||
destroy() {
|
||||
stopScheduler();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user