Files
jambonz-feature-server/lib/middleware.js
2020-01-29 15:27:20 -05:00

106 lines
3.3 KiB
JavaScript

//const debug = require('debug')('jambonz:feature-server');
const uuidv4 = require('uuid/v4');
const {CallStatus, CallDirection} = require('./utils/constants');
const CallInfo = require('./session/call-info');
const retrieveApp = require('./utils/retrieve-app');
const parseUrl = require('parse-url');
module.exports = function(srf, logger) {
const {lookupAppByPhoneNumber} = srf.locals.dbHelpers;
function initLocals(req, res, next) {
const callSid = uuidv4();
req.locals = {
callSid,
logger: logger.child({callId: req.get('Call-ID'), callSid})
};
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 = await lookupAppByPhoneNumber(req.locals.calledNumber);
if (!app || !app.call_hook || !app.call_hook.url) {
logger.info(`rejecting call to ${req.locals.calledNumber}: no application or webhook url`);
return res.send(480, {
headers: {
'X-Reason': 'no configured application'
}
});
}
req.locals.application = app;
logger.debug(app, `retrieved application for ${req.locals.calledNumber}`);
req.locals.callInfo = new CallInfo({req, app, direction: CallDirection.Inbound});
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_hook = app.call_hook;
const method = (call_hook.method || 'POST').toUpperCase();
let auth;
if (call_hook.username && call_hook.password) {
auth = {username: call_hook.username, password: call_hook.password};
}
try {
const u = parseUrl(call_hook.url);
const myPort = u.port ? `:${u.port}` : '';
app.originalRequest = {
baseUrl: `${u.protocol}://${u.resource}${myPort}`,
auth
};
logger.debug({originalRequest: app.originalRequest}, 'invokeWebCallback');
const obj = req.locals.callInfo;
// if the call hook is a POST add the entire SIP message to the payload
if (method === 'POST') Object.assign(obj, {sip: req.msg});
app.tasks = await retrieveApp(logger, app.call_hook, method, auth, obj);
next();
} catch (err) {
logger.error(err, 'Error retrieving or parsing application');
res.send(500);
}
}
return {
initLocals,
normalizeNumbers,
retrieveApplication,
invokeWebCallback
};
};