mirror of
https://github.com/n8n-io/n8n.git
synced 2025-01-11 12:57: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
|
* Returns the ids of the currently active workflows
|
||||||
*
|
*
|
||||||
|
|
|
@ -1567,6 +1567,25 @@ class App {
|
||||||
ResponseHelper.sendSuccessResponse(res, response.data, true, response.responseCode);
|
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
|
// GET webhook requests
|
||||||
this.app.get(`/${this.endpointWebhook}/*`, async (req: express.Request, res: express.Response) => {
|
this.app.get(`/${this.endpointWebhook}/*`, async (req: express.Request, res: express.Response) => {
|
||||||
// Cut away the "/webhook/" to get the registred part of the url
|
// 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);
|
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)
|
// GET webhook requests (test for UI)
|
||||||
this.app.get(`/${this.endpointWebhookTest}/*`, async (req: express.Request, res: express.Response) => {
|
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
|
// 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
|
* 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];
|
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
|
* Returns the ids of all the workflows which have active webhooks
|
||||||
|
|
|
@ -590,7 +590,7 @@ export interface IWorkflowMetadata {
|
||||||
active: boolean;
|
active: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type WebhookHttpMethod = 'GET' | 'POST' | 'HEAD';
|
export type WebhookHttpMethod = 'GET' | 'POST' | 'HEAD' | 'OPTIONS';
|
||||||
|
|
||||||
export interface IWebhookResponseData {
|
export interface IWebhookResponseData {
|
||||||
workflowData?: INodeExecutionData[][];
|
workflowData?: INodeExecutionData[][];
|
||||||
|
|
Loading…
Reference in a new issue