mirror of
https://github.com/n8n-io/n8n.git
synced 2025-01-11 04:47:29 -08:00
✨ OPTIONS request support for Production/Test Webhooks (#787)
* 🐛 Fix naming of events in AffinityTrigger Node * 🚧 OPTIONS allow header response for production webhooks * ✅ Implemented Allow header for test webhook OPTIONS response Co-authored-by: Jan Oberhauser <jan.oberhauser@gmail.com>
This commit is contained in:
parent
de8e6f2811
commit
82d94873fc
|
@ -161,6 +161,24 @@ export class ActiveWorkflowRunner {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all request methods associated with a single webhook
|
||||
* @param path webhook path
|
||||
*/
|
||||
async getWebhookMethods(path: string) : Promise<string[]> {
|
||||
const webhooks = await Db.collections.Webhook?.find({ webhookPath: path}) as IWebhookDb[];
|
||||
|
||||
// check if something exist
|
||||
if (webhooks === undefined) {
|
||||
// The requested webhooks are not registered
|
||||
throw new ResponseHelper.ResponseError(`The requested webhook "${path}" is not registered.`, 404, 404);
|
||||
}
|
||||
|
||||
// Gather all request methods in string array
|
||||
let webhookMethods : string[] = webhooks.map(webhook => webhook.method);
|
||||
return webhookMethods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ids of the currently active workflows
|
||||
*
|
||||
|
|
|
@ -1567,6 +1567,25 @@ class App {
|
|||
ResponseHelper.sendSuccessResponse(res, response.data, true, response.responseCode);
|
||||
});
|
||||
|
||||
// OPTIONS webhook requests
|
||||
this.app.options(`/${this.endpointWebhook}/*`, async (req: express.Request, res: express.Response) => {
|
||||
// Cut away the "/webhook/" to get the registred part of the url
|
||||
const requestUrl = (req as ICustomRequest).parsedUrl!.pathname!.slice(this.endpointWebhook.length + 2);
|
||||
|
||||
let allowedMethods;
|
||||
try {
|
||||
allowedMethods = await this.activeWorkflowRunner.getWebhookMethods(requestUrl);
|
||||
|
||||
// Add custom "Allow" header to satisfy OPTIONS response.
|
||||
res.append('Allow', allowedMethods);
|
||||
} catch (error) {
|
||||
ResponseHelper.sendErrorResponse(res, error);
|
||||
return;
|
||||
}
|
||||
|
||||
ResponseHelper.sendSuccessResponse(res, {}, true, 204);
|
||||
});
|
||||
|
||||
// GET webhook requests
|
||||
this.app.get(`/${this.endpointWebhook}/*`, async (req: express.Request, res: express.Response) => {
|
||||
// Cut away the "/webhook/" to get the registred part of the url
|
||||
|
@ -1630,6 +1649,25 @@ class App {
|
|||
ResponseHelper.sendSuccessResponse(res, response.data, true, response.responseCode);
|
||||
});
|
||||
|
||||
// HEAD webhook requests (test for UI)
|
||||
this.app.options(`/${this.endpointWebhookTest}/*`, async (req: express.Request, res: express.Response) => {
|
||||
// Cut away the "/webhook-test/" to get the registred part of the url
|
||||
const requestUrl = (req as ICustomRequest).parsedUrl!.pathname!.slice(this.endpointWebhookTest.length + 2);
|
||||
|
||||
let allowedMethods;
|
||||
try {
|
||||
allowedMethods = await this.testWebhooks.getWebhookMethods(requestUrl);
|
||||
|
||||
// Add custom "Allow" header to satisfy OPTIONS response.
|
||||
res.append('Allow', allowedMethods);
|
||||
} catch (error) {
|
||||
ResponseHelper.sendErrorResponse(res, error);
|
||||
return;
|
||||
}
|
||||
|
||||
ResponseHelper.sendSuccessResponse(res, {}, true, 204);
|
||||
});
|
||||
|
||||
// GET webhook requests (test for UI)
|
||||
this.app.get(`/${this.endpointWebhookTest}/*`, async (req: express.Request, res: express.Response) => {
|
||||
// Cut away the "/webhook-test/" to get the registred part of the url
|
||||
|
|
|
@ -110,6 +110,21 @@ export class TestWebhooks {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all request methods associated with a single test webhook
|
||||
* @param path webhook path
|
||||
*/
|
||||
async getWebhookMethods(path : string) : Promise<string[]> {
|
||||
const webhookMethods: string[] = this.activeWebhooks!.getWebhookMethods(path);
|
||||
|
||||
if (webhookMethods === undefined) {
|
||||
// The requested webhook is not registered
|
||||
throw new ResponseHelper.ResponseError(`The requested webhook "${path}" is not registered.`, 404, 404);
|
||||
}
|
||||
|
||||
return webhookMethods;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if it has to wait for webhook data to execute the workflow. If yes it waits
|
||||
|
|
|
@ -85,6 +85,21 @@ export class ActiveWebhooks {
|
|||
return this.webhookUrls[webhookKey];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all request methods associated with a single webhook
|
||||
* @param path
|
||||
*/
|
||||
getWebhookMethods(path: string): string[] {
|
||||
let methods : string[] = [];
|
||||
|
||||
Object.keys(this.webhookUrls)
|
||||
.filter(key => key.includes(path))
|
||||
.map(key => {
|
||||
methods.push(key.split('|')[0]);
|
||||
});
|
||||
|
||||
return methods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ids of all the workflows which have active webhooks
|
||||
|
|
|
@ -590,7 +590,7 @@ export interface IWorkflowMetadata {
|
|||
active: boolean;
|
||||
}
|
||||
|
||||
export type WebhookHttpMethod = 'GET' | 'POST' | 'HEAD';
|
||||
export type WebhookHttpMethod = 'GET' | 'POST' | 'HEAD' | 'OPTIONS';
|
||||
|
||||
export interface IWebhookResponseData {
|
||||
workflowData?: INodeExecutionData[][];
|
||||
|
|
Loading…
Reference in a new issue