diff --git a/packages/eventsub/src/EventSubListener.ts b/packages/eventsub/src/EventSubListener.ts index 85ceab5e8..1e47988bf 100644 --- a/packages/eventsub/src/EventSubListener.ts +++ b/packages/eventsub/src/EventSubListener.ts @@ -81,9 +81,28 @@ export class EventSubListener extends EventSubBase { }); next(); }); - this._server.get('/', this._createHandleHealthRequest()); - this._server.post('/:id', this._createDropLegacyRequest()); - this._server.post('/event/:id', this._createHandleRequest()); + let requestPathPrefix: string | undefined = undefined; + if (this._adapter.usePathPrefixInHandlers) { + requestPathPrefix = this._adapter.pathPrefix; + if (requestPathPrefix) { + requestPathPrefix = `/${requestPathPrefix.replace(/^\/|\/$/g, '')}`; + } + } + + const healthHandler = this._createHandleHealthRequest(); + const dropLegacyHandler = this._createDropLegacyRequest(); + const requestHandler = this._createHandleRequest(); + + if (requestPathPrefix) { + this._server.post(`${requestPathPrefix}/event/:id`, requestHandler); + this._server.post(`${requestPathPrefix}/:id`, dropLegacyHandler); + this._server.get(`${requestPathPrefix}`, healthHandler); + } else { + this._server.post('/event/:id', requestHandler); + this._server.post('/:id', dropLegacyHandler); + this._server.get('/', healthHandler); + } + const adapterListenerPort = await this._adapter.getListenerPort(); if (adapterListenerPort && port) { this._logger.warn(`Your passed port (${port}) is being ignored because the adapter has overridden it. diff --git a/packages/eventsub/src/EventSubMiddleware.ts b/packages/eventsub/src/EventSubMiddleware.ts index cef30ab27..3fc93efc5 100644 --- a/packages/eventsub/src/EventSubMiddleware.ts +++ b/packages/eventsub/src/EventSubMiddleware.ts @@ -1,5 +1,5 @@ import { rtfm } from '@twurple/common'; -import type { Express, RequestHandler } from 'express-serve-static-core'; +import type { IRouter, RequestHandler } from 'express-serve-static-core'; import { checkHostName } from './checks'; import type { EventSubBaseConfig } from './EventSubBase'; import { EventSubBase } from './EventSubBase'; @@ -19,6 +19,13 @@ export interface EventSubMiddlewareConfig extends EventSubBaseConfig { * The path your listener is mounted under. */ pathPrefix?: string; + + /** + * Whether the path prefix will added to the mount point. Defaults to `true`. + * + * Must be `false` if you use this with subrouters. + */ + usePathPrefixInHandlers?: boolean; } /** @@ -33,6 +40,7 @@ export interface EventSubMiddlewareConfig extends EventSubBaseConfig { export class EventSubMiddleware extends EventSubBase { private readonly _hostName: string; private readonly _pathPrefix?: string; + private readonly _usePathPrefixInHandlers: boolean; /** * Creates a new EventSub middleware wrapper. @@ -48,29 +56,33 @@ export class EventSubMiddleware extends EventSubBase { this._hostName = config.hostName; this._pathPrefix = config.pathPrefix; + this._usePathPrefixInHandlers = config.usePathPrefixInHandlers ?? true; } /** - * Applies middleware that handles EventSub notifications to an Express app. + * Applies middleware that handles EventSub notifications to an Express app/router. * - * @param app The app the middleware should be applied to. + * @param router The app or router the middleware should be applied to. */ - async apply(app: Express): Promise { - let pathPrefix = this._pathPrefix; - if (pathPrefix) { - pathPrefix = `/${pathPrefix.replace(/^\/|\/$/g, '')}`; + async apply(router: IRouter): Promise { + let requestPathPrefix: string | undefined = undefined; + if (this._usePathPrefixInHandlers) { + requestPathPrefix = this._pathPrefix; + if (requestPathPrefix) { + requestPathPrefix = `/${requestPathPrefix.replace(/^\/|\/$/g, '')}`; + } } const requestHandler = this._createHandleRequest() as unknown as RequestHandler; const dropLegacyHandler = this._createDropLegacyRequest() as unknown as RequestHandler; const healthHandler = this._createHandleHealthRequest() as unknown as RequestHandler; - if (pathPrefix) { - app.post(`${pathPrefix}/event/:id`, requestHandler); - app.post(`${pathPrefix}/:id`, dropLegacyHandler); - app.get(`${pathPrefix}`, healthHandler); + if (requestPathPrefix) { + router.post(`${requestPathPrefix}/event/:id`, requestHandler); + router.post(`${requestPathPrefix}/:id`, dropLegacyHandler); + router.get(`${requestPathPrefix}`, healthHandler); } else { - app.post('event/:id', requestHandler); - app.post(':id', dropLegacyHandler); - app.get('/', healthHandler); + router.post('event/:id', requestHandler); + router.post(':id', dropLegacyHandler); + router.get('/', healthHandler); } } diff --git a/packages/eventsub/src/adapters/ConnectionAdapter.ts b/packages/eventsub/src/adapters/ConnectionAdapter.ts index e4388a477..bbc915cc7 100644 --- a/packages/eventsub/src/adapters/ConnectionAdapter.ts +++ b/packages/eventsub/src/adapters/ConnectionAdapter.ts @@ -44,13 +44,25 @@ export abstract class ConnectionAdapter { /** * The path prefix an external connection needs to reach this server. * - * Please note that the layer redirecting to this server needs to strip the path prefix in order for this to work. - * - * For example, if this is set to /hooks, an external connection to /hooks/abc should pass /abc as the path to this server. - * * @protected */ get pathPrefix(): string | undefined { return undefined; } + + /** + * Whether the path prefix is passed to the handler. + * + * Defaults to `false` which means that the layer redirecting to this server needs to strip the path prefix in order for it to work. + * + * For example, if the path prefix is set to /hooks, an external connection to /hooks/abc should pass /abc as the path to this server. + * + * Conversely, if this is set to `true`, the path should be passed as is (i.e. /hooks/abc). + * + * @protected + */ + // eslint-disable-next-line @typescript-eslint/class-literal-property-style + get usePathPrefixInHandlers(): boolean { + return false; + } } diff --git a/packages/eventsub/src/adapters/ReverseProxyAdapter.ts b/packages/eventsub/src/adapters/ReverseProxyAdapter.ts index 88707a38b..13c85efb8 100644 --- a/packages/eventsub/src/adapters/ReverseProxyAdapter.ts +++ b/packages/eventsub/src/adapters/ReverseProxyAdapter.ts @@ -22,6 +22,17 @@ export interface ReverseProxyAdapterConfig { * The path prefix your reverse proxy redirects to the listener. */ pathPrefix?: string; + + /** + * Whether the path prefix is passed to the handler. + * + * Defaults to `false` which means that the layer redirecting to this server needs to strip the path prefix in order for it to work. + * + * For example, if the path prefix is set to /hooks, an external connection to /hooks/abc should pass /abc as the path to this server. + * + * Conversely, if this is set to `true`, the path should be passed as is (i.e. /hooks/abc). + */ + usePathPrefixInHandlers?: boolean; } /** @@ -34,6 +45,7 @@ export class ReverseProxyAdapter extends ConnectionAdapter { private readonly _hostName: string; private readonly _port: number; private readonly _pathPrefix?: string; + private readonly _usePathPrefixInHandlers: boolean; /** * Creates a reverse proxy connection adapter. @@ -50,6 +62,7 @@ export class ReverseProxyAdapter extends ConnectionAdapter { this._hostName = options.hostName; this._port = options.port ?? 8080; this._pathPrefix = options.pathPrefix; + this._usePathPrefixInHandlers = options.usePathPrefixInHandlers ?? false; } /** @protected */ @@ -66,4 +79,9 @@ export class ReverseProxyAdapter extends ConnectionAdapter { get pathPrefix(): string | undefined { return this._pathPrefix; } + + /** @protected */ + get usePathPrefixInHandlers(): boolean { + return this._usePathPrefixInHandlers; + } }