@apidaze/node-red-contrib-apidaze
Version:
Node-RED module for Apidaze
125 lines (105 loc) • 3.53 kB
JavaScript
module.exports = function (RED) {
'use strict';
const bodyParser = require('body-parser');
const multer = require('multer');
const url = require('url');
const upload = multer();
function Webhook(config) {
RED.nodes.createNode(this, config);
// methods names are case-sensitive. changing them will break functionality.
const supportedHttpMethods = ['get', 'post'];
this.accountNode = RED.nodes.getNode(config.account);
this.path = config.path;
const host = this.context().global.get('baseUrl');
if (!this.accountNode) {
this.warn('Missing account node in the webhook!');
}
if (!this.path) {
this.warn('Missing path!');
}
if (!this.path.startsWith('/')) {
this.path = `/${this.path}`;
}
const node = this;
this.errorHandler = function errorHandler(err, req, res) {
node.warn(err);
res.sendStatus(500);
};
this.callback = function callback(req, res) {
/**
* debug context
*/
const flowContext = node.context().flow;
const msgid = RED.util.generateId();
res._msgid = msgid;
const httpMethod = req.method.toLowerCase();
const requestType = ((method, type, req) => {
if (method === 'post') {
if (type === 'recording' || req.get('content-type').includes('multipart/form-data')) {
return 'recording';
} else if (type === 'incomingWebhookSMS') {
return 'message';
} else if (type === 'CDR') {
return 'cdr';
} else if (type === 'transcription') {
return 'transcription';
}
return 'unknown';
} else if (type === 'incomingRTCCall' || !!req.query.sessid) {
return 'rtcCall';
}
return 'voiceCall';
})(httpMethod, req.query.type || req.body.type, req);
const response = {
_msgid: msgid,
req: req,
res: { _res: res },
payload: {
hookType: requestType,
call: { custom: {} },
message: {},
body: req.body,
query: req.query,
headers: req.headers,
webhookUrl: host ? url.resolve(host, node.path) : (req.query.url || req.url),
file: req.file,
responses: [],
script: [],
},
};
node.send(response);
};
const maxApiRequestSize = RED.settings.apiMaxLength || '5mb';
const reqBodyParser = bodyParser.urlencoded({
limit: maxApiRequestSize,
extended: true,
});
supportedHttpMethods.forEach((supportedHttpMethod) => {
RED.httpNode[supportedHttpMethod](
this.path,
reqBodyParser,
upload.single('mediafile'),
this.callback,
this.errorHandler
);
});
this.on('close', function () {
const routerStack = RED.httpNode._router.stack;
const stackCount = routerStack.length;
for (let i = stackCount - 1; i >= 0; i -= 1) {
const layer = routerStack[i];
const routeLayer = layer.route;
const hasRouteLayer = !!routeLayer;
const pathMatches = hasRouteLayer && routeLayer.path === node.path;
const methodMatches = supportedHttpMethods.some(
(method) => routeLayer && routeLayer.methods[method]
);
const routeMatches = hasRouteLayer && pathMatches && methodMatches;
if (routeMatches) {
routerStack.splice(i, 1);
}
}
});
}
RED.nodes.registerType('webhook', Webhook);
};