Compare commits

...

5 Commits

12 changed files with 650 additions and 787 deletions

View File

@@ -26,8 +26,19 @@ router.post('/', async(req, res) => {
switch (target.type) {
case 'phone':
case 'teams':
uri = `sip:${target.number}@${sbcAddress}`;
to = target.number;
if ('teams' === target.type) {
const {lookupTeamsByAccount} = srf.locals.dbHelpers;
const obj = await lookupTeamsByAccount(req.body.account_sid);
if (!obj) throw new Error('dial to ms teams not allowed; account must first be configured with teams info');
Object.assign(opts.headers, {
'X-MS-Teams-FQDN': obj.ms_teams_fqdn,
'X-MS-Teams-Tenant-FQDN': target.tenant || obj.tenant_fqdn
});
if (target.vmail === true) uri = `${uri};opaque=app:voicemail`;
}
break;
case 'user':
uri = `sip:${target.name}`;
@@ -124,12 +135,14 @@ router.post('/', async(req, res) => {
if (err instanceof SipError) {
if ([486, 603].includes(err.status)) callStatus = CallStatus.Busy;
else if (487 === err.status) callStatus = CallStatus.NoAnswer;
sipLogger.info(`REST outdial failed with ${err.status}`);
cs.emit('callStatusChange', {callStatus, sipStatus: err.status});
if (sipLogger) sipLogger.info(`REST outdial failed with ${err.status}`);
else console.log(`REST outdial failed with ${err.status}`);
if (cs) cs.emit('callStatusChange', {callStatus, sipStatus: err.status});
}
else {
cs.emit('callStatusChange', {callStatus, sipStatus: 500});
sipLogger.error({err}, 'REST outdial failed');
if (cs) cs.emit('callStatusChange', {callStatus, sipStatus: 500});
if (sipLogger) sipLogger.error({err}, 'REST outdial failed');
else console.error(err);
}
ep.destroy();
}

View File

@@ -138,7 +138,7 @@ module.exports = function(srf, logger) {
if (0 === app.tasks.length) throw new Error('no application provided');
next();
} catch (err) {
logger.info(`Error retrieving or parsing application: ${err.message}`);
logger.info({err}, `Error retrieving or parsing application: ${err.message}`);
res.send(480, {headers: {'X-Reason': err.message}});
}
}

View File

@@ -1,5 +1,5 @@
const Task = require('./task');
const {TaskName, TaskPreconditions} = require('../utils/constants');
const {TaskName, TaskPreconditions, CallStatus} = require('../utils/constants');
/**
* Rejects an incoming call with user-specified status code and reason
@@ -19,6 +19,7 @@ class TaskSipDecline extends Task {
res.send(this.data.status, this.data.reason, {
headers: this.headers
});
cs.emit('callStatusChange', {callStatus: CallStatus.Failed, sipStatus: this.data.status});
}
}

View File

@@ -237,7 +237,8 @@
"sipUri": "string",
"auth": "#auth",
"vmail": "boolean",
"tenant": "string"
"tenant": "string",
"overrideTo": "string"
},
"required": [
"type"

View File

@@ -59,6 +59,7 @@ class SingleDialer extends Emitter {
async exec(srf, ms, opts) {
opts = opts || {};
opts.headers = opts.headers || {};
let uri, to;
try {
switch (this.target.type) {
@@ -69,7 +70,6 @@ class SingleDialer extends Emitter {
to = this.target.number;
if ('teams' === this.target.type) {
assert(this.target.teamsInfo);
opts.headers = opts.headers || {};
Object.assign(opts.headers, {
'X-MS-Teams-FQDN': this.target.teamsInfo.ms_teams_fqdn,
'X-MS-Teams-Tenant-FQDN': this.target.teamsInfo.tenant_fqdn
@@ -83,6 +83,12 @@ class SingleDialer extends Emitter {
uri = `sip:${this.target.name}`;
to = this.target.name;
if (this.target.overrideTo) {
Object.assign(opts.headers, {
'X-Override-To': this.target.overrideTo
});
}
// need to send to the SBC registered on
const reg = await registrar.query(aor);
if (reg) {
@@ -108,11 +114,22 @@ class SingleDialer extends Emitter {
this.ep = await ms.createEndpoint();
this.logger.debug(`SingleDialer:exec - created endpoint ${this.ep.uuid}`);
let promiseStreamConnected;
/**
* were we killed whilst we were off getting an endpoint ?
* https://github.com/jambonz/jambonz-feature-server/issues/30
*/
if (this.killed) {
this.logger.info('SingleDialer:exec got quick CANCEL from caller, abort outdial');
this.ep.destroy()
.catch((err) => this.logger.error({err}, 'Error destroying endpoint'));
return;
}
let lastSdp;
const connectStream = async(remoteSdp) => {
// wait for previous re-invite to complete, if any
if (promiseStreamConnected) await promiseStreamConnected.catch((err) => {});
return promiseStreamConnected = this.ep.modify(remoteSdp);
if (remoteSdp === lastSdp) return;
lastSdp = remoteSdp;
return this.ep.modify(remoteSdp);
};
Object.assign(opts, {
@@ -161,9 +178,9 @@ class SingleDialer extends Emitter {
this.emit('callStatusChange', status);
}
});
await connectStream(this.dlg.remote.sdp);
this.dlg.callSid = this.callSid;
this.inviteInProgress = null;
this.dlg.callSid = this.callSid;
await connectStream(this.dlg.remote.sdp);
this.emit('callStatusChange', {sipStatus: 200, callStatus: CallStatus.InProgress});
this.logger.debug(`SingleDialer:exec call connected: ${this.callSid}`);
const connectTime = this.dlg.connectTime = moment();
@@ -209,6 +226,7 @@ class SingleDialer extends Emitter {
* kill the call in progress or the stable dialog, whichever we have
*/
async kill() {
this.killed = true;
if (this.inviteInProgress) await this.inviteInProgress.cancel();
else if (this.dlg && this.dlg.connected) {
const duration = moment().diff(this.dlg.connectTime, 'seconds');

1359
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "jambonz-feature-server",
"version": "0.2.4",
"version": "0.2.5",
"main": "app.js",
"engines": {
"node": ">= 10.16.0"
@@ -29,18 +29,20 @@
"@jambonz/db-helpers": "^0.4.2",
"@jambonz/realtimedb-helpers": "^0.2.16",
"@jambonz/stats-collector": "^0.0.4",
"aws-sdk": "^2.830.0",
"bent": "^7.3.9",
"cidr-matcher": "^2.1.1",
"debug": "^4.1.1",
"deepcopy": "^2.1.0",
"drachtio-fsmrf": "^2.0.1",
"drachtio-srf": "^4.4.37",
"drachtio-srf": "^4.4.39",
"express": "^4.17.1",
"ip": "^1.1.5",
"jambonz-mw-registrar": "^0.1.3",
"moment": "^2.27.0",
"parse-url": "^5.0.2",
"pino": "^6.5.1",
"uuid": "^3.4.0",
"verify-aws-sns-signature": "^0.0.6",
"xml2js": "^0.4.23"
},

View File

@@ -1,4 +1,4 @@
const test = require('tape').test ;
const test = require('blue-tape') ;
const exec = require('child_process').exec ;
const pwd = process.env.TRAVIS ? '' : '-p$MYSQL_ROOT_PASSWORD';

View File

@@ -1,6 +1,5 @@
const test = require('tape').test ;
const test = require('blue-tape') ;
const exec = require('child_process').exec ;
const async = require('async');
test('starting docker network..', (t) => {
exec(`docker-compose -f ${__dirname}/docker-compose-testbed.yaml up -d`, (err, stdout, stderr) => {

View File

@@ -1,4 +1,4 @@
const test = require('tape').test ;
const test = require('blue-tape') ;
const exec = require('child_process').exec ;
test('stopping docker network..', (t) => {

View File

@@ -1,4 +1,4 @@
const test = require('tape').test ;
const test = require('blue-tape') ;
const exec = require('child_process').exec ;
const pwd = process.env.TRAVIS ? '' : '-p$MYSQL_ROOT_PASSWORD';

View File

@@ -1,4 +1,4 @@
const test = require('tape');
const test = require('blue-tape');
const debug = require('debug')('drachtio:jambonz:test');
const makeTask = require('../lib/tasks/make_task');
const noop = () => {};