mirror of
https://github.com/jambonz/jambonz-feature-server.git
synced 2025-12-20 08:40:38 +00:00
126 lines
4.0 KiB
JavaScript
126 lines
4.0 KiB
JavaScript
//const debug = require('debug')('jambonz:feature-server');
|
|
const assert = require('assert');
|
|
const request = require('request');
|
|
//require('request-debug')(request);
|
|
const uuidv4 = require('uuid/v4');
|
|
const makeTask = require('./tasks/make_task');
|
|
const normalizeJamones = require('./utils/normalize-jamones');
|
|
const {CallStatus, CallDirection} = require('./utils/constants');
|
|
|
|
module.exports = function(srf, logger) {
|
|
const {lookupAppByPhoneNumber} = srf.locals.dbHelpers;
|
|
|
|
function initLocals(req, res, next) {
|
|
req.locals = req.locals || {};
|
|
req.locals.logger = logger.child({callId: req.get('Call-ID')});
|
|
next();
|
|
}
|
|
|
|
/**
|
|
* Within the system, we deal with E.164 numbers _without_ the leading '+
|
|
*/
|
|
function normalizeNumbers(req, res, next) {
|
|
const logger = req.locals.logger;
|
|
Object.assign(req.locals, {
|
|
calledNumber: req.calledNumber,
|
|
callingNumber: req.callingNumber
|
|
});
|
|
try {
|
|
const regex = /^\+(\d+)$/;
|
|
let arr = regex.exec(req.calledNumber);
|
|
if (arr) req.locals.calledNumber = arr[1];
|
|
arr = regex.exec(req.callingNumber);
|
|
if (arr) req.locals.callingNumber = arr[1];
|
|
} catch (err) {
|
|
logger.error(err, `${req.get('Call-ID')} Error performing regex`);
|
|
}
|
|
next();
|
|
}
|
|
|
|
/**
|
|
* Given the dialed DID/phone number, retrieve the application to invoke
|
|
*/
|
|
async function retrieveApplication(req, res, next) {
|
|
const logger = req.locals.logger;
|
|
try {
|
|
const app = req.locals.application = await lookupAppByPhoneNumber(req.locals.calledNumber);
|
|
if (!app) {
|
|
logger.info(`rejecting call to DID ${req.locals.calledNumber}: no application associated`);
|
|
return res.send(480, {
|
|
headers: {
|
|
'X-Reason': 'no configured application'
|
|
}
|
|
});
|
|
}
|
|
logger.debug(app, `retrieved application for ${req.locals.calledNumber}`);
|
|
next();
|
|
} catch (err) {
|
|
logger.error(err, `${req.get('Call-ID')} Error looking up application for ${req.calledNumber}`);
|
|
res.send(500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Invoke the application callback and get the initial set of instructions
|
|
*/
|
|
async function invokeWebCallback(req, res, next) {
|
|
const logger = req.locals.logger;
|
|
const app = req.locals.application;
|
|
const call_sid = uuidv4();
|
|
const method = (app.hook_http_method || 'POST').toUpperCase();
|
|
const from = req.getParsedHeader('From');
|
|
req.locals.callAttributes = {
|
|
CallSid: call_sid,
|
|
AccountSid: app.account_sid,
|
|
From: req.callingNumber,
|
|
To: req.calledNumber,
|
|
Direction: CallDirection.Inbound,
|
|
CallerName: from.name || req.callingNumber,
|
|
SipCallID: req.get('Call-ID')
|
|
};
|
|
const qs = Object.assign({}, req.locals.callAttributes, {
|
|
CallStatus: CallStatus.Trying,
|
|
SipStatus: 100,
|
|
RequestorIP: req.get('X-Forwarded-For'),
|
|
RequestorName: req.get('X-Originating-Carrier')
|
|
});
|
|
const opts = {
|
|
url: app.call_hook,
|
|
method,
|
|
json: true,
|
|
qs
|
|
};
|
|
if (app.hook_basic_auth_user && app.hook_basic_auth_password) {
|
|
logger.debug(`using basic auth with ${app.hook_basic_auth_user}:${app.hook_basic_auth_password}`);
|
|
Object.assign(opts, {auth: {user: app.hook_basic_auth_user, password: app.hook_basic_auth_password}});
|
|
}
|
|
if (method === 'POST') Object.assign(opts, {body: req.msg});
|
|
try {
|
|
request(opts, (err, response, body) => {
|
|
if (err) {
|
|
logger.error(err, `Error invoking callback ${app.call_hook}`);
|
|
return res.send(500, 'Webhook Failure');
|
|
}
|
|
logger.debug(body, `application payload: ${body}`);
|
|
try {
|
|
app.tasks = normalizeJamones(logger, body).map((tdata) => makeTask(logger, tdata));
|
|
next();
|
|
} catch (err) {
|
|
logger.error(err, 'Invalid Webhook Response');
|
|
res.send(500);
|
|
}
|
|
});
|
|
} catch (err) {
|
|
logger.error(err, 'Error invoking web callback');
|
|
res.send(500);
|
|
}
|
|
}
|
|
|
|
return {
|
|
initLocals,
|
|
normalizeNumbers,
|
|
retrieveApplication,
|
|
invokeWebCallback
|
|
};
|
|
};
|