UNPKG

@indiekit/endpoint-micropub

Version:

Micropub endpoint for Indiekit. Enables publishing content to your website using the Micropub protocol.

142 lines (121 loc) 4.15 kB
import { IndiekitError } from "@indiekit/error"; import { formEncodedToJf2, mf2ToJf2 } from "../jf2.js"; import { uploadMedia } from "../media.js"; import { postContent } from "../post-content.js"; import { postData } from "../post-data.js"; import { checkScope } from "../scope.js"; /** * Perform requested post action * @type {import("express").RequestHandler} */ export const actionController = async (request, response, next) => { const { app, body, files, query, session } = request; const action = query.action || body?.action || "create"; const url = query.url || body?.url; const { application, publication } = app.locals; try { // Check provided scope const { scope, token } = session; const hasScope = checkScope(scope, action); if (!hasScope) { throw IndiekitError.insufficientScope( response.locals.__("ForbiddenError.insufficientScope"), { scope: action }, ); } // Toggle draft mode if `draft` scope const draftMode = hasScope === "draft"; // Check for URL if not creating a new post if (action !== "create" && !url) { throw IndiekitError.badRequest( response.locals.__("BadRequestError.missingParameter", "url"), ); } let data; let jf2; let content; switch (action) { case "create": { // Create and normalise JF2 data jf2 = request.is("json") ? await mf2ToJf2(body, publication.enrichPostData) : await formEncodedToJf2(body, publication.enrichPostData); // Attach files jf2 = files ? await uploadMedia(application.mediaEndpoint, token, jf2, files) : jf2; data = await postData.create(application, publication, jf2, draftMode); content = await postContent.create(publication, data); break; } case "update": { // Check for update operations if (!(body.replace || body.add || body.remove)) { throw IndiekitError.badRequest( response.locals.__( "BadRequestError.missingProperty", "replace, add or remove operations", ), ); } data = await postData.update(application, publication, url, body); if (!data) { content = { status: 200, location: url, json: { success: "update", success_description: `Post at ${url} not updated as no properties changed`, }, }; break; } // Draft mode: Only update posts that have `draft` post status if (draftMode && data.properties["post-status"] !== "draft") { throw IndiekitError.insufficientScope( response.locals.__("ForbiddenError.insufficientScope"), { scope: action }, ); } content = await postContent.update(publication, data, url); break; } case "delete": { data = await postData.delete(application, publication, url); content = await postContent.delete(publication, data); break; } case "undelete": { data = await postData.undelete( application, publication, url, draftMode, ); content = await postContent.undelete(publication, data); break; } default: } response .status(content.status) .location(content.location) .json(content.json); } catch (error) { let nextError = error; // Hoist not found error to controller to localise response if (error.name === "NotFoundError") { nextError = IndiekitError.notFound( response.locals.__("NotFoundError.record", error.message), ); } // Hoist unsupported post type error to controller to localise response if (error.name === "NotImplementedError") { nextError = IndiekitError.notImplemented( response.locals.__("NotImplementedError.postType", error.message), { uri: "https://getindiekit.com/configuration/post-types" }, ); } return next(nextError); } };