diff --git a/README.md b/README.md index 62dace634..1501445f6 100644 --- a/README.md +++ b/README.md @@ -556,12 +556,19 @@ const webhooks = new Webhooks({ secret: "mysecret", }); -const middleware = createNodeMiddleware(webhooks, { path: "/" }); - -createServer(middleware).listen(3000); -// can now receive user authorization callbacks at POST / +const middleware = createNodeMiddleware(webhooks, { path: "/webhooks" }); +createServer(async (req, res) => { + // `middleware` returns `false` when `req` is unhandled (beyond `/webhooks`) + if (await middleware(req, res)) return; + res.writeHead(404); + res.end(); +}).listen(3000); +// can now receive user authorization callbacks at POST /webhooks ``` +The middleware returned from `createNodeMiddleware` can also serve as an +`Express.js` middleware directly. + @@ -597,32 +604,6 @@ createServer(middleware).listen(3000); Used for internal logging. Defaults to [`console`](https://developer.mozilla.org/en-US/docs/Web/API/console) with `debug` and `info` doing nothing. - - - - - diff --git a/src/middleware/node/index.ts b/src/middleware/node/index.ts index 06312ba3b..eacb0c43b 100644 --- a/src/middleware/node/index.ts +++ b/src/middleware/node/index.ts @@ -1,20 +1,17 @@ import { createLogger } from "../../createLogger"; import { Webhooks } from "../../index"; import { middleware } from "./middleware"; -import { onUnhandledRequestDefault } from "./on-unhandled-request-default"; import { MiddlewareOptions } from "./types"; export function createNodeMiddleware( webhooks: Webhooks, { path = "/api/github/webhooks", - onUnhandledRequest = onUnhandledRequestDefault, log = createLogger(), }: MiddlewareOptions = {} ) { return middleware.bind(null, webhooks, { path, - onUnhandledRequest, log, } as Required); } diff --git a/src/middleware/node/middleware.ts b/src/middleware/node/middleware.ts index f0c818a5e..5ea36bf5f 100644 --- a/src/middleware/node/middleware.ts +++ b/src/middleware/node/middleware.ts @@ -11,6 +11,7 @@ import { WebhookEventHandlerError } from "../../types"; import { MiddlewareOptions } from "./types"; import { getMissingHeaders } from "./get-missing-headers"; import { getPayload } from "./get-payload"; +import { onUnhandledRequestDefault } from "./on-unhandled-request-default"; export async function middleware( webhooks: Webhooks, @@ -18,7 +19,7 @@ export async function middleware( request: IncomingMessage, response: ServerResponse, next?: Function -) { +): Promise { let pathname: string; try { pathname = new URL(request.url as string, "http://localhost").pathname; @@ -31,17 +32,15 @@ export async function middleware( error: `Request URL could not be parsed: ${request.url}`, }) ); - return; + return true; } - const isUnknownRoute = request.method !== "POST" || pathname !== options.path; - const isExpressMiddleware = typeof next === "function"; - if (isUnknownRoute) { - if (isExpressMiddleware) { - return next!(); - } else { - return options.onUnhandledRequest(request, response); - } + if (pathname !== options.path) { + next?.(); + return false; + } else if (request.method !== "POST") { + onUnhandledRequestDefault(request, response); + return true; } const missingHeaders = getMissingHeaders(request).join(", "); @@ -56,7 +55,7 @@ export async function middleware( }) ); - return; + return true; } const eventName = request.headers["x-github-event"] as WebhookEventName; @@ -85,13 +84,14 @@ export async function middleware( }); clearTimeout(timeout); - if (didTimeout) return; + if (didTimeout) return true; response.end("ok\n"); + return true; } catch (error) { clearTimeout(timeout); - if (didTimeout) return; + if (didTimeout) return true; const err = Array.from(error as WebhookEventHandlerError)[0]; const errorMessage = err.message @@ -106,5 +106,7 @@ export async function middleware( error: errorMessage, }) ); + + return true; } } diff --git a/src/middleware/node/types.ts b/src/middleware/node/types.ts index 81c4e0ede..f3de1ba5a 100644 --- a/src/middleware/node/types.ts +++ b/src/middleware/node/types.ts @@ -1,16 +1,6 @@ -// remove type imports from http for Deno compatibility -// see https://github.com/octokit/octokit.js/issues/24#issuecomment-817361886 -// import { IncomingMessage, ServerResponse } from "http"; -type IncomingMessage = any; -type ServerResponse = any; - import { Logger } from "../../createLogger"; export type MiddlewareOptions = { path?: string; log?: Logger; - onUnhandledRequest?: ( - request: IncomingMessage, - response: ServerResponse - ) => void; }; diff --git a/test/integration/node-middleware.test.ts b/test/integration/node-middleware.test.ts index d0d69f47a..7f45487a3 100644 --- a/test/integration/node-middleware.test.ts +++ b/test/integration/node-middleware.test.ts @@ -7,7 +7,7 @@ import { sign } from "@octokit/webhooks-methods"; // import without types const express = require("express"); -import { Webhooks, createNodeMiddleware } from "../../src"; +import { createNodeMiddleware, Webhooks } from "../../src"; const pushEventPayload = readFileSync( "test/fixtures/push-payload.json", @@ -168,39 +168,36 @@ describe("createNodeMiddleware(webhooks)", () => { server.close(); }); - test("custom non-found handler", async () => { + test("handle unhandled requests", async () => { const webhooks = new Webhooks({ secret: "mySecret", }); - const server = createServer( - createNodeMiddleware(webhooks, { - onUnhandledRequest(_request, response) { - response.writeHead(404); - response.end("nope"); - }, - }) - ).listen(); + const middleware = createNodeMiddleware(webhooks, {}); + const server = createServer(async (req, res) => { + if (!(await middleware(req, res))) { + res.writeHead(404, { "Content-Type": "text/plain" }); + res.write("nope."); + res.end(); + } + }).listen(); // @ts-expect-error complains about { port } although it's included in returned AddressInfo interface const { port } = server.address(); - const response = await fetch( - `http://localhost:${port}/api/github/webhooks`, - { - method: "PUT", - headers: { - "X-GitHub-Delivery": "123e4567-e89b-12d3-a456-426655440000", - "X-GitHub-Event": "push", - "X-Hub-Signature-256": signatureSha256, - }, - body: "invalid", - } - ); + const response = await fetch(`http://localhost:${port}/foo`, { + method: "PUT", + headers: { + "X-GitHub-Delivery": "123e4567-e89b-12d3-a456-426655440000", + "X-GitHub-Event": "push", + "X-Hub-Signature-256": signatureSha256, + }, + body: "invalid", + }); expect(response.status).toEqual(404); - await expect(response.text()).resolves.toEqual("nope"); + await expect(response.text()).resolves.toEqual("nope."); server.close(); });
- onUnhandledRequest - - function - - - -Defaults to - -```js -function onUnhandledRequest(request, response) { - response.writeHead(400, { - "content-type": "application/json", - }); - response.end( - JSON.stringify({ - error: error.message, - }) - ); -} -``` -