mirror of
https://github.com/jambonz/sbc-inbound.git
synced 2026-10-04 02:04:22 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f2e820915d | ||
|
|
a1ec0fd5da | ||
|
|
d7654de526 | ||
|
|
6e47e37cc2 | ||
|
|
c37d417a69 | ||
|
|
f7b3815ee3 |
@@ -33,6 +33,8 @@ Configuration is provided via environment variables:
|
||||
|K8S| service running as kubernetes service |no|
|
||||
|K8S_RTPENGINE_SERVICE_NAME| rtpengine service name(required for K8S) |no|
|
||||
|K8S_FEATURE_SERVER_SERVICE_NAME| feature server service name(required for K8S) |no|
|
||||
|JWT_SECRET| secret for signing JWT token |yes|
|
||||
|ENCRYPTION_SECRET| secret for credential encryption(JWT_SECRET is deprecated) |yes|
|
||||
|
||||
##### drachtio server location
|
||||
```
|
||||
|
||||
@@ -76,7 +76,8 @@ const {
|
||||
lookupAccountBySipRealm,
|
||||
lookupAccountBySid,
|
||||
lookupAccountCapacitiesBySid,
|
||||
queryCallLimits
|
||||
queryCallLimits,
|
||||
lookupClientByAccountAndUsername
|
||||
} = require('@jambonz/db-helpers')({
|
||||
host: process.env.JAMBONES_MYSQL_HOST,
|
||||
port: process.env.JAMBONES_MYSQL_PORT || 3306,
|
||||
@@ -125,7 +126,8 @@ srf.locals = {...srf.locals,
|
||||
lookupAccountBySid,
|
||||
lookupAccountBySipRealm,
|
||||
lookupAccountCapacitiesBySid,
|
||||
queryCallLimits
|
||||
queryCallLimits,
|
||||
lookupClientByAccountAndUsername
|
||||
},
|
||||
realtimeDbHelpers: {
|
||||
createSet,
|
||||
@@ -138,7 +140,8 @@ const {
|
||||
getSPForAccount,
|
||||
wasOriginatedFromCarrier,
|
||||
getApplicationForDidAndCarrier,
|
||||
getOutboundGatewayForRefer
|
||||
getOutboundGatewayForRefer,
|
||||
getApplicationBySid
|
||||
} = require('./lib/db-utils')(srf, logger);
|
||||
srf.locals = {
|
||||
...srf.locals,
|
||||
@@ -146,7 +149,8 @@ srf.locals = {
|
||||
wasOriginatedFromCarrier,
|
||||
getApplicationForDidAndCarrier,
|
||||
getOutboundGatewayForRefer,
|
||||
getFeatureServer: require('./lib/fs-tracking')(srf, logger)
|
||||
getFeatureServer: require('./lib/fs-tracking')(srf, logger),
|
||||
getApplicationBySid
|
||||
};
|
||||
const activeCallIds = srf.locals.activeCallIds;
|
||||
|
||||
|
||||
+33
-23
@@ -6,15 +6,20 @@ const {LifeCycleEvents} = require('./constants');
|
||||
const express = require('express');
|
||||
const app = express();
|
||||
const getString = bent('string');
|
||||
const AWS = require('aws-sdk');
|
||||
const sns = new AWS.SNS({apiVersion: '2010-03-31'});
|
||||
const autoscaling = new AWS.AutoScaling({apiVersion: '2011-01-01'});
|
||||
const {
|
||||
SNSClient,
|
||||
SubscribeCommand,
|
||||
UnsubscribeCommand } = require('@aws-sdk/client-sns');
|
||||
const snsClient = new SNSClient({ region: process.env.AWS_REGION, apiVersion: '2010-03-31' });
|
||||
const {
|
||||
AutoScalingClient,
|
||||
DescribeAutoScalingGroupsCommand,
|
||||
CompleteLifecycleActionCommand } = require('@aws-sdk/client-auto-scaling');
|
||||
const autoScalingClient = new AutoScalingClient({ region: process.env.AWS_REGION, apiVersion: '2011-01-01' });
|
||||
const {Parser} = require('xml2js');
|
||||
const parser = new Parser();
|
||||
const {validatePayload} = require('verify-aws-sns-signature');
|
||||
|
||||
AWS.config.update({region: process.env.AWS_REGION});
|
||||
|
||||
class SnsNotifier extends Emitter {
|
||||
constructor(logger) {
|
||||
super();
|
||||
@@ -64,7 +69,7 @@ class SnsNotifier extends Emitter {
|
||||
subscriptionRequestId: this.subscriptionRequestId
|
||||
}, 'response from SNS SubscribeURL');
|
||||
const data = await this.describeInstance();
|
||||
this.lifecycleState = data.AutoScalingInstances[0].LifecycleState;
|
||||
this.lifecycleState = data.AutoScalingGroups[0].Instances[0].LifecycleState;
|
||||
this.emit('SubscriptionConfirmation', {publicIp: this.publicIp});
|
||||
break;
|
||||
|
||||
@@ -130,11 +135,12 @@ class SnsNotifier extends Emitter {
|
||||
|
||||
async subscribe() {
|
||||
try {
|
||||
const response = await sns.subscribe({
|
||||
const params = {
|
||||
Protocol: 'http',
|
||||
TopicArn: process.env.AWS_SNS_TOPIC_ARM,
|
||||
Endpoint: this.snsEndpoint
|
||||
}).promise();
|
||||
};
|
||||
const response = await snsClient.send(new SubscribeCommand(params));
|
||||
this.logger.info({response}, `response to SNS subscribe to ${process.env.AWS_SNS_TOPIC_ARM}`);
|
||||
} catch (err) {
|
||||
this.logger.error({err}, `Error subscribing to SNS topic arn ${process.env.AWS_SNS_TOPIC_ARM}`);
|
||||
@@ -144,9 +150,10 @@ class SnsNotifier extends Emitter {
|
||||
async unsubscribe() {
|
||||
if (!this.subscriptionArn) throw new Error('SnsNotifier#unsubscribe called without an active subscription');
|
||||
try {
|
||||
const response = await sns.unsubscribe({
|
||||
const params = {
|
||||
SubscriptionArn: this.subscriptionArn
|
||||
}).promise();
|
||||
};
|
||||
const response = await snsClient.send(new UnsubscribeCommand(params));
|
||||
this.logger.info({response}, `response to SNS unsubscribe to ${process.env.AWS_SNS_TOPIC_ARM}`);
|
||||
} catch (err) {
|
||||
this.logger.error({err}, `Error unsubscribing to SNS topic arn ${process.env.AWS_SNS_TOPIC_ARM}`);
|
||||
@@ -155,26 +162,29 @@ class SnsNotifier extends Emitter {
|
||||
|
||||
completeScaleIn() {
|
||||
assert(this.scaleInParams);
|
||||
autoscaling.completeLifecycleAction(this.scaleInParams, (err, response) => {
|
||||
if (err) return this.logger.error({err}, 'Error completing scale-in');
|
||||
this.logger.info({response}, 'Successfully completed scale-in action');
|
||||
});
|
||||
autoScalingClient.send(new CompleteLifecycleActionCommand(this.scaleInParams))
|
||||
.then((data) => {
|
||||
return this.logger.info({data}, 'Successfully completed scale-in action');
|
||||
})
|
||||
.catch((err) => {
|
||||
this.logger.error({err}, 'Error completing scale-in');
|
||||
});
|
||||
}
|
||||
|
||||
describeInstance() {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this.instanceId) return reject('instance-id unknown');
|
||||
autoscaling.describeAutoScalingInstances({
|
||||
autoScalingClient.send(new DescribeAutoScalingGroupsCommand({
|
||||
InstanceIds: [this.instanceId]
|
||||
}, (err, data) => {
|
||||
if (err) {
|
||||
}))
|
||||
.then((data) => {
|
||||
this.logger.info({data}, 'SnsNotifier: describeInstance');
|
||||
return resolve(data);
|
||||
})
|
||||
.catch((err) => {
|
||||
this.logger.error({err}, 'Error describing instances');
|
||||
reject(err);
|
||||
} else {
|
||||
this.logger.info({data}, 'SnsNotifier: describeInstance');
|
||||
resolve(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -188,7 +198,7 @@ module.exports = async function(logger) {
|
||||
process.on('SIGHUP', async() => {
|
||||
try {
|
||||
const data = await notifier.describeInstance();
|
||||
const state = data.AutoScalingInstances[0].LifecycleState;
|
||||
const state = data.AutoScalingGroups[0].Instances[0].LifecycleState;
|
||||
if (state !== notifier.lifecycleState) {
|
||||
notifier.lifecycleState = state;
|
||||
switch (state) {
|
||||
|
||||
+43
-24
@@ -64,6 +64,7 @@ class CallSession extends Emitter {
|
||||
this.application_sid = req.locals.application_sid;
|
||||
this.account_sid = req.locals.account_sid;
|
||||
this.service_provider_sid = req.locals.service_provider_sid;
|
||||
this.srsClients = [];
|
||||
}
|
||||
|
||||
get isFromMSTeams() {
|
||||
@@ -317,10 +318,7 @@ class CallSession extends Emitter {
|
||||
this.activeCallIds.delete(callId);
|
||||
if (dlg.other && dlg.other.connected) dlg.other.destroy().catch((e) => {});
|
||||
|
||||
if (this.srsClient) {
|
||||
this.srsClient.stop();
|
||||
this.srsClient = null;
|
||||
}
|
||||
this._stopRecording();
|
||||
|
||||
this.srf.endSession(this.req);
|
||||
});
|
||||
@@ -329,6 +327,13 @@ class CallSession extends Emitter {
|
||||
dlg.on('modify', this._onReinvite.bind(this, dlg));
|
||||
}
|
||||
|
||||
_stopRecording() {
|
||||
if (this.srsClients.length) {
|
||||
this.srsClients.forEach((c) => c.stop());
|
||||
this.srsClients = [];
|
||||
}
|
||||
}
|
||||
|
||||
_setHandlers({uas, uac}) {
|
||||
this.emit('connected');
|
||||
const callStart = Date.now();
|
||||
@@ -380,12 +385,19 @@ class CallSession extends Emitter {
|
||||
const trunk = ['trunk', 'teams'].includes(this.req.locals.originator) ?
|
||||
this.req.locals.carrier :
|
||||
this.req.locals.originator;
|
||||
const application = await this.srf.locals.getApplicationBySid(application_sid);
|
||||
const isRecording = this.req.locals.account.record_all_calls || (application && application.record_all_calls);
|
||||
const day = new Date();
|
||||
let recording_url = `/Accounts/${this.account_sid}/RecentCalls/${call_sid}/record`;
|
||||
recording_url += `/${day.getFullYear()}/${(day.getMonth() + 1).toString().padStart(2, '0')}`;
|
||||
recording_url += `/${day.getDate().toString().padStart(2, '0')}/${this.req.locals.account.record_format}`;
|
||||
const cdr = {...this.req.locals.cdr,
|
||||
terminated_at: now,
|
||||
termination_reason: dlg.type === 'uas' ? 'caller hungup' : 'called party hungup',
|
||||
sip_status: 200,
|
||||
duration: Math.floor((now - callStart) / 1000),
|
||||
trunk
|
||||
trunk,
|
||||
...(isRecording && {recording_url})
|
||||
};
|
||||
this.logger.info({cdr}, 'going to write a cdr now..');
|
||||
this.writeCdrs({...this.req.locals.cdr,
|
||||
@@ -393,7 +405,8 @@ class CallSession extends Emitter {
|
||||
termination_reason: dlg.type === 'uas' ? 'caller hungup' : 'called party hungup',
|
||||
sip_status: 200,
|
||||
duration: Math.floor((now - callStart) / 1000),
|
||||
trunk
|
||||
trunk,
|
||||
...(isRecording && {recording_url})
|
||||
})
|
||||
.then(() => this.logger.debug('successfully wrote cdr'))
|
||||
.catch((err) => this.logger.error({err}, 'Error writing cdr for completed call'));
|
||||
@@ -404,10 +417,7 @@ class CallSession extends Emitter {
|
||||
dlg.other = null;
|
||||
other.other = null;
|
||||
|
||||
if (this.srsClient) {
|
||||
this.srsClient.stop();
|
||||
this.srsClient = null;
|
||||
}
|
||||
this._stopRecording();
|
||||
|
||||
this.logger.info(`call ended with normal termination, there are ${this.activeCallIds.size} active`);
|
||||
this.srf.endSession(this.req);
|
||||
@@ -688,7 +698,7 @@ Duration=${payload.duration} `
|
||||
const callSid = req.get('X-Call-Sid');
|
||||
const accountSid = req.get('X-Account-Sid');
|
||||
const applicationSid = req.get('X-Application-Sid');
|
||||
if (this.srsClient) {
|
||||
if (this.srsClients.length) {
|
||||
res.send(400);
|
||||
this.logger.info('discarding duplicate startCallRecording request for a call');
|
||||
return;
|
||||
@@ -698,13 +708,14 @@ Duration=${payload.duration} `
|
||||
res.send(400);
|
||||
return;
|
||||
}
|
||||
this.srsClient = new SrsClient(this.logger, {
|
||||
const arr = srsUrl.split(',');
|
||||
this.srsClients = arr.map((url) => new SrsClient(this.logger, {
|
||||
srf: dlg.srf,
|
||||
direction: 'inbound',
|
||||
originalInvite: this.req,
|
||||
callingNumber: this.req.callingNumber,
|
||||
calledNumber: this.req.calledNumber,
|
||||
srsUrl,
|
||||
srsUrl: url,
|
||||
srsRecordingId,
|
||||
callSid,
|
||||
accountSid,
|
||||
@@ -720,41 +731,49 @@ Duration=${payload.duration} `
|
||||
blockMedia: this.blockMedia,
|
||||
unblockMedia: this.unblockMedia,
|
||||
unsubscribe: this.unsubscribe
|
||||
});
|
||||
}));
|
||||
try {
|
||||
succeeded = await this.srsClient.start();
|
||||
succeeded = (await Promise.all(
|
||||
this.srsClients.map((c) => c.start())
|
||||
)).every((r) => r);
|
||||
} catch (err) {
|
||||
this.logger.error({err}, 'Error starting SipRec call recording');
|
||||
}
|
||||
}
|
||||
else if (reason === 'stopCallRecording') {
|
||||
if (!this.srsClient) {
|
||||
if (!this.srsClients.length) {
|
||||
res.send(400);
|
||||
this.logger.info('discarding stopCallRecording request because we are not recording');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
succeeded = await this.srsClient.stop();
|
||||
succeeded = (await Promise.all(
|
||||
this.srsClients.map((c) => c.stop())
|
||||
)).every((r) => r);
|
||||
} catch (err) {
|
||||
this.logger.error({err}, 'Error stopping SipRec call recording');
|
||||
}
|
||||
this.srsClient = null;
|
||||
this.srsClients = [];
|
||||
}
|
||||
else if (reason === 'pauseCallRecording') {
|
||||
if (!this.srsClient || this.srsClient.paused) {
|
||||
if (!this.srsClients.length || this.srsClients.every((c) => c.paused)) {
|
||||
this.logger.info('discarding invalid pauseCallRecording request');
|
||||
res.send(400);
|
||||
return;
|
||||
}
|
||||
succeeded = await this.srsClient.pause();
|
||||
succeeded = (await Promise.all(
|
||||
this.srsClients.map((c) => c.pause())
|
||||
)).every((r) => r);
|
||||
}
|
||||
else if (reason === 'resumeCallRecording') {
|
||||
if (!this.srsClient || !this.srsClient.paused) {
|
||||
if (!this.srsClients.length || !this.srsClients.every((c) => c.paused)) {
|
||||
res.send(400);
|
||||
this.logger.info('discarding invalid resumeCallRecording request');
|
||||
return;
|
||||
}
|
||||
succeeded = await this.srsClient.resume();
|
||||
succeeded = (await Promise.all(
|
||||
this.srsClients.map((c) => c.resume())
|
||||
)).every((r) => r);
|
||||
}
|
||||
res.send(succeeded ? 200 : 503);
|
||||
}
|
||||
@@ -807,8 +826,8 @@ Duration=${payload.duration} `
|
||||
res.send(response.status, {headers: responseHeaders, body: response.body});
|
||||
}
|
||||
} catch (err) {
|
||||
if (this.srsClient) {
|
||||
this.srsClient = null;
|
||||
if (this.srsClients.length) {
|
||||
this.srsClients = [];
|
||||
}
|
||||
res.send(500);
|
||||
this.logger.info({err}, `Error handing INFO request on ${dlg.type} leg`);
|
||||
|
||||
+9
-1
@@ -47,6 +47,7 @@ AND sg.voip_carrier_sid = vc.voip_carrier_sid`;
|
||||
|
||||
const sqlAccountByRealm = 'SELECT * from accounts WHERE sip_realm = ?';
|
||||
const sqlAccountBySid = 'SELECT * from accounts WHERE account_sid = ?';
|
||||
const sqlApplicationBySid = 'SELECT * from applications WHERE application_sid = ?';
|
||||
|
||||
const sqlQueryApplicationByDid = `
|
||||
SELECT * FROM phone_numbers
|
||||
@@ -91,6 +92,12 @@ module.exports = (srf, logger) => {
|
||||
const {pool} = srf.locals.dbHelpers;
|
||||
const pp = pool.promise();
|
||||
|
||||
const getApplicationBySid = async(application_sid) => {
|
||||
const [r] = await pp.query(sqlApplicationBySid, [application_sid]);
|
||||
if (0 === r.length) return null;
|
||||
return r[0];
|
||||
};
|
||||
|
||||
const getSPForAccount = async(account_sid) => {
|
||||
const [r] = await pp.query(sqlSelectSPForAccount, [account_sid]);
|
||||
if (0 === r.length) return null;
|
||||
@@ -317,6 +324,7 @@ module.exports = (srf, logger) => {
|
||||
wasOriginatedFromCarrier,
|
||||
getApplicationForDidAndCarrier,
|
||||
getOutboundGatewayForRefer,
|
||||
getSPForAccount
|
||||
getSPForAccount,
|
||||
getApplicationBySid
|
||||
};
|
||||
};
|
||||
|
||||
+9
-40
@@ -1,8 +1,8 @@
|
||||
const debug = require('debug')('jambonz:sbc-inbound');
|
||||
const assert = require('assert');
|
||||
const Emitter = require('events');
|
||||
const parseUri = require('drachtio-srf').parseUri;
|
||||
const {nudgeCallCounts, roundTripTime} = require('./utils');
|
||||
const digestChallenge = require('@jambonz/digest-utils');
|
||||
const msProxyIps = process.env.MS_TEAMS_SIP_PROXY_IPS ?
|
||||
process.env.MS_TEAMS_SIP_PROXY_IPS.split(',').map((i) => i.trim()) :
|
||||
[];
|
||||
@@ -22,42 +22,8 @@ const initCdr = (req) => {
|
||||
};
|
||||
|
||||
module.exports = function(srf, logger) {
|
||||
class AuthOutcomeReporter extends Emitter {
|
||||
constructor(stats) {
|
||||
super();
|
||||
this
|
||||
.on('regHookOutcome', ({rtt, status}) => {
|
||||
stats.histogram('app.hook.response_time', rtt, ['hook_type:auth', `status:${status}`]);
|
||||
})
|
||||
.on('error', async(err, req) => {
|
||||
const {account_sid, account} = req.locals;
|
||||
const {writeAlerts, AlertType} = req.srf.locals;
|
||||
if (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};
|
||||
}
|
||||
else if (err.code === 'ENOTFOUND') {
|
||||
opts = {...opts, alert_type: AlertType.WEBHOOK_CONNECTION_FAILURE, url: err.hook};
|
||||
}
|
||||
else if (err.name === 'StatusError') {
|
||||
opts = {...opts, alert_type: AlertType.WEBHOOK_STATUS_FAILURE, url: err.hook, status: err.statusCode};
|
||||
}
|
||||
|
||||
if (opts.alert_type) {
|
||||
try {
|
||||
await writeAlerts(opts);
|
||||
} catch (err) {
|
||||
logger.error({err, opts}, 'Error writing alert');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const {
|
||||
lookupAuthHook,
|
||||
lookupAppByTeamsTenant,
|
||||
lookupAccountBySipRealm,
|
||||
lookupAccountBySid,
|
||||
@@ -65,10 +31,6 @@ module.exports = function(srf, logger) {
|
||||
queryCallLimits
|
||||
} = srf.locals.dbHelpers;
|
||||
const {stats, writeCdrs} = srf.locals;
|
||||
const authenticator = require('@jambonz/http-authenticator')(lookupAuthHook, logger, {
|
||||
blacklistUnknownRealms: true,
|
||||
emitter: new AuthOutcomeReporter(stats)
|
||||
});
|
||||
|
||||
const initLocals = (req, res, next) => {
|
||||
const callId = req.get('Call-ID');
|
||||
@@ -240,6 +202,13 @@ module.exports = function(srf, logger) {
|
||||
account,
|
||||
application_sid: account.device_calling_application_sid,
|
||||
webhook_secret: account.webhook_secret,
|
||||
realm: uri.host,
|
||||
...(account.registration_hook && {
|
||||
registration_hook_url: account.registration_hook.url,
|
||||
registration_hook_method: account.registration_hook.method,
|
||||
registration_hook_username: account.registration_hook.username,
|
||||
registration_hook_password: account.registration_hook.password
|
||||
}),
|
||||
...req.locals
|
||||
};
|
||||
}
|
||||
@@ -382,7 +351,7 @@ module.exports = function(srf, logger) {
|
||||
try {
|
||||
/* TODO: check if this is a gateway that we have an ACL for */
|
||||
if (req.locals.originator !== 'user') return next();
|
||||
return authenticator(req, res, next);
|
||||
return digestChallenge(req, res, next);
|
||||
} catch (err) {
|
||||
stats.increment('sbc.terminations', ['sipStatus:500']);
|
||||
logger.error(err, `${req.get('Call-ID')} Error looking up related info for inbound call`);
|
||||
|
||||
Generated
+2102
-278
File diff suppressed because it is too large
Load Diff
+6
-5
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sbc-inbound",
|
||||
"version": "0.8.3",
|
||||
"version": "0.8.4",
|
||||
"main": "app.js",
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
@@ -20,20 +20,21 @@
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node app",
|
||||
"test": "NODE_ENV=test HTTP_PORT=3050 JAMBONES_NETWORK_CIDR='127.0.0.1/32' JAMBONES_HOSTING=1 SBC_ACCOUNT_SID=ed649e33-e771-403a-8c99-1780eabbc803 JAMBONES_TIME_SERIES_HOST=127.0.0.1 JAMBONES_MYSQL_HOST=127.0.0.1 JAMBONES_MYSQL_USER=jambones_test JAMBONES_MYSQL_PASSWORD=jambones_test JAMBONES_MYSQL_DATABASE=jambones_test JAMBONES_REDIS_HOST=localhost JAMBONES_REDIS_PORT=16379 JAMBONES_LOGLEVEL=error DRACHTIO_SECRET=cymru DRACHTIO_HOST=127.0.0.1 DRACHTIO_PORT=9060 JAMBONES_RTPENGINES=127.0.0.1:12222 JAMBONES_FEATURE_SERVERS=172.38.0.11 node test/ ",
|
||||
"test": "NODE_ENV=test HTTP_PORT=3050 JAMBONES_NETWORK_CIDR='127.0.0.1/32' JAMBONES_HOSTING=1 JWT_SECRET=foobarbazzle SBC_ACCOUNT_SID=ed649e33-e771-403a-8c99-1780eabbc803 JAMBONES_TIME_SERIES_HOST=127.0.0.1 JAMBONES_MYSQL_HOST=127.0.0.1 JAMBONES_MYSQL_USER=jambones_test JAMBONES_MYSQL_PASSWORD=jambones_test JAMBONES_MYSQL_DATABASE=jambones_test JAMBONES_REDIS_HOST=localhost JAMBONES_REDIS_PORT=16379 JAMBONES_LOGLEVEL=error DRACHTIO_SECRET=cymru DRACHTIO_HOST=127.0.0.1 DRACHTIO_PORT=9060 JAMBONES_RTPENGINES=127.0.0.1:12222 JAMBONES_FEATURE_SERVERS=172.38.0.11 node test/ ",
|
||||
"coverage": "./node_modules/.bin/nyc --reporter html --report-dir ./coverage npm run test",
|
||||
"jslint": "eslint app.js lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"@jambonz/db-helpers": "^0.9.0",
|
||||
"@jambonz/http-authenticator": "^0.2.2",
|
||||
"@jambonz/db-helpers": "^0.9.1",
|
||||
"@jambonz/http-health-check": "^0.0.1",
|
||||
"@jambonz/realtimedb-helpers": "^0.8.6",
|
||||
"@jambonz/rtpengine-utils": "^0.4.3",
|
||||
"@jambonz/siprec-client-utils": "^0.2.5",
|
||||
"@jambonz/stats-collector": "^0.1.8",
|
||||
"@jambonz/time-series": "^0.2.5",
|
||||
"aws-sdk": "^2.1354.0",
|
||||
"@jambonz/digest-utils": "^0.0.3",
|
||||
"@aws-sdk/client-sns": "^3.360.0",
|
||||
"@aws-sdk/client-auto-scaling": "^3.360.0",
|
||||
"bent": "^7.3.12",
|
||||
"cidr-matcher": "^2.1.1",
|
||||
"debug": "^4.3.4",
|
||||
|
||||
Reference in New Issue
Block a user