diff --git a/db/jambones-sql.sql b/db/jambones-sql.sql
index b157f3a..3614fa1 100644
--- a/db/jambones-sql.sql
+++ b/db/jambones-sql.sql
@@ -418,6 +418,10 @@ disable_cdrs BOOLEAN NOT NULL DEFAULT 0,
trial_end_date DATETIME,
deactivated_reason VARCHAR(255),
device_to_call_ratio INTEGER NOT NULL DEFAULT 5,
+subspace_client_id VARCHAR(255),
+subspace_client_secret VARCHAR(255),
+subspace_sip_teleport_id VARCHAR(255),
+subspace_sip_teleport_destinations VARCHAR(255),
PRIMARY KEY (account_sid)
) COMMENT='An enterprise that uses the platform for comm services';
diff --git a/db/jambones.sqs b/db/jambones.sqs
index cb1fac9..c003746 100644
--- a/db/jambones.sqs
+++ b/db/jambones.sqs
@@ -1411,8 +1411,8 @@
327.00
- 296.00
- 340.00
+ 318.00
+ 420.00
4
@@ -1557,7 +1557,27 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1945,8 +1965,8 @@
- 837.00
- 741.00
+ 841.00
+ 824.00
345.00
@@ -2402,7 +2422,7 @@
-
+
@@ -2413,17 +2433,17 @@
-
+
-
-
-
-
+
+
+
+
diff --git a/lib/models/account.js b/lib/models/account.js
index 19e3ead..265d975 100644
--- a/lib/models/account.js
+++ b/lib/models/account.js
@@ -296,7 +296,23 @@ Account.fields = [
{
name: 'disable_cdrs',
type: 'number',
- }
+ },
+ {
+ name: 'subspace_client_id',
+ type: 'string',
+ },
+ {
+ name: 'subspace_client_secret',
+ type: 'string',
+ },
+ {
+ name: 'subspace_sip_teleport_id',
+ type: 'string',
+ },
+ {
+ name: 'subspace_sip_teleport_entry_points',
+ type: 'string',
+ },
];
module.exports = Account;
diff --git a/lib/routes/api/accounts.js b/lib/routes/api/accounts.js
index 415b209..5c4b9c0 100644
--- a/lib/routes/api/accounts.js
+++ b/lib/routes/api/accounts.js
@@ -12,7 +12,7 @@ const uuidv4 = require('uuid/v4');
const snakeCase = require('../../utils/snake-case');
const sysError = require('../error');
const {promisePool} = require('../../db');
-const {hasAccountPermissions, parseAccountSid} = require('./utils');
+const {hasAccountPermissions, parseAccountSid, enableSubspace, disableSubspace} = require('./utils');
const short = require('short-uuid');
const VoipCarrier = require('../../models/voip-carrier');
const translator = short();
@@ -371,6 +371,47 @@ router.get('/:sid/WebhookSecret', async(req, res) => {
}
});
+router.post('/:sid/SubspaceTeleport', async(req, res) => {
+ const logger = req.app.locals.logger;
+ try {
+ const service_provider_sid = req.user.hasServiceProviderAuth ? req.user.service_provider_sid : null;
+ const results = await Account.retrieve(req.params.sid, service_provider_sid);
+ if (results.length === 0) return res.status(404).end();
+ const {subspace_client_id, subspace_client_secret, sip_realm} = results[0];
+
+ const teleport = await enableSubspace({subspace_client_id, subspace_client_secret, sip_realm});
+ await Account.update(req.params.sid, {
+ subspace_sip_teleport_id: teleport.id,
+ subspace_sip_teleport_entry_points: JSON.stringify(teleport.teleport_entry_points)//hacky
+ });
+
+ return res.status(204);
+ }
+ catch (err) {
+ sysError(logger, res, err);
+ }
+});
+
+router.delete('/:sid/SubspaceTeleport', async(req, res) => {
+ const logger = req.app.locals.logger;
+ try {
+ const service_provider_sid = req.user.hasServiceProviderAuth ? req.user.service_provider_sid : null;
+ const results = await Account.retrieve(req.params.sid, service_provider_sid);
+ if (results.length === 0) return res.status(404).end();
+ const {subspace_client_id, subspace_client_secret, subspace_sip_teleport_id} = results[0];
+
+ await disableSubspace({subspace_client_id, subspace_client_secret, subspace_sip_teleport_id});
+ await Account.update(req.params.sid, {
+ subspace_sip_teleport_id: null,
+ subspace_sip_teleport_entry_points: null
+ });
+ return res.status(204);
+ }
+ catch (err) {
+ sysError(logger, res, err);
+ }
+});
+
/* update */
router.put('/:sid', async(req, res) => {
const sid = req.params.sid;
diff --git a/lib/routes/api/utils.js b/lib/routes/api/utils.js
index 96f2fec..1ad58e3 100644
--- a/lib/routes/api/utils.js
+++ b/lib/routes/api/utils.js
@@ -1,4 +1,5 @@
const uuid = require('uuid').v4;
+const bent = require('bent');
const Account = require('../../models/account');
const {promisePool} = require('../../db');
const {cancelSubscription, detachPaymentMethod} = require('../../utils/stripe-utils');
@@ -224,6 +225,55 @@ const checkLimits = async(req, res, next) => {
next();
};
+const getSubspaceJWT = async(id, secret) => {
+ const getToken = bent('https://id.subspace.com/oauth/token', 'POST', 'json', 200);
+
+ const jwt = await getToken(
+ {
+ client_id: id,
+ client_secret: secret,
+ audience: 'https://api.subspace.com/',
+ grant_type: 'client_credentials',
+ }
+ );
+ return jwt.access_token;
+};
+
+const enableSubspace = async(opts) => {
+ const accessToken = await getSubspaceJWT(opts.subspace_client_id, opts.subspace_client_secret);
+
+ const getTeleport = bent('https://api.subspace.com/v1/sip-teleports', 'POST', 'json', 200);
+
+ const teleport = await getTeleport(
+ {
+ destination: opts.sip_realm,
+ name: 'Jambonz'
+ },
+ {
+ 'Content-Type': 'application/json',
+ Authorization: `Bearer ${accessToken}`
+ }
+ );
+
+ return teleport;
+};
+
+const disableSubspace = async(opts) => {
+ const accessToken = await getSubspaceJWT(opts.subspace_client_id, opts.subspace_client_secret);
+
+ const deleteTeleport = bent(
+ `https://api.subspace.com/v1/sip-teleports/${opts.subspace_sip_teleport_id}`, 'DELETE', 'json', 200);
+
+ await deleteTeleport(
+ {
+ 'Content-Type': 'application/json',
+ Authorization: `Bearer ${accessToken}`
+ }
+ );
+
+ return;
+};
+
module.exports = {
setupFreeTrial,
createTestCdrs,
@@ -232,5 +282,7 @@ module.exports = {
parseServiceProviderSid,
hasAccountPermissions,
hasServiceProviderPermissions,
- checkLimits
+ checkLimits,
+ enableSubspace,
+ disableSubspace
};