UNPKG

@indiekit/indiekit

Version:

The little server that connects your website to the independent web

130 lines (107 loc) 4.05 kB
import path from "node:path"; import { assetsPath } from "@indiekit/frontend"; import express from "express"; import rateLimit from "express-rate-limit"; import * as assetsController from "./controllers/assets.js"; import * as clientController from "./controllers/client.js"; import * as feedController from "./controllers/feed.js"; import * as homepageController from "./controllers/homepage.js"; import * as manifestController from "./controllers/manifest.js"; import * as offlineController from "./controllers/offline.js"; import * as pluginController from "./controllers/plugin.js"; import * as sessionController from "./controllers/session.js"; import * as statusController from "./controllers/status.js"; import { IndieAuth } from "./indieauth.js"; const router = express.Router(); const limit = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 250, standardHeaders: true, legacyHeaders: false, validate: false, }); /** * Expose configuration to frontend templates and plug-ins * @param {object} Indiekit - Indiekit instance * @returns {import("express").Router} Express router */ export const routes = (Indiekit) => { const { endpoints, installedPlugins, publication } = Indiekit; const indieauth = new IndieAuth({ devMode: process.env.NODE_ENV === "development", me: publication.me, }); // Prevent pages from being indexed router.use((request, response, next) => { response.setHeader("X-Robots-Tag", "noindex"); next(); }); router.get("/robots.txt", (request, response) => { response.type("text/plain"); response.send("User-agent: *\nDisallow: /"); }); // Assets router.use("/assets", express.static(assetsPath, { maxAge: "7d" })); router.get("/assets/app-:hash.js", assetsController.getScripts); router.get("/assets/app-:hash.css", assetsController.getStyles); router.get( "/assets/app-icon-:size-:purpose.png", assetsController.getAppIcon, ); router.get( "/assets/shortcut-icon-:size-:name.png", assetsController.getShortcutIcon, ); // Service worker router.get("/serviceworker.js", offlineController.serviceworker); router.get("/offline", offlineController.offline); // Plug-in assets for (const plugin of installedPlugins) { if (plugin.filePath) { const assetsPath = path.join(plugin.filePath, "assets"); router.use(`/assets/${plugin.id}`, express.static(assetsPath)); } } // Feed router.get("/feed.jf2", feedController.jf2); // Web App Manifest router.get("/app.webmanifest", manifestController.get); // Client metadata router.get("/id", clientController.get); // Session router.get("/session/login", limit, sessionController.login); router.post("/session/login", limit, indieauth.login()); router.get("/session/auth", limit, indieauth.authorize()); router.get("/session/logout", sessionController.logout); // Public and .well-known endpoints for (const endpoint of endpoints) { // Internal routing // Currently used for endpoint-image which requires configuration values // to be passed on to express-sharp middleware if (endpoint.mountPath && endpoint._routes) { router.use(endpoint.mountPath, limit, endpoint._routes(Indiekit)); } if (endpoint.mountPath && endpoint.routesPublic) { router.use(endpoint.mountPath, limit, endpoint.routesPublic); } if (endpoint.routesWellKnown) { router.use("/.well-known/", limit, endpoint.routesWellKnown); } } // Authenticate subsequent requests router.use(indieauth.authenticate()); // Homepage router.get("/", homepageController.viewHomepage); // Plugin router.get("/plugins", limit, pluginController.list); router.get("/plugins/:pluginId", limit, pluginController.view); // Status router.get("/status", limit, statusController.viewStatus); // Authenticated endpoints for (const endpoint of endpoints) { if (endpoint.mountPath && endpoint.routes) { router.use(endpoint.mountPath, limit, endpoint.routes); } } return router; };