Skip to content

Commit

Permalink
add usePathPrefixInHandlers option to EventSubMiddleware and ReverseP…
Browse files Browse the repository at this point in the history
…roxyAdapter

defaults to true in the former, false in the latter (to not break BC)
  • Loading branch information
d-fischer committed Mar 5, 2022
1 parent 86b2d27 commit 3dba99c
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 21 deletions.
25 changes: 22 additions & 3 deletions packages/eventsub/src/EventSubListener.ts
Expand Up @@ -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.
Expand Down
40 changes: 26 additions & 14 deletions 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';
Expand All @@ -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;
}

/**
Expand All @@ -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.
Expand All @@ -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<void> {
let pathPrefix = this._pathPrefix;
if (pathPrefix) {
pathPrefix = `/${pathPrefix.replace(/^\/|\/$/g, '')}`;
async apply(router: IRouter): Promise<void> {
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);
}
}

Expand Down
20 changes: 16 additions & 4 deletions packages/eventsub/src/adapters/ConnectionAdapter.ts
Expand Up @@ -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;
}
}
18 changes: 18 additions & 0 deletions packages/eventsub/src/adapters/ReverseProxyAdapter.ts
Expand Up @@ -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;
}

/**
Expand All @@ -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.
Expand All @@ -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 */
Expand All @@ -66,4 +79,9 @@ export class ReverseProxyAdapter extends ConnectionAdapter {
get pathPrefix(): string | undefined {
return this._pathPrefix;
}

/** @protected */
get usePathPrefixInHandlers(): boolean {
return this._usePathPrefixInHandlers;
}
}

0 comments on commit 3dba99c

Please sign in to comment.