mirror of
https://github.com/jambonz/sbc-outbound.git
synced 2026-10-04 02:04:19 +00:00
Compare commits
14
Commits
v0.6.7-rc3
...
v0.7.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
607fb3d127 | ||
|
|
8da9c4ffa1 | ||
|
|
97131be582 | ||
|
|
3144cfd9c9 | ||
|
|
a1810e577a | ||
|
|
bd6c39d9ac | ||
|
|
24a974f48d | ||
|
|
1dfad575b9 | ||
|
|
dc30075665 | ||
|
|
e2bcc16568 | ||
|
|
a9ee51d671 | ||
|
|
9e45de261c | ||
|
|
5eb209354a | ||
|
|
4c3c167fe0 |
@@ -0,0 +1,51 @@
|
||||
name: Docker
|
||||
|
||||
on:
|
||||
push:
|
||||
# Publish `main` as Docker `latest` image.
|
||||
branches:
|
||||
- main
|
||||
|
||||
# Publish `v1.2.3` tags as releases.
|
||||
tags:
|
||||
- v*
|
||||
|
||||
env:
|
||||
IMAGE_NAME: sbc-outbound
|
||||
|
||||
jobs:
|
||||
push:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'push'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Build image
|
||||
run: docker build . --file Dockerfile --tag $IMAGE_NAME
|
||||
|
||||
- name: Log into registry
|
||||
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
|
||||
|
||||
- name: Push image
|
||||
run: |
|
||||
IMAGE_ID=ghcr.io/${{ github.repository_owner }}/$IMAGE_NAME
|
||||
|
||||
# Change all uppercase to lowercase
|
||||
IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]')
|
||||
|
||||
# Strip git ref prefix from version
|
||||
VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,')
|
||||
|
||||
# Strip "v" prefix from tag name
|
||||
[[ "${{ github.ref }}" == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//')
|
||||
|
||||
# Use Docker `latest` tag convention
|
||||
[ "$VERSION" == "main" ] && VERSION=latest
|
||||
|
||||
echo IMAGE_ID=$IMAGE_ID
|
||||
echo VERSION=$VERSION
|
||||
|
||||
docker tag $IMAGE_NAME $IMAGE_ID:$VERSION
|
||||
docker push $IMAGE_ID:$VERSION
|
||||
+3
-8
@@ -1,15 +1,10 @@
|
||||
FROM node:alpine as builder
|
||||
FROM node:16
|
||||
WORKDIR /opt/app/
|
||||
COPY package.json ./
|
||||
RUN npm install
|
||||
RUN npm prune --production
|
||||
|
||||
FROM node:alpine as app
|
||||
WORKDIR /opt/app
|
||||
RUN npm prune
|
||||
COPY . /opt/app
|
||||
COPY --from=builder /opt/app/node_modules ./node_modules
|
||||
|
||||
ARG NODE_ENV
|
||||
ENV NODE_ENV $NODE_ENV
|
||||
|
||||
CMD [ "npm", "start" ]
|
||||
CMD [ "npm", "start" ]
|
||||
@@ -36,7 +36,9 @@ const {
|
||||
lookupAllTeamsFQDNs,
|
||||
lookupAccountBySipRealm,
|
||||
lookupAccountBySid,
|
||||
lookupAccountCapacitiesBySid
|
||||
lookupAccountCapacitiesBySid,
|
||||
lookupSipGatewaysByCarrier,
|
||||
lookupCarrierBySid
|
||||
} = require('@jambonz/db-helpers')({
|
||||
host: process.env.JAMBONES_MYSQL_HOST,
|
||||
user: process.env.JAMBONES_MYSQL_USER,
|
||||
@@ -63,7 +65,9 @@ srf.locals = {...srf.locals,
|
||||
lookupAllTeamsFQDNs,
|
||||
lookupAccountBySipRealm,
|
||||
lookupAccountBySid,
|
||||
lookupAccountCapacitiesBySid
|
||||
lookupAccountCapacitiesBySid,
|
||||
lookupSipGatewaysByCarrier,
|
||||
lookupCarrierBySid
|
||||
},
|
||||
realtimeDbHelpers: {
|
||||
createHash,
|
||||
|
||||
+77
-23
@@ -1,8 +1,7 @@
|
||||
const Emitter = require('events');
|
||||
const {makeRtpEngineOpts, makeCallCountKey} = require('./utils');
|
||||
const {forwardInDialogRequests} = require('drachtio-fn-b2b-sugar');
|
||||
const {SipError} = require('drachtio-srf');
|
||||
const {parseUri} = require('drachtio-srf');
|
||||
const {SipError, stringifyUri, parseUri} = require('drachtio-srf');
|
||||
const debug = require('debug')('jambonz:sbc-outbound');
|
||||
|
||||
const makeInviteInProgressKey = (callid) => `sbc-out-iip${callid}`;
|
||||
@@ -54,7 +53,6 @@ class CallSession extends Emitter {
|
||||
this.req = req;
|
||||
this.res = res;
|
||||
this.srf = req.srf;
|
||||
this.performLcr = this.srf.locals.dbHelpers.performLcr;
|
||||
this.logger = logger.child({callId: req.get('Call-ID')});
|
||||
this.useWss = req.locals.registration && req.locals.registration.protocol === 'wss';
|
||||
this.stats = this.srf.locals.stats;
|
||||
@@ -64,6 +62,11 @@ class CallSession extends Emitter {
|
||||
this.incrKey = req.srf.locals.realtimeDbHelpers.incrKey;
|
||||
this.decrKey = req.srf.locals.realtimeDbHelpers.decrKey;
|
||||
this.callCountKey = makeCallCountKey(req.locals.account_sid);
|
||||
|
||||
const {performLcr, lookupCarrierBySid, lookupSipGatewaysByCarrier} = this.srf.locals.dbHelpers;
|
||||
this.performLcr = performLcr;
|
||||
this.lookupCarrierBySid = lookupCarrierBySid;
|
||||
this.lookupSipGatewaysByCarrier = lookupSipGatewaysByCarrier;
|
||||
}
|
||||
|
||||
get account_sid() {
|
||||
@@ -149,23 +152,59 @@ class CallSession extends Emitter {
|
||||
else {
|
||||
debug('calling lcr');
|
||||
try {
|
||||
/**
|
||||
* We normalize the called number by removing a leading + before sending it to LCR..
|
||||
* but LCR will return us an array of sip uris, with leading + for carriers that require it
|
||||
*/
|
||||
const routableNumber = this.req.calledNumber.startsWith('+') ?
|
||||
this.req.calledNumber.slice(1) :
|
||||
this.req.calledNumber;
|
||||
const gateways = await this.performLcr(routableNumber, this.account_sid);
|
||||
if (!gateways || gateways.length === 0) throw new Error('no routes found');
|
||||
debug(`got gateways: ${JSON.stringify(gateways)}`);
|
||||
gateways.forEach((gw) => mapGateways.set(gw.uri, {
|
||||
name: gw.name,
|
||||
auth: gw.auth,
|
||||
diversion: gw.diversion,
|
||||
hostport: gw.hostport
|
||||
}));
|
||||
uris = gateways.map((gw) => gw.uri);
|
||||
/* was a specific carrier requested */
|
||||
const voip_carrier_sid = this.req.get('X-Requested-Carrier-Sid');
|
||||
if (voip_carrier_sid) {
|
||||
const vc = await this.lookupCarrierBySid(voip_carrier_sid);
|
||||
const gateways = await this.lookupSipGatewaysByCarrier(voip_carrier_sid);
|
||||
const gws = (gateways || [])
|
||||
.filter((gw) => gw.outbound);
|
||||
if (gws.length) {
|
||||
uris = [];
|
||||
gws.forEach((o) => {
|
||||
const prefix = vc.tech_prefix;
|
||||
const hostport = !o.port || 5060 === o.port ? o.ipv4 : `${o.ipv4}:${o.port}`;
|
||||
const prependPlus = vc.e164_leading_plus && !this.req.calledNumber.startsWith('0');
|
||||
const u = `sip:${prefix ? prefix : ''}${prependPlus ? '+' : ''}${this.req.calledNumber}@${hostport}`;
|
||||
const obj = {
|
||||
name: vc.name,
|
||||
diversion: vc.diversion,
|
||||
hostport
|
||||
};
|
||||
if (vc.register_username && vc.register_password) {
|
||||
obj.auth = {
|
||||
username: vc.register_username,
|
||||
password: vc.register_password
|
||||
};
|
||||
}
|
||||
mapGateways.set(u, obj);
|
||||
uris.push(u);
|
||||
});
|
||||
this.logger.debug({uris, voip_carrier_sid}, 'selected outbound gateways for requested carrier');
|
||||
}
|
||||
else {
|
||||
this.logger.info({voip_carrier_sid}, 'no outbound gateways found for requested carrier');
|
||||
}
|
||||
}
|
||||
if (mapGateways.size === 0) {
|
||||
/**
|
||||
* We normalize the called number by removing a leading + before sending it to LCR..
|
||||
* but LCR will return us an array of sip uris, with leading + for carriers that require it
|
||||
*/
|
||||
const routableNumber = this.req.calledNumber.startsWith('+') ?
|
||||
this.req.calledNumber.slice(1) :
|
||||
this.req.calledNumber;
|
||||
const gateways = await this.performLcr(routableNumber, this.account_sid);
|
||||
if (!gateways || gateways.length === 0) throw new Error('no routes found');
|
||||
debug(`got gateways: ${JSON.stringify(gateways)}`);
|
||||
gateways.forEach((gw) => mapGateways.set(gw.uri, {
|
||||
name: gw.name,
|
||||
auth: gw.auth,
|
||||
diversion: gw.diversion,
|
||||
hostport: gw.hostport
|
||||
}));
|
||||
uris = gateways.map((gw) => gw.uri);
|
||||
}
|
||||
} catch (err) {
|
||||
debug(err);
|
||||
this.logger.error(err, 'Error performing lcr');
|
||||
@@ -238,7 +277,7 @@ class CallSession extends Emitter {
|
||||
proxy,
|
||||
passFailure,
|
||||
proxyRequestHeaders: ['all', '-X-MS-Teams-FQDN', '-X-MS-Teams-Tenant-FQDN', 'X-CID', '-Allow',
|
||||
'-Session-Expires', 'Min-SE'],
|
||||
'-Session-Expires', '-X-Requested-Carrier-Sid', 'Min-SE'],
|
||||
proxyResponseHeaders: ['all', '-Allow', '-Session-Expires'],
|
||||
headers: hdrs,
|
||||
responseHeaders,
|
||||
@@ -297,6 +336,7 @@ class CallSession extends Emitter {
|
||||
|
||||
// successfully connected
|
||||
this.logger.info(`call connected to ${uri}`);
|
||||
this.connectedUri = uri;
|
||||
debug('call connected');
|
||||
this.emit('connected', uri);
|
||||
|
||||
@@ -523,8 +563,22 @@ Duration=${payload.duration} `
|
||||
this.logger.info({uri, referTo}, 'received REFER from feature server');
|
||||
const arr = /context-(.*)/.exec(uri.user);
|
||||
if (!arr) {
|
||||
this.logger.info(`invalid Refer-To header: ${referTo.uri}`);
|
||||
return res.send(501);
|
||||
/* call transfer requested */
|
||||
const referredBy = req.getParsedHeader('Referred-By');
|
||||
if (!referredBy) return res.send(400);
|
||||
const u = parseUri(referredBy.uri);
|
||||
const farEnd = parseUri(this.connectedUri);
|
||||
uri.host = farEnd.host;
|
||||
uri.port = farEnd.port;
|
||||
|
||||
const response = await this.uac.request({
|
||||
method: 'REFER',
|
||||
headers: {
|
||||
'Refer-To': stringifyUri(uri),
|
||||
'Referred-By': stringifyUri(u)
|
||||
}
|
||||
});
|
||||
return res.send(response.status);
|
||||
}
|
||||
res.send(202);
|
||||
|
||||
|
||||
+2
-2
@@ -177,8 +177,8 @@ module.exports = (srf, logger, opts) => {
|
||||
return next();
|
||||
}
|
||||
|
||||
// if the called number is digits only (after possible leading plus sign) and long enough, do lcr
|
||||
if (!/^\d+$/.test(req.calledNumber.slice(1)) || req.calledNumber.length < 8) {
|
||||
// if the called number is digits only (after possible leading plus sign), do lcr
|
||||
if (!/^\d+$/.test(req.calledNumber.slice(1))) {
|
||||
debug(`unable to route call to ${aor}; no registered user found`);
|
||||
logger.info(`unable to route call to ${aor}; no registered user found`);
|
||||
return res.send(404);
|
||||
|
||||
Generated
+5314
-1369
File diff suppressed because it is too large
Load Diff
+12
-12
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sbc-outbound",
|
||||
"version": "0.6.7-rc3",
|
||||
"version": "v0.7.1",
|
||||
"main": "app.js",
|
||||
"engines": {
|
||||
"node": ">= 8.10.0"
|
||||
@@ -27,22 +27,22 @@
|
||||
"jslint": "eslint app.js lib"
|
||||
},
|
||||
"dependencies": {
|
||||
"@jambonz/db-helpers": "^0.6.12",
|
||||
"@jambonz/db-helpers": "^0.6.14",
|
||||
"@jambonz/mw-registrar": "0.2.1",
|
||||
"@jambonz/realtimedb-helpers": "^0.4.3",
|
||||
"@jambonz/rtpengine-utils": "^0.1.17",
|
||||
"@jambonz/stats-collector": "^0.1.5",
|
||||
"@jambonz/time-series": "^0.1.5",
|
||||
"@jambonz/realtimedb-helpers": "^0.4.9",
|
||||
"@jambonz/rtpengine-utils": "^0.1.21",
|
||||
"@jambonz/stats-collector": "^0.1.6",
|
||||
"@jambonz/time-series": "^0.1.6",
|
||||
"cidr-matcher": "^2.1.1",
|
||||
"debug": "^4.3.1",
|
||||
"debug": "^4.3.3",
|
||||
"drachtio-fn-b2b-sugar": "^0.0.12",
|
||||
"drachtio-srf": "^4.4.55",
|
||||
"pino": "^6.11.2"
|
||||
"drachtio-srf": "^4.4.59",
|
||||
"pino": "^7.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^7.18.0",
|
||||
"eslint-plugin-promise": "^4.2.1",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-plugin-promise": "^5.1.1",
|
||||
"nyc": "^15.1.0",
|
||||
"tape": "^5.2.2"
|
||||
"tape": "^5.3.2"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user