mirror of
https://github.com/jambonz/sbc-inbound.git
synced 2026-10-04 02:04:22 +00:00
Feature/recent calls enhancement with sp (#47)
* write cdrs and alerts with service_provider_sid * update to latest @jambonz/http-authenticator which sends sip headers * add call_sid to cdr when call is connected * write call counts by SP as well as account, bugfix to allow digit '0' in dtmf-relay
This commit is contained in:
+55
-37
@@ -2,7 +2,7 @@ const debug = require('debug')('jambonz:sbc-inbound');
|
||||
const assert = require('assert');
|
||||
const Emitter = require('events');
|
||||
const parseUri = require('drachtio-srf').parseUri;
|
||||
const {makeCallCountKey} = require('./utils');
|
||||
const {makeAccountCallCountKey, makeSPCallCountKey} = require('./utils');
|
||||
const msProxyIps = process.env.MS_TEAMS_SIP_PROXY_IPS ?
|
||||
process.env.MS_TEAMS_SIP_PROXY_IPS.split(',').map((i) => i.trim()) :
|
||||
[];
|
||||
@@ -30,10 +30,10 @@ module.exports = function(srf, logger) {
|
||||
stats.histogram('app.hook.response_time', rtt, ['hook_type:auth', `status:${status}`]);
|
||||
})
|
||||
.on('error', async(err, req) => {
|
||||
const {account_sid} = req.locals;
|
||||
const {account_sid, account} = req.locals;
|
||||
const {writeAlerts, AlertType} = req.srf.locals;
|
||||
if (account_sid) {
|
||||
let opts = {account_sid};
|
||||
let opts = {account_sid, service_provider_sid: account.service_provider_sid};
|
||||
if (err.code === 'ECONNREFUSED') {
|
||||
opts = {...opts, alert_type: AlertType.WEBHOOK_CONNECTION_FAILURE, url: err.hook};
|
||||
}
|
||||
@@ -134,12 +134,13 @@ module.exports = function(srf, logger) {
|
||||
const identifyAccount = async(req, res, next) => {
|
||||
try {
|
||||
const {siprec, callId} = req.locals;
|
||||
const {wasOriginatedFromCarrier, getApplicationForDidAndCarrier} = req.srf.locals;
|
||||
const {getSPForAccount, wasOriginatedFromCarrier, getApplicationForDidAndCarrier} = req.srf.locals;
|
||||
const {
|
||||
fromCarrier,
|
||||
gateway,
|
||||
account_sid,
|
||||
application_sid,
|
||||
service_provider_sid,
|
||||
account
|
||||
} = await wasOriginatedFromCarrier(req);
|
||||
/**
|
||||
@@ -173,6 +174,7 @@ module.exports = function(srf, logger) {
|
||||
gateway,
|
||||
voip_carrier_sid: gateway.voip_carrier_sid,
|
||||
application_sid: sid || gateway.application_sid,
|
||||
service_provider_sid,
|
||||
account_sid,
|
||||
account,
|
||||
...req.locals
|
||||
@@ -188,12 +190,13 @@ module.exports = function(srf, logger) {
|
||||
res.send(404, {headers: {'X-Reason': 'no configured application'}});
|
||||
return req.srf.endSession(req);
|
||||
}
|
||||
|
||||
const service_provider_sid = await getSPForAccount(app.account_sid);
|
||||
req.locals = {
|
||||
originator: 'teams',
|
||||
carrier: 'Microsoft Teams',
|
||||
msTeamsTenantFqdn: uri.host,
|
||||
account_sid: app.account_sid,
|
||||
service_provider_sid,
|
||||
...req.locals
|
||||
};
|
||||
}
|
||||
@@ -219,18 +222,21 @@ module.exports = function(srf, logger) {
|
||||
return req.srf.endSession(req);
|
||||
}
|
||||
req.locals = {
|
||||
service_provider_sid: account.service_provider_sid,
|
||||
account_sid: account.account_sid,
|
||||
account,
|
||||
webhook_secret: account.webhook_secret,
|
||||
...req.locals
|
||||
};
|
||||
}
|
||||
assert(req.locals.service_provider_sid);
|
||||
assert(req.locals.account_sid);
|
||||
req.locals.cdr.account_sid = req.locals.account_sid;
|
||||
|
||||
if (!req.locals.account) {
|
||||
req.locals.account = await lookupAccountBySid(req.locals.account_sid);
|
||||
}
|
||||
req.locals.cdr.service_provider_sid = req.locals.account?.service_provider_sid;
|
||||
|
||||
if (!req.locals.account.is_active) {
|
||||
stats.increment('sbc.terminations', ['sipStatus:503']);
|
||||
@@ -242,7 +248,11 @@ module.exports = function(srf, logger) {
|
||||
delete req.locals.cdr;
|
||||
}
|
||||
|
||||
req.locals.logger = logger.child({callId: req.get('Call-ID'), account_sid: req.locals.account_sid});
|
||||
req.locals.logger = logger.child({
|
||||
callId: req.get('Call-ID'),
|
||||
service_provider_sid: req.locals.service_provider_sid,
|
||||
account_sid: req.locals.account_sid
|
||||
});
|
||||
|
||||
next();
|
||||
} catch (err) {
|
||||
@@ -256,32 +266,35 @@ module.exports = function(srf, logger) {
|
||||
if (!process.env.JAMBONES_HOSTING && !process.env.JAMBONES_TRACK_ACCOUNT_CALLS) return next(); // skip
|
||||
|
||||
const {incrKey, decrKey} = req.srf.locals.realtimeDbHelpers;
|
||||
const {logger, account_sid} = req.locals;
|
||||
const {writeCallCount, writeAlerts, AlertType} = req.srf.locals;
|
||||
const {logger, account_sid, account, service_provider_sid} = req.locals;
|
||||
const {writeCallCount, writeCallCountSP, writeAlerts, AlertType} = req.srf.locals;
|
||||
assert(account_sid);
|
||||
const key = makeCallCountKey(account_sid);
|
||||
assert(service_provider_sid);
|
||||
const keyAccount = makeAccountCallCountKey(account_sid);
|
||||
const keySP = makeSPCallCountKey(service_provider_sid);
|
||||
|
||||
/* decrement count if INVITE is later rejected */
|
||||
res.once('end', ({status}) => {
|
||||
res.once('end', async({status}) => {
|
||||
if (status > 200) {
|
||||
decrKey(key)
|
||||
.then((count) => {
|
||||
logger.info({key}, `after rejection there are ${count} active calls for this account`);
|
||||
debug({key}, `after rejection there are ${count} active calls for this account`);
|
||||
return count;
|
||||
})
|
||||
.then((count) => writeCallCount({account_sid, calls_in_progress: count}))
|
||||
.catch((err) => logger.error({err}, 'checkLimits: decrKey err'));
|
||||
try {
|
||||
const [calls, callsSP] = await Promise.all([decrKey(keyAccount), decrKey(keySP)]);
|
||||
logger.info({calls, callsSP}, `decremented call counts after ${status} response`);
|
||||
} catch (err) {
|
||||
logger.info({err}, 'Error decrementing call count');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
/* increment the call count */
|
||||
const calls = await incrKey(key);
|
||||
writeCallCount({account_sid, calls_in_progress: calls})
|
||||
.then(() => logger.info(`checkLimits: after incrementing there are ${calls} active calls for this account`))
|
||||
.catch((err) => logger.error({err}, 'checkLimits: error writing call count'));
|
||||
if (!process.env.JAMBONES_HOSTING) return next();
|
||||
const [calls, callsSP] = await Promise.all([incrKey(keyAccount), incrKey(keySP)]);
|
||||
logger.info({calls, callsSP}, 'incremented call counts');
|
||||
|
||||
/* write the call counts to the database for both account and service provider */
|
||||
Promise.all([
|
||||
writeCallCount(account_sid, calls),
|
||||
writeCallCountSP(service_provider_sid, callsSP)
|
||||
]).catch((err) => logger.error({err}, 'Error writing call counts'));
|
||||
|
||||
/* compare to account's limit, though avoid db hit when call count is low */
|
||||
const minLimit = process.env.MIN_CALL_LIMIT ?
|
||||
@@ -290,21 +303,26 @@ module.exports = function(srf, logger) {
|
||||
logger.debug(`checkLimits: call count is now ${calls}, limit is ${minLimit}`);
|
||||
if (calls <= minLimit) return next();
|
||||
|
||||
const capacities = await lookupAccountCapacitiesBySid(account_sid);
|
||||
const limit = capacities.find((c) => c.category == 'voice_call_session');
|
||||
if (!limit) throw new Error('no account_capacities found');
|
||||
const limit_sessions = limit.quantity;
|
||||
if (calls > limit_sessions) {
|
||||
debug(`checkLimits: limits exceeded: call count ${calls}, limit ${limit_sessions}`);
|
||||
logger.info({calls, limit_sessions}, 'checkLimits: limits exceeded');
|
||||
writeAlerts({
|
||||
alert_type: AlertType.CALL_LIMIT,
|
||||
account_sid,
|
||||
count: limit_sessions
|
||||
}).catch((err) => logger.info({err}, 'checkLimits: error writing alert'));
|
||||
res.send(503, 'Maximum Calls In Progress');
|
||||
return req.srf.endSession(req);
|
||||
const accountCapacities = await lookupAccountCapacitiesBySid(account_sid);
|
||||
const accountLimit = accountCapacities.find((c) => c.category == 'voice_call_session');
|
||||
if (accountLimit) {
|
||||
/* check account limit */
|
||||
const limit_sessions = accountLimit.quantity;
|
||||
if (calls > limit_sessions) {
|
||||
debug(`checkLimits: limits exceeded: call count ${calls}, limit ${limit_sessions}`);
|
||||
logger.info({calls, limit_sessions}, 'checkLimits: limits exceeded');
|
||||
writeAlerts({
|
||||
alert_type: AlertType.ACCOUNT_CALL_LIMIT,
|
||||
service_provider_sid: account.service_provider_sid,
|
||||
account_sid,
|
||||
count: limit_sessions
|
||||
}).catch((err) => logger.info({err}, 'checkLimits: error writing alert'));
|
||||
res.send(503, 'Maximum Calls In Progress');
|
||||
return req.srf.endSession(req);
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: implement service provider limits
|
||||
next();
|
||||
} catch (err) {
|
||||
stats.increment('sbc.terminations', ['sipStatus:500']);
|
||||
|
||||
Reference in New Issue
Block a user