mirror of
https://github.com/jambonz/jambonz-api-server.git
synced 2025-12-18 21:37:43 +00:00
initial checkin
This commit is contained in:
1
.eslintignore
Normal file
1
.eslintignore
Normal file
@@ -0,0 +1 @@
|
||||
test/*
|
||||
126
.eslintrc.json
Normal file
126
.eslintrc.json
Normal file
@@ -0,0 +1,126 @@
|
||||
{
|
||||
"env": {
|
||||
"node": true,
|
||||
"es6": true
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaFeatures": {
|
||||
"jsx": false,
|
||||
"modules": false
|
||||
},
|
||||
"ecmaVersion": 2017
|
||||
},
|
||||
"plugins": ["promise"],
|
||||
"rules": {
|
||||
"promise/always-return": "error",
|
||||
"promise/no-return-wrap": "error",
|
||||
"promise/param-names": "error",
|
||||
"promise/catch-or-return": "error",
|
||||
"promise/no-native": "off",
|
||||
"promise/no-nesting": "warn",
|
||||
"promise/no-promise-in-callback": "warn",
|
||||
"promise/no-callback-in-promise": "warn",
|
||||
"promise/no-return-in-finally": "warn",
|
||||
|
||||
// Possible Errors
|
||||
// http://eslint.org/docs/rules/#possible-errors
|
||||
"comma-dangle": [2, "only-multiline"],
|
||||
"no-control-regex": 2,
|
||||
"no-debugger": 2,
|
||||
"no-dupe-args": 2,
|
||||
"no-dupe-keys": 2,
|
||||
"no-duplicate-case": 2,
|
||||
"no-empty-character-class": 2,
|
||||
"no-ex-assign": 2,
|
||||
"no-extra-boolean-cast" : 2,
|
||||
"no-extra-parens": [2, "functions"],
|
||||
"no-extra-semi": 2,
|
||||
"no-func-assign": 2,
|
||||
"no-invalid-regexp": 2,
|
||||
"no-irregular-whitespace": 2,
|
||||
"no-negated-in-lhs": 2,
|
||||
"no-obj-calls": 2,
|
||||
"no-proto": 2,
|
||||
"no-unexpected-multiline": 2,
|
||||
"no-unreachable": 2,
|
||||
"use-isnan": 2,
|
||||
"valid-typeof": 2,
|
||||
|
||||
// Best Practices
|
||||
// http://eslint.org/docs/rules/#best-practices
|
||||
"no-fallthrough": 2,
|
||||
"no-octal": 2,
|
||||
"no-redeclare": 2,
|
||||
"no-self-assign": 2,
|
||||
"no-unused-labels": 2,
|
||||
|
||||
// Strict Mode
|
||||
// http://eslint.org/docs/rules/#strict-mode
|
||||
"strict": [2, "never"],
|
||||
|
||||
// Variables
|
||||
// http://eslint.org/docs/rules/#variables
|
||||
"no-delete-var": 2,
|
||||
"no-undef": 2,
|
||||
"no-unused-vars": [2, {"args": "none"}],
|
||||
|
||||
// Node.js and CommonJS
|
||||
// http://eslint.org/docs/rules/#nodejs-and-commonjs
|
||||
"no-mixed-requires": 2,
|
||||
"no-new-require": 2,
|
||||
"no-path-concat": 2,
|
||||
"no-restricted-modules": [2, "sys", "_linklist"],
|
||||
|
||||
// Stylistic Issues
|
||||
// http://eslint.org/docs/rules/#stylistic-issues
|
||||
"comma-spacing": 2,
|
||||
"eol-last": 2,
|
||||
"indent": [2, 2, {"SwitchCase": 1}],
|
||||
"keyword-spacing": 2,
|
||||
"max-len": [2, 120, 2],
|
||||
"new-parens": 2,
|
||||
"no-mixed-spaces-and-tabs": 2,
|
||||
"no-multiple-empty-lines": [2, {"max": 2}],
|
||||
"no-trailing-spaces": [2, {"skipBlankLines": false }],
|
||||
"quotes": [2, "single", "avoid-escape"],
|
||||
"semi": 2,
|
||||
"space-before-blocks": [2, "always"],
|
||||
"space-before-function-paren": [2, "never"],
|
||||
"space-in-parens": [2, "never"],
|
||||
"space-infix-ops": 2,
|
||||
"space-unary-ops": 2,
|
||||
|
||||
// ECMAScript 6
|
||||
// http://eslint.org/docs/rules/#ecmascript-6
|
||||
"arrow-parens": [2, "always"],
|
||||
"arrow-spacing": [2, {"before": true, "after": true}],
|
||||
"constructor-super": 2,
|
||||
"no-class-assign": 2,
|
||||
"no-confusing-arrow": 2,
|
||||
"no-const-assign": 2,
|
||||
"no-dupe-class-members": 2,
|
||||
"no-new-symbol": 2,
|
||||
"no-this-before-super": 2,
|
||||
"prefer-const": 2
|
||||
},
|
||||
"globals": {
|
||||
"DTRACE_HTTP_CLIENT_REQUEST" : false,
|
||||
"LTTNG_HTTP_CLIENT_REQUEST" : false,
|
||||
"COUNTER_HTTP_CLIENT_REQUEST" : false,
|
||||
"DTRACE_HTTP_CLIENT_RESPONSE" : false,
|
||||
"LTTNG_HTTP_CLIENT_RESPONSE" : false,
|
||||
"COUNTER_HTTP_CLIENT_RESPONSE" : false,
|
||||
"DTRACE_HTTP_SERVER_REQUEST" : false,
|
||||
"LTTNG_HTTP_SERVER_REQUEST" : false,
|
||||
"COUNTER_HTTP_SERVER_REQUEST" : false,
|
||||
"DTRACE_HTTP_SERVER_RESPONSE" : false,
|
||||
"LTTNG_HTTP_SERVER_RESPONSE" : false,
|
||||
"COUNTER_HTTP_SERVER_RESPONSE" : false,
|
||||
"DTRACE_NET_STREAM_END" : false,
|
||||
"LTTNG_NET_STREAM_END" : false,
|
||||
"COUNTER_NET_SERVER_CONNECTION_CLOSE" : false,
|
||||
"DTRACE_NET_SERVER_CONNECTION" : false,
|
||||
"LTTNG_NET_SERVER_CONNECTION" : false,
|
||||
"COUNTER_NET_SERVER_CONNECTION" : false
|
||||
}
|
||||
}
|
||||
41
.gitignore
vendored
Normal file
41
.gitignore
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
package-lock.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
|
||||
# github pages site
|
||||
_site
|
||||
|
||||
#transient test cases
|
||||
examples/nosave.*.js
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul and nyc
|
||||
coverage
|
||||
.nyc_output/
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directory
|
||||
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
|
||||
node_modules
|
||||
|
||||
.DS_Store
|
||||
|
||||
examples/*
|
||||
|
||||
create_db.sql
|
||||
23
app.js
Normal file
23
app.js
Normal file
@@ -0,0 +1,23 @@
|
||||
const config = require('config');
|
||||
const opts = Object.assign({
|
||||
timestamp: () => {return `, "time": "${new Date().toISOString()}"`;}
|
||||
}, config.get('logging'));
|
||||
const logger = require('pino')(opts);
|
||||
const express = require('express');
|
||||
const app = express();
|
||||
const bodyParser = require('body-parser');
|
||||
const passport = require('passport');
|
||||
const authStrategy = require('./lib/auth')(logger);
|
||||
const routes = require('./lib/routes');
|
||||
const PORT = process.env.HTTP_PORT || 3000;
|
||||
|
||||
passport.use(authStrategy);
|
||||
|
||||
app.locals.logger = logger;
|
||||
|
||||
app.use(bodyParser.urlencoded({ extended: true }));
|
||||
app.use(bodyParser.json());
|
||||
app.use('/v1', passport.authenticate('bearer', { session: false }));
|
||||
app.use('/', routes);
|
||||
|
||||
app.listen(PORT);
|
||||
12
config/default.json.example
Normal file
12
config/default.json.example
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"logging": {
|
||||
"level": "info"
|
||||
},
|
||||
"mysql": {
|
||||
"host": "example.org",
|
||||
"user": "bob",
|
||||
"password": "secret",
|
||||
"database": "jambones",
|
||||
"connectionLimit": 10
|
||||
}
|
||||
}
|
||||
284
db/jambones-sql.sql
Normal file
284
db/jambones-sql.sql
Normal file
@@ -0,0 +1,284 @@
|
||||
/* SQLEditor (MySQL (2))*/
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `api_keys`;
|
||||
|
||||
DROP TABLE IF EXISTS `call_routes`;
|
||||
|
||||
DROP TABLE IF EXISTS `conference_participants`;
|
||||
|
||||
DROP TABLE IF EXISTS `queue_members`;
|
||||
|
||||
DROP TABLE IF EXISTS `calls`;
|
||||
|
||||
DROP TABLE IF EXISTS `phone_numbers`;
|
||||
|
||||
DROP TABLE IF EXISTS `applications`;
|
||||
|
||||
DROP TABLE IF EXISTS `conferences`;
|
||||
|
||||
DROP TABLE IF EXISTS `old_call`;
|
||||
|
||||
DROP TABLE IF EXISTS `queues`;
|
||||
|
||||
DROP TABLE IF EXISTS `subscriptions`;
|
||||
|
||||
DROP TABLE IF EXISTS `registered_users`;
|
||||
|
||||
DROP TABLE IF EXISTS `accounts`;
|
||||
|
||||
DROP TABLE IF EXISTS `service_providers`;
|
||||
|
||||
DROP TABLE IF EXISTS `phone_number_inventory`;
|
||||
|
||||
DROP TABLE IF EXISTS `voip_carriers`;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `api_keys`
|
||||
(
|
||||
`id` INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE ,
|
||||
`uuid` CHAR(36) NOT NULL UNIQUE ,
|
||||
`token` CHAR(36) NOT NULL UNIQUE ,
|
||||
`account_id` INTEGER(10) UNSIGNED,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB COMMENT='An authorization token that is used to access the REST api';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `applications`
|
||||
(
|
||||
`id` INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE ,
|
||||
`uuid` CHAR(36) NOT NULL UNIQUE ,
|
||||
`name` VARCHAR(255) NOT NULL,
|
||||
`account_id` INTEGER(10) UNSIGNED NOT NULL,
|
||||
`call_hook` VARCHAR(255),
|
||||
`call_status_hook` VARCHAR(255),
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB COMMENT='A defined set of behaviors to be applied to phone calls with';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `call_routes`
|
||||
(
|
||||
`id` INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE ,
|
||||
`uuid` CHAR(36) NOT NULL UNIQUE ,
|
||||
`account_id` INTEGER(10) UNSIGNED NOT NULL,
|
||||
`regex` VARCHAR(255) NOT NULL,
|
||||
`application_id` INTEGER(10) UNSIGNED NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `conferences`
|
||||
(
|
||||
`id` INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE ,
|
||||
`uuid` CHAR(36) NOT NULL UNIQUE ,
|
||||
`name` VARCHAR(255),
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB COMMENT='An audio conference';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `conference_participants`
|
||||
(
|
||||
`id` INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE ,
|
||||
`uuid` CHAR(36) NOT NULL UNIQUE ,
|
||||
`call_id` INTEGER(10) UNSIGNED,
|
||||
`conference_id` INTEGER(10) UNSIGNED NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB COMMENT='A relationship between a call and a conference that it is co';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `old_call`
|
||||
(
|
||||
`id` INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE ,
|
||||
`uuid` CHAR(36) NOT NULL UNIQUE ,
|
||||
`parent_call_id` INTEGER(10) UNSIGNED UNIQUE ,
|
||||
`application_id` INTEGER(10) UNSIGNED,
|
||||
`status_url` VARCHAR(255),
|
||||
`time_start` DATETIME NOT NULL,
|
||||
`time_alerting` DATETIME,
|
||||
`time_answered` DATETIME,
|
||||
`time_ended` DATETIME,
|
||||
`direction` ENUM('inbound','outbound'),
|
||||
`inbound_phone_number_id` INTEGER(10) UNSIGNED,
|
||||
`inbound_user_id` INTEGER(10) UNSIGNED,
|
||||
`outbound_user_id` INTEGER(10) UNSIGNED,
|
||||
`calling_number` VARCHAR(255),
|
||||
`called_number` VARCHAR(255),
|
||||
`caller_name` VARCHAR(255),
|
||||
`status` VARCHAR(255) NOT NULL COMMENT 'Possible values are queued, ringing, in-progress, completed, failed, busy and no-answer',
|
||||
`sip_uri` VARCHAR(255) NOT NULL,
|
||||
`sip_call_id` VARCHAR(255) NOT NULL,
|
||||
`sip_cseq` INTEGER NOT NULL,
|
||||
`sip_from_tag` VARCHAR(255) NOT NULL,
|
||||
`sip_via_branch` VARCHAR(255) NOT NULL,
|
||||
`sip_contact` VARCHAR(255),
|
||||
`sip_final_status` INTEGER UNSIGNED,
|
||||
`sdp_offer` VARCHAR(4096),
|
||||
`sdp_answer` VARCHAR(4096),
|
||||
`source_address` VARCHAR(255) NOT NULL,
|
||||
`source_port` INTEGER UNSIGNED NOT NULL,
|
||||
`dest_address` VARCHAR(255),
|
||||
`dest_port` INTEGER UNSIGNED,
|
||||
`url` VARCHAR(255),
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `phone_numbers`
|
||||
(
|
||||
`id` INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE ,
|
||||
`uuid` CHAR(36) NOT NULL UNIQUE ,
|
||||
`number` VARCHAR(255) NOT NULL UNIQUE ,
|
||||
`account_id` INTEGER(10) UNSIGNED NOT NULL,
|
||||
`application_id` INTEGER(10) UNSIGNED,
|
||||
`phone_number_inventory_id` INTEGER(10) UNSIGNED NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB COMMENT='A phone number that has been assigned to an account';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `queues`
|
||||
(
|
||||
`id` INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE ,
|
||||
`uuid` CHAR(36) NOT NULL UNIQUE ,
|
||||
`name` VARCHAR(255),
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB COMMENT='A set of behaviors to be applied to parked calls';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `queue_members`
|
||||
(
|
||||
`id` INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE ,
|
||||
`uuid` CHAR(36) NOT NULL UNIQUE ,
|
||||
`call_id` INTEGER(10) UNSIGNED,
|
||||
`queue_id` INTEGER(10) UNSIGNED NOT NULL,
|
||||
`position` INTEGER,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB COMMENT='A relationship between a call and a queue that it is waiting';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `registered_users`
|
||||
(
|
||||
`id` INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE ,
|
||||
`uuid` CHAR(36) NOT NULL UNIQUE ,
|
||||
`username` VARCHAR(255) NOT NULL,
|
||||
`domain` VARCHAR(255) NOT NULL,
|
||||
`sip_contact` VARCHAR(255) NOT NULL,
|
||||
`sip_user_agent` VARCHAR(255),
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB COMMENT='An active sip registration';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `calls`
|
||||
(
|
||||
`id` INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE ,
|
||||
`uuid` CHAR(36) NOT NULL UNIQUE ,
|
||||
`parent_call_id` INTEGER(10) UNSIGNED UNIQUE ,
|
||||
`application_id` INTEGER(10) UNSIGNED,
|
||||
`status_url` VARCHAR(255),
|
||||
`time_start` DATETIME NOT NULL,
|
||||
`time_alerting` DATETIME,
|
||||
`time_answered` DATETIME,
|
||||
`time_ended` DATETIME,
|
||||
`direction` ENUM('inbound','outbound'),
|
||||
`inbound_phone_number_id` INTEGER(10) UNSIGNED,
|
||||
`inbound_user_id` INTEGER(10) UNSIGNED,
|
||||
`outbound_user_id` INTEGER(10) UNSIGNED,
|
||||
`calling_number` VARCHAR(255),
|
||||
`called_number` VARCHAR(255),
|
||||
`caller_name` VARCHAR(255),
|
||||
`status` VARCHAR(255) NOT NULL COMMENT 'Possible values are queued, ringing, in-progress, completed, failed, busy and no-answer',
|
||||
`sip_uri` VARCHAR(255) NOT NULL,
|
||||
`sip_call_id` VARCHAR(255) NOT NULL,
|
||||
`sip_cseq` INTEGER NOT NULL,
|
||||
`sip_from_tag` VARCHAR(255) NOT NULL,
|
||||
`sip_via_branch` VARCHAR(255) NOT NULL,
|
||||
`sip_contact` VARCHAR(255),
|
||||
`sip_final_status` INTEGER UNSIGNED,
|
||||
`sdp_offer` VARCHAR(4096),
|
||||
`sdp_answer` VARCHAR(4096),
|
||||
`source_address` VARCHAR(255) NOT NULL,
|
||||
`source_port` INTEGER UNSIGNED NOT NULL,
|
||||
`dest_address` VARCHAR(255),
|
||||
`dest_port` INTEGER UNSIGNED,
|
||||
`url` VARCHAR(255),
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB COMMENT='A phone call';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `service_providers`
|
||||
(
|
||||
`id` INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE ,
|
||||
`uuid` CHAR(36) NOT NULL UNIQUE ,
|
||||
`name` VARCHAR(255) NOT NULL UNIQUE ,
|
||||
`description` VARCHAR(255),
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB COMMENT='An organization that provides communication services to its ';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `accounts`
|
||||
(
|
||||
`id` INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE ,
|
||||
`uuid` CHAR(36) NOT NULL UNIQUE ,
|
||||
`name` VARCHAR(255) NOT NULL,
|
||||
`sip_realm` VARCHAR(255),
|
||||
`service_provider_id` INTEGER(10) UNSIGNED NOT NULL,
|
||||
`registration_hook` VARCHAR(255),
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB COMMENT='A single end-user of the platform';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `subscriptions`
|
||||
(
|
||||
`id` INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE ,
|
||||
`uuid` CHAR(36) NOT NULL UNIQUE ,
|
||||
`subscribed_user_id` INTEGER(10) UNSIGNED NOT NULL,
|
||||
`event` VARCHAR(255),
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB COMMENT='An active sip subscription';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `voip_carriers`
|
||||
(
|
||||
`id` INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE ,
|
||||
`uuid` CHAR(36) NOT NULL UNIQUE ,
|
||||
`name` VARCHAR(255) NOT NULL UNIQUE ,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB COMMENT='An external organization that can provide sip trunking and D';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `phone_number_inventory`
|
||||
(
|
||||
`id` INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE ,
|
||||
`uuid` CHAR(36) NOT NULL UNIQUE ,
|
||||
`number` VARCHAR(255) NOT NULL UNIQUE ,
|
||||
`voip_carrier_id` INTEGER(10) UNSIGNED NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB COMMENT='Telephone numbers (DIDs) that have been procured from a voip';
|
||||
|
||||
ALTER TABLE `api_keys` ADD FOREIGN KEY account_id_idxfk (`account_id`) REFERENCES `accounts` (`id`);
|
||||
|
||||
CREATE INDEX `applications_name_idx` ON `applications` (`name`);
|
||||
ALTER TABLE `applications` ADD FOREIGN KEY account_id_idxfk_1 (`account_id`) REFERENCES `accounts` (`id`);
|
||||
|
||||
ALTER TABLE `call_routes` ADD FOREIGN KEY account_id_idxfk_2 (`account_id`) REFERENCES `accounts` (`id`);
|
||||
|
||||
ALTER TABLE `call_routes` ADD FOREIGN KEY application_id_idxfk (`application_id`) REFERENCES `applications` (`id`);
|
||||
|
||||
ALTER TABLE `conference_participants` ADD FOREIGN KEY call_id_idxfk (`call_id`) REFERENCES `calls` (`id`);
|
||||
|
||||
ALTER TABLE `conference_participants` ADD FOREIGN KEY conference_id_idxfk (`conference_id`) REFERENCES `conferences` (`id`);
|
||||
|
||||
ALTER TABLE `old_call` ADD FOREIGN KEY parent_call_id_idxfk (`parent_call_id`) REFERENCES `old_call` (`id`);
|
||||
|
||||
ALTER TABLE `old_call` ADD FOREIGN KEY application_id_idxfk_1 (`application_id`) REFERENCES `old_call` (`parent_call_id`);
|
||||
|
||||
ALTER TABLE `phone_numbers` ADD FOREIGN KEY account_id_idxfk_3 (`account_id`) REFERENCES `accounts` (`id`);
|
||||
|
||||
ALTER TABLE `phone_numbers` ADD FOREIGN KEY application_id_idxfk_2 (`application_id`) REFERENCES `applications` (`id`);
|
||||
|
||||
ALTER TABLE `phone_numbers` ADD FOREIGN KEY phone_number_inventory_id_idxfk (`phone_number_inventory_id`) REFERENCES `phone_number_inventory` (`id`);
|
||||
|
||||
ALTER TABLE `queue_members` ADD FOREIGN KEY call_id_idxfk_1 (`call_id`) REFERENCES `calls` (`id`);
|
||||
|
||||
ALTER TABLE `queue_members` ADD FOREIGN KEY queue_id_idxfk (`queue_id`) REFERENCES `queues` (`id`);
|
||||
|
||||
ALTER TABLE `calls` ADD FOREIGN KEY parent_call_id_idxfk_1 (`parent_call_id`) REFERENCES `calls` (`id`);
|
||||
|
||||
ALTER TABLE `calls` ADD FOREIGN KEY application_id_idxfk_3 (`application_id`) REFERENCES `calls` (`parent_call_id`);
|
||||
|
||||
ALTER TABLE `calls` ADD FOREIGN KEY inbound_phone_number_id_idxfk (`inbound_phone_number_id`) REFERENCES `phone_numbers` (`id`);
|
||||
|
||||
ALTER TABLE `calls` ADD FOREIGN KEY inbound_user_id_idxfk (`inbound_user_id`) REFERENCES `registered_users` (`id`);
|
||||
|
||||
ALTER TABLE `calls` ADD FOREIGN KEY outbound_user_id_idxfk (`outbound_user_id`) REFERENCES `registered_users` (`id`);
|
||||
|
||||
CREATE INDEX `service_providers_name_idx` ON `service_providers` (`name`);
|
||||
CREATE INDEX `accounts_name_idx` ON `accounts` (`name`);
|
||||
ALTER TABLE `accounts` ADD FOREIGN KEY service_provider_id_idxfk (`service_provider_id`) REFERENCES `service_providers` (`id`);
|
||||
|
||||
ALTER TABLE `subscriptions` ADD FOREIGN KEY subscribed_user_id_idxfk (`subscribed_user_id`) REFERENCES `registered_users` (`id`);
|
||||
|
||||
ALTER TABLE `phone_number_inventory` ADD FOREIGN KEY voip_carrier_id_idxfk (`voip_carrier_id`) REFERENCES `voip_carriers` (`id`);
|
||||
1138
db/jambones.sqs
Normal file
1138
db/jambones.sqs
Normal file
File diff suppressed because one or more lines are too long
39
lib/auth/index.js
Normal file
39
lib/auth/index.js
Normal file
@@ -0,0 +1,39 @@
|
||||
const Strategy = require('passport-http-bearer').Strategy;
|
||||
const {getMysqlConnection} = require('../db');
|
||||
const sql = `
|
||||
SELECT api_keys.uuid, accounts.uuid
|
||||
FROM api_keys
|
||||
LEFT JOIN accounts
|
||||
ON api_keys.account_id = accounts.id`;
|
||||
|
||||
function makeStrategy(logger) {
|
||||
return new Strategy(
|
||||
function(token, done) {
|
||||
logger.info(`validating with token ${token}`);
|
||||
getMysqlConnection((err, conn) => {
|
||||
if (err) {
|
||||
logger.error(err, 'Error retrieving mysql connection');
|
||||
return done(err);
|
||||
}
|
||||
conn.query({sql, nestTables: '_'}, [token], (err, results, fields) => {
|
||||
conn.release();
|
||||
if (err) {
|
||||
logger.error(err, 'Error querying for api key');
|
||||
return done(err);
|
||||
}
|
||||
if (0 == results.length) return done(null, false);
|
||||
if (results.length > 1) {
|
||||
logger.info(`api key ${token} exists in multiple rows of api_keys table!!`);
|
||||
return done(null, false);
|
||||
}
|
||||
|
||||
// found api key
|
||||
return done(null,
|
||||
{accountSid: results[0].accounts_uuid},
|
||||
{scope: results[0].accounts_uuid ? ['user'] : ['admin']});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = makeStrategy;
|
||||
5
lib/db/index.js
Normal file
5
lib/db/index.js
Normal file
@@ -0,0 +1,5 @@
|
||||
const getMysqlConnection = require('./mysql');
|
||||
|
||||
module.exports = {
|
||||
getMysqlConnection
|
||||
};
|
||||
13
lib/db/mysql.js
Normal file
13
lib/db/mysql.js
Normal file
@@ -0,0 +1,13 @@
|
||||
const mysql = require('mysql');
|
||||
const config = require('config');
|
||||
const pool = mysql.createPool(config.get('mysql'));
|
||||
|
||||
pool.getConnection((err, conn) => {
|
||||
if (err) return console.error(err, 'Error testing pool');
|
||||
conn.ping((err) => {
|
||||
if (err) return console.error(err, `Error pinging mysql at ${JSON.stringify(config.get('mysql'))}`);
|
||||
console.log('successfully pinged mysql database');
|
||||
});
|
||||
});
|
||||
|
||||
module.exports = pool.getConnection.bind(pool);
|
||||
23
lib/models/service-provider.js
Normal file
23
lib/models/service-provider.js
Normal file
@@ -0,0 +1,23 @@
|
||||
const Emitter = require('events');
|
||||
const {getMysqlConnection} = require('../db');
|
||||
const scrubIds = require('../utils/scrub-ids');
|
||||
|
||||
class ServiceProvider extends Emitter {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
static retrieveAll() {
|
||||
return new Promise((resolve, reject) => {
|
||||
getMysqlConnection((err, conn) => {
|
||||
if (err) return reject(err);
|
||||
conn.query('SELECT * from service_providers', (err, results, fields) => {
|
||||
if (err) return reject(err);
|
||||
resolve(scrubIds(results));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ServiceProvider;
|
||||
21
lib/routes/api/index.js
Normal file
21
lib/routes/api/index.js
Normal file
@@ -0,0 +1,21 @@
|
||||
const api = require('express').Router();
|
||||
|
||||
function isAdmin(req, res, next) {
|
||||
if (req.authInfo.scope.includes('admin')) return next();
|
||||
res.status(403).json({
|
||||
status: 'fail',
|
||||
message: 'insufficient privileges'
|
||||
});
|
||||
}
|
||||
|
||||
function isUser(req, res, next) {
|
||||
if (req.authInfo.scope.includes('user')) return next();
|
||||
res.status(403).json({
|
||||
status: 'fail',
|
||||
message: 'end-user data can not be modified with admin privileges'
|
||||
});
|
||||
}
|
||||
|
||||
api.use('/ServiceProviders', isAdmin, require('./service-providers'));
|
||||
|
||||
module.exports = api;
|
||||
28
lib/routes/api/service-providers.js
Normal file
28
lib/routes/api/service-providers.js
Normal file
@@ -0,0 +1,28 @@
|
||||
const router = require('express').Router();
|
||||
const ServiceProvider = require('../../models/service-provider');
|
||||
|
||||
function sysError(logger, res, err) {
|
||||
logger.error(err, 'Database error');
|
||||
res.status(500).end();
|
||||
}
|
||||
|
||||
/* return list of all service providers */
|
||||
router.get('/', async(req, res) => {
|
||||
const logger = req.app.locals.logger;
|
||||
logger.info(`user: ${JSON.stringify(req.user)}`);
|
||||
logger.info(`scope: ${JSON.stringify(req.authInfo.scope)}`);
|
||||
try {
|
||||
const results = await ServiceProvider.retrieveAll();
|
||||
res.status(200).json(results);
|
||||
} catch (err) {
|
||||
logger.error(err, 'Error retrieving service providers');
|
||||
sysError(logger, res, err);
|
||||
}
|
||||
});
|
||||
|
||||
/* add a service provider */
|
||||
router.post('/', (req, res) => {
|
||||
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
23
lib/routes/index.js
Normal file
23
lib/routes/index.js
Normal file
@@ -0,0 +1,23 @@
|
||||
const express = require('express');
|
||||
const swaggerUi = require('swagger-ui-express');
|
||||
const YAML = require('yamljs');
|
||||
const path = require('path');
|
||||
const swaggerDocument = YAML.load(path.resolve(__dirname, '../swagger/swagger.yaml'));
|
||||
const api = require('./api');
|
||||
|
||||
const routes = express.Router();
|
||||
|
||||
routes.use('/v1', api);
|
||||
routes.use('/swagger', swaggerUi.serve);
|
||||
routes.get('/swagger', swaggerUi.setup(swaggerDocument));
|
||||
|
||||
// health checks
|
||||
routes.get('/', (req, res) => {
|
||||
res.sendStatus(200);
|
||||
});
|
||||
|
||||
routes.get('/health', (req, res) => {
|
||||
res.sendStatus(200);
|
||||
});
|
||||
|
||||
module.exports = routes;
|
||||
895
lib/swagger/swagger.yaml
Normal file
895
lib/swagger/swagger.yaml
Normal file
@@ -0,0 +1,895 @@
|
||||
openapi: 3.0.0
|
||||
info:
|
||||
title: Jambones REST API
|
||||
description: Jambones REST API
|
||||
contact:
|
||||
email: daveh@drachtio.org
|
||||
license:
|
||||
name: MIT
|
||||
url: https://opensource.org/licenses/MIT
|
||||
version: 1.0.0
|
||||
servers:
|
||||
- url: /v1
|
||||
description: development server
|
||||
paths:
|
||||
/ServiceProviders:
|
||||
post:
|
||||
summary: create service provider
|
||||
operationId: createServiceProvider
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: service provider name
|
||||
description:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
responses:
|
||||
201:
|
||||
description: service provider successfully created
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
required:
|
||||
- serviceProviderSid
|
||||
properties:
|
||||
serviceProviderSid:
|
||||
type: string
|
||||
format: uuid
|
||||
example: 2531329f-fb09-4ef7-887e-84e648214436
|
||||
400:
|
||||
description: bad request
|
||||
409:
|
||||
description: an existing service provider already exists with this name
|
||||
get:
|
||||
summary: list service providers
|
||||
operationId: listServiceProviders
|
||||
responses:
|
||||
200:
|
||||
description: list of service providers
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/ServiceProvider'
|
||||
/ServiceProviders/{ServiceProviderSid}:
|
||||
parameters:
|
||||
- name: ServiceProviderSid
|
||||
in: path
|
||||
required: true
|
||||
style: simple
|
||||
explode: false
|
||||
schema:
|
||||
type: string
|
||||
delete:
|
||||
summary: delete a service provider
|
||||
operationId: deleteServiceProvider
|
||||
responses:
|
||||
200:
|
||||
description: service provider successfully deleted
|
||||
404:
|
||||
description: service provider not found
|
||||
409:
|
||||
description: service provider with active accounts can not be deleted
|
||||
get:
|
||||
summary: retrieve service provider
|
||||
operationId: getServiceProvider
|
||||
responses:
|
||||
200:
|
||||
description: service provider found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ServiceProvider'
|
||||
404:
|
||||
description: service provider not found
|
||||
put:
|
||||
summary: update service provider
|
||||
operationId: updateServiceProvider
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ServiceProvider'
|
||||
responses:
|
||||
200:
|
||||
description: service provider updated
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ServiceProvider'
|
||||
404:
|
||||
description: service provider not found
|
||||
/ServiceProviders/{ServiceProviderSid}/Accounts:
|
||||
parameters:
|
||||
- name: ServiceProviderSid
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
post:
|
||||
summary: create an account
|
||||
operationId: createAccount
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: account name
|
||||
example: foobar
|
||||
sipRealm:
|
||||
type: string
|
||||
description: sip realm for registration
|
||||
example: sip.mycompany.com
|
||||
registrationUrl:
|
||||
type: string
|
||||
format: url
|
||||
description: authentication webhook for registration
|
||||
example: https://mycompany.com
|
||||
required:
|
||||
- name
|
||||
responses:
|
||||
201:
|
||||
description: account successfully created
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
required:
|
||||
- accountSid
|
||||
properties:
|
||||
accountSid:
|
||||
type: string
|
||||
format: uuid
|
||||
example: 2531329f-fb09-4ef7-887e-84e648214436
|
||||
400:
|
||||
description: bad request
|
||||
409:
|
||||
description: account with this name already exists
|
||||
callbacks:
|
||||
onRegistrationAttempt:
|
||||
'{$request.body#/registrationUrl}/auth':
|
||||
post:
|
||||
requestBody:
|
||||
description: |
|
||||
provides details of the authentication request. The receiving server is responsible for authenticating the
|
||||
request as per [RFC 2617](https://tools.ietf.org/html/rfc2617)
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
required:
|
||||
- method
|
||||
- realm
|
||||
- username
|
||||
- expires
|
||||
- nonce
|
||||
- uri
|
||||
- response
|
||||
type: object
|
||||
properties:
|
||||
method:
|
||||
type: string
|
||||
description: sip request method
|
||||
example: REGISTER
|
||||
realm:
|
||||
type: string
|
||||
description: sip realm
|
||||
example: mycompany.com
|
||||
username:
|
||||
type: string
|
||||
description: sip username provided
|
||||
example: daveh
|
||||
expires:
|
||||
type: number
|
||||
description: expiration requested, in seconds
|
||||
example: 3600
|
||||
nonce:
|
||||
type: string
|
||||
description: nonce value
|
||||
example: InFriVGWVoKeCckYrTx7wg=="
|
||||
uri:
|
||||
type: string
|
||||
format: uri
|
||||
description: sip uri in request
|
||||
example: sip:mycompany.com
|
||||
algorithm:
|
||||
type: string
|
||||
description: encryption algorithm used, default to MD5 if not provided
|
||||
example: MD5
|
||||
qop:
|
||||
type: string
|
||||
description: qop value
|
||||
example: auth
|
||||
cnonce:
|
||||
type: string
|
||||
description: cnonce value
|
||||
example: 03d8d2aafd5a975f2b07dc90fe5f4100
|
||||
nc:
|
||||
type: string
|
||||
description: nc value
|
||||
example: 00000001
|
||||
response:
|
||||
type: string
|
||||
description: digest value calculated by the client
|
||||
example: db7b7dbec7edc0c427c1708031f67cc6
|
||||
responses:
|
||||
'200':
|
||||
description: |
|
||||
Your callback should return this HTTP status code in all cases.
|
||||
if the request was authenticated and you wish to admit
|
||||
the client to the network, this is indicated by setting the 'response'
|
||||
attribute in the body to 'ok'
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required:
|
||||
- response
|
||||
properties:
|
||||
response:
|
||||
type: string
|
||||
description: indicates whether the request was successfully authenticated
|
||||
enum:
|
||||
- ok
|
||||
- failed
|
||||
example: ok
|
||||
message:
|
||||
type: string
|
||||
description: a human-readable message
|
||||
example: authentication granted
|
||||
expires:
|
||||
type: number
|
||||
description: |
|
||||
The expires value to grant to the requesting user.
|
||||
If not provided, the expires value in the request is observed.
|
||||
If provided, must be less than the requested expires value.
|
||||
exileDuration:
|
||||
type: number
|
||||
description: |
|
||||
If provided, represents a period in seconds during which the source IP
|
||||
address will be blacklisted by the platform.
|
||||
|
||||
get:
|
||||
summary: list accounts for a service provider
|
||||
operationId: listAccounts
|
||||
responses:
|
||||
200:
|
||||
description: list of accounts
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Account'
|
||||
/ServiceProviders/{ServiceProviderSid}/Accounts/{AccountSid}:
|
||||
parameters:
|
||||
- name: ServiceProviderSid
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
- name: AccountSid
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
delete:
|
||||
summary: delete an account
|
||||
operationId: deleteAccount
|
||||
responses:
|
||||
200:
|
||||
description: account successfully deleted
|
||||
404:
|
||||
description: account not found
|
||||
409:
|
||||
description: account with applications or phone numbers can not be deleted
|
||||
|
||||
/VoipCarriers:
|
||||
post:
|
||||
summary: create voip carrier
|
||||
operationId: createVoipCarrier
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: voip carrier name
|
||||
description:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
responses:
|
||||
201:
|
||||
description: voip carrier successfully created
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
required:
|
||||
- voipCarrierSid
|
||||
properties:
|
||||
accountSid:
|
||||
type: string
|
||||
format: uuid
|
||||
example: 2531329f-fb09-4ef7-887e-84e648214436
|
||||
400:
|
||||
description: bad request
|
||||
409:
|
||||
description: an existing voip carrier already exists with this name
|
||||
get:
|
||||
summary: list voip carriers
|
||||
operationId: listVoipCarriers
|
||||
responses:
|
||||
200:
|
||||
description: list of voip carriers
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/VoipCarrier'
|
||||
/VoipCarriers/{VoipCarrierSid}:
|
||||
parameters:
|
||||
- name: VoipCarrierSid
|
||||
in: path
|
||||
required: true
|
||||
style: simple
|
||||
explode: false
|
||||
schema:
|
||||
type: string
|
||||
delete:
|
||||
summary: delete a voip carrier
|
||||
operationId: deleteVoipCarrier
|
||||
responses:
|
||||
200:
|
||||
description: voip carrier successfully deleted
|
||||
404:
|
||||
description: voip carrier not found
|
||||
409:
|
||||
description: voip carrier with active phone numbers can not be deleted
|
||||
get:
|
||||
summary: retrieve voip carrier
|
||||
operationId: getVoipCarrier
|
||||
responses:
|
||||
200:
|
||||
description: voip carrier found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/VoipCarrier'
|
||||
404:
|
||||
description: voip carrier not found
|
||||
put:
|
||||
summary: update voip carrier
|
||||
operationId: updateVoipCarrier
|
||||
parameters:
|
||||
- name: VoipCarrierSid
|
||||
in: path
|
||||
required: true
|
||||
style: simple
|
||||
explode: false
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/VoipCarrier'
|
||||
responses:
|
||||
200:
|
||||
description: voip carrier updated
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/VoipCarrier'
|
||||
404:
|
||||
description: voip carrier not found
|
||||
/VoipCarriers/{VoipCarrierSid}/PhoneNumbers:
|
||||
parameters:
|
||||
- name: VoipCarrierSid
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
post:
|
||||
summary: provision a phone number into inventory from a Voip Carrier
|
||||
operationId: provisionPhoneNumber
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
number:
|
||||
type: string
|
||||
description: telephone number
|
||||
description:
|
||||
type: string
|
||||
required:
|
||||
- number
|
||||
responses:
|
||||
201:
|
||||
description: phone number successfully provisioned into inventory
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
required:
|
||||
- phoneNumberSid
|
||||
properties:
|
||||
phoneNumberSid:
|
||||
type: string
|
||||
format: uuid
|
||||
example: 2531329f-fb09-4ef7-887e-84e648214436
|
||||
400:
|
||||
description: bad request
|
||||
409:
|
||||
description: the specified phone number already exists in inventory
|
||||
get:
|
||||
summary: list phone numbers for a carrier
|
||||
operationId: listProvisionedPhoneNumbers
|
||||
responses:
|
||||
200:
|
||||
description: list of phone numbers
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/PhoneNumber'
|
||||
|
||||
/Accounts/{AccountSid}/Applications:
|
||||
post:
|
||||
summary: create application
|
||||
operationId: createApplication
|
||||
parameters:
|
||||
- name: AccountSid
|
||||
in: path
|
||||
required: true
|
||||
style: simple
|
||||
explode: false
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: application name
|
||||
description:
|
||||
type: string
|
||||
statusUrl:
|
||||
type: string
|
||||
format: url
|
||||
description: webhook to pass call status updates to
|
||||
voiceUrl:
|
||||
type: string
|
||||
format: url
|
||||
description: webhook to call when call is received
|
||||
voiceFallbackUrl:
|
||||
type: string
|
||||
format: url
|
||||
description: fallback webook url
|
||||
required:
|
||||
- name
|
||||
- statusUrl
|
||||
- voiceUrl
|
||||
responses:
|
||||
201:
|
||||
description: application successfully created
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
required:
|
||||
- ApplicationSid
|
||||
properties:
|
||||
accountSid:
|
||||
type: string
|
||||
format: uuid
|
||||
example: 2531329f-fb09-4ef7-887e-84e648214436
|
||||
400:
|
||||
description: bad request
|
||||
409:
|
||||
description: an existing application already exists with this name
|
||||
get:
|
||||
summary: list applications for an account
|
||||
operationId: listApplications
|
||||
parameters:
|
||||
- name: AccountSid
|
||||
in: path
|
||||
required: true
|
||||
style: simple
|
||||
explode: false
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
200:
|
||||
description: retrieve applications for the account
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Application'
|
||||
/Accounts/{AccountSid}/Applications/{ApplicationSid}:
|
||||
parameters:
|
||||
- name: AccountSid
|
||||
in: path
|
||||
required: true
|
||||
style: simple
|
||||
explode: false
|
||||
schema:
|
||||
type: string
|
||||
- name: ApplicationSid
|
||||
in: path
|
||||
required: true
|
||||
style: simple
|
||||
explode: false
|
||||
schema:
|
||||
type: string
|
||||
delete:
|
||||
summary: delete an application
|
||||
operationId: deleteApplication
|
||||
responses:
|
||||
200:
|
||||
description: application successfully deleted
|
||||
404:
|
||||
description: application not found
|
||||
get:
|
||||
summary: retrieve application
|
||||
operationId: getApplication
|
||||
responses:
|
||||
200:
|
||||
description: application found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Application'
|
||||
404:
|
||||
description: application not found
|
||||
put:
|
||||
summary: update application
|
||||
operationId: updateApplication
|
||||
parameters:
|
||||
- name: AccountSid
|
||||
in: path
|
||||
required: true
|
||||
style: simple
|
||||
explode: false
|
||||
schema:
|
||||
type: string
|
||||
- name: ApplicationSid
|
||||
in: path
|
||||
required: true
|
||||
style: simple
|
||||
explode: false
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Application'
|
||||
responses:
|
||||
200:
|
||||
description: application updated
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Application'
|
||||
404:
|
||||
description: application not found
|
||||
/Accounts/{AccountSid}/Applications/{ApplicationSid}/PhoneNumbers/{PhoneNumberSid}:
|
||||
parameters:
|
||||
- name: AccountSid
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
- name: ApplicationSid
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
- name: PhoneNumberSid
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
delete:
|
||||
summary: remove a phone number from an application
|
||||
operationId: removePhoneNumberFromApplication
|
||||
responses:
|
||||
200:
|
||||
description: phone number removed from this application
|
||||
404:
|
||||
description: phone number not found or was not assigned to this application
|
||||
put:
|
||||
summary: provision a phone number for an Application
|
||||
operationId: assignPhoneNumberToApplication
|
||||
responses:
|
||||
200:
|
||||
description: phone number successfully assigned to application
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PhoneNumber'
|
||||
400:
|
||||
description: bad request
|
||||
409:
|
||||
description: the specified phone number is already assigned to another application or account
|
||||
|
||||
/Accounts/{AccountSid}/Apikeys:
|
||||
post:
|
||||
summary: create api key
|
||||
operationId: createApikey
|
||||
parameters:
|
||||
- name: AccountSid
|
||||
in: path
|
||||
required: true
|
||||
style: simple
|
||||
explode: false
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
201:
|
||||
description: api key successfully created
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
required:
|
||||
- apiKey
|
||||
- apiKeySid
|
||||
properties:
|
||||
apiKeySid:
|
||||
type: string
|
||||
description: system identifier for api key that was created
|
||||
format: uuid
|
||||
example: 7531328e-eb08-4eff-887e-84e648214872
|
||||
apiKey:
|
||||
type: string
|
||||
description: api key
|
||||
format: uuid
|
||||
example: 2531329f-fb09-4ef7-887e-84e648214436
|
||||
404:
|
||||
description: Account not found
|
||||
/Accounts/{AccountSid}/Apikeys/{ApiKeySid}:
|
||||
delete:
|
||||
summary: delete api key
|
||||
operationId: deleteApiKey
|
||||
parameters:
|
||||
- name: AccountSid
|
||||
in: path
|
||||
required: true
|
||||
style: simple
|
||||
explode: false
|
||||
schema:
|
||||
type: string
|
||||
- name: ApiKeySid
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
200:
|
||||
description: api key deleted
|
||||
404:
|
||||
description: api key or account not found
|
||||
|
||||
/Accounts/{AccountSid}/Applications/{ApplicationSid}/Calls:
|
||||
post:
|
||||
summary: create a call
|
||||
operationId: createCall
|
||||
parameters:
|
||||
- name: AccountSid
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- name: ApplicationSid
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
required:
|
||||
- from
|
||||
- to
|
||||
type: object
|
||||
properties:
|
||||
url:
|
||||
type: string
|
||||
description: |
|
||||
The url of the web application to control this call.
|
||||
If not provided, the url specified in the application will be used
|
||||
example: https://mycompany.com/deliver-message.json
|
||||
from:
|
||||
type: string
|
||||
description: The calling party number
|
||||
example: 16172375089
|
||||
to:
|
||||
type: string
|
||||
description: The telephone number or sip endpoint to call
|
||||
example: 16172228000
|
||||
recordingUrl:
|
||||
type: string
|
||||
format: url
|
||||
description: A websocket url to stream the call audio to
|
||||
example: wss://myserver.com
|
||||
recordingMix:
|
||||
type: string
|
||||
description: whether to record either or both parties
|
||||
enum:
|
||||
- caller
|
||||
- callee
|
||||
- stereo
|
||||
- mixed
|
||||
example: stereo
|
||||
statusCallback:
|
||||
type: string
|
||||
format: url
|
||||
description: The url to send call status change events to
|
||||
example: https://company.com/status
|
||||
responses:
|
||||
201:
|
||||
description: call successfully created
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
required:
|
||||
- callSid
|
||||
properties:
|
||||
callSid:
|
||||
type: string
|
||||
format: uuid
|
||||
example: 2531329f-fb09-4ef7-887e-84e648214436
|
||||
400:
|
||||
description: bad request
|
||||
|
||||
components:
|
||||
securitySchemes:
|
||||
bearerAuth:
|
||||
type: http
|
||||
scheme: bearer
|
||||
bearerFormat: token
|
||||
schemas:
|
||||
ServiceProvider:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
name:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
VoipCarrier:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
name:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
Account:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
sipRealm:
|
||||
type: string
|
||||
registrationHook:
|
||||
type: string
|
||||
format: url
|
||||
serviceProvider:
|
||||
$ref: '#/components/schemas/ServiceProvider'
|
||||
Application:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
name:
|
||||
type: string
|
||||
account:
|
||||
$ref: '#/components/schemas/Account'
|
||||
callHook:
|
||||
type: string
|
||||
format: url
|
||||
callBackupHook:
|
||||
type: string
|
||||
format: url
|
||||
callStatusChangeHook:
|
||||
type: string
|
||||
format: url
|
||||
PhoneNumber:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
number:
|
||||
type: string
|
||||
voipCarrier:
|
||||
$ref: '#/components/schemas/VoipCarrier'
|
||||
account:
|
||||
$ref: '#/components/schemas/Account'
|
||||
application:
|
||||
$ref: '#/components/schemas/Application'
|
||||
RegisteredUser:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
username:
|
||||
type: string
|
||||
domain:
|
||||
type: string
|
||||
Call:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
application:
|
||||
$ref: '#/components/schemas/Application'
|
||||
parentCall:
|
||||
$ref: '#/components/schemas/Call'
|
||||
direction:
|
||||
type: string
|
||||
enum:
|
||||
- inbound
|
||||
- outbound
|
||||
phoneNumber:
|
||||
$ref: '#/components/schemas/PhoneNumber'
|
||||
inboundUser:
|
||||
$ref: '#/components/schemas/RegisteredUser'
|
||||
outboundUser:
|
||||
$ref: '#/components/schemas/RegisteredUser'
|
||||
callingNumber:
|
||||
type: string
|
||||
calledNumber:
|
||||
type: string
|
||||
|
||||
security:
|
||||
- bearerAuth: []
|
||||
6
lib/utils/scrub-ids.js
Normal file
6
lib/utils/scrub-ids.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = function(tuples) {
|
||||
return tuples.map((t) => {
|
||||
delete t.id;
|
||||
return t;
|
||||
});
|
||||
};
|
||||
24
package.json
Normal file
24
package.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "jambones-api-server",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "app.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"body-parser": "^1.19.0",
|
||||
"config": "^3.2.4",
|
||||
"express": "^4.17.1",
|
||||
"mysql": "^2.17.1",
|
||||
"passport": "^0.4.0",
|
||||
"passport-http-bearer": "^1.0.1",
|
||||
"pino": "^5.14.0",
|
||||
"request": "^2.88.0",
|
||||
"request-debug": "^0.2.0",
|
||||
"swagger-ui-express": "^4.1.2",
|
||||
"yamljs": "^0.3.0"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user