mirror of
https://github.com/jambonz/jambonz-api-server.git
synced 2025-12-19 05:47:46 +00:00
revamped db schema and implemented some simple api
This commit is contained in:
4
app.js
4
app.js
@@ -19,5 +19,7 @@ app.use(bodyParser.urlencoded({ extended: true }));
|
||||
app.use(bodyParser.json());
|
||||
app.use('/v1', passport.authenticate('bearer', { session: false }));
|
||||
app.use('/', routes);
|
||||
|
||||
app.use((err, req, res, next) => {
|
||||
req.status(500).json({msg: err.message});
|
||||
});
|
||||
app.listen(PORT);
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/* SQLEditor (MySQL (2))*/
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `api_keys`;
|
||||
|
||||
DROP TABLE IF EXISTS `call_routes`;
|
||||
|
||||
DROP TABLE IF EXISTS `conference_participants`;
|
||||
@@ -17,13 +15,13 @@ 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 `registrations`;
|
||||
|
||||
DROP TABLE IF EXISTS `api_keys`;
|
||||
|
||||
DROP TABLE IF EXISTS `accounts`;
|
||||
|
||||
@@ -33,144 +31,92 @@ 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 ,
|
||||
`application_sid` 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`)
|
||||
`account_sid` CHAR(36) NOT NULL,
|
||||
`call_hook` VARCHAR(255) NOT NULL,
|
||||
`call_status_hook` VARCHAR(255) NOT NULL,
|
||||
PRIMARY KEY (`application_sid`)
|
||||
) 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,
|
||||
`call_route_sid` CHAR(36) NOT NULL UNIQUE ,
|
||||
`account_sid` CHAR(36) NOT NULL,
|
||||
`regex` VARCHAR(255) NOT NULL,
|
||||
`application_id` INTEGER(10) UNSIGNED NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
`application_sid` CHAR(36) NOT NULL,
|
||||
PRIMARY KEY (`call_route_sid`)
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `conferences`
|
||||
(
|
||||
`id` INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE ,
|
||||
`uuid` CHAR(36) NOT NULL UNIQUE ,
|
||||
`conference_sid` 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`)
|
||||
`conference_participant_sid` CHAR(36) NOT NULL UNIQUE ,
|
||||
`call_sid` CHAR(36),
|
||||
`conference_sid` CHAR(36) NOT NULL,
|
||||
PRIMARY KEY (`conference_participant_sid`)
|
||||
) 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 ,
|
||||
`phone_number_sid` CHAR(36) NOT NULL UNIQUE ,
|
||||
`number` VARCHAR(255) NOT NULL UNIQUE ,
|
||||
`account_id` INTEGER(10) UNSIGNED NOT NULL,
|
||||
`application_id` INTEGER(10) UNSIGNED,
|
||||
`account_sid` CHAR(36) NOT NULL,
|
||||
`application_sid` CHAR(36),
|
||||
`phone_number_inventory_id` INTEGER(10) UNSIGNED NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
PRIMARY KEY (`phone_number_sid`)
|
||||
) 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 ,
|
||||
`queue_sid` 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`
|
||||
CREATE TABLE IF NOT EXISTS `registrations`
|
||||
(
|
||||
`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 ,
|
||||
`registration_sid` 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`)
|
||||
PRIMARY KEY (`registration_sid`)
|
||||
) ENGINE=InnoDB COMMENT='An active sip registration';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `queue_members`
|
||||
(
|
||||
`queue_member_sid` CHAR(36) NOT NULL UNIQUE ,
|
||||
`call_sid` CHAR(36),
|
||||
`queue_sid` CHAR(36) NOT NULL,
|
||||
`position` INTEGER,
|
||||
PRIMARY KEY (`queue_member_sid`)
|
||||
) ENGINE=InnoDB COMMENT='A relationship between a call and a queue that it is waiting';
|
||||
|
||||
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,
|
||||
`call_sid` CHAR(36) NOT NULL UNIQUE ,
|
||||
`parent_call_sid` CHAR(36),
|
||||
`application_sid` CHAR(36),
|
||||
`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,
|
||||
`phone_number_sd` CHAR(36),
|
||||
`inbound_user_sid` CHAR(36),
|
||||
`outbound_user_sid` CHAR(36),
|
||||
`calling_number` VARCHAR(255),
|
||||
`called_number` VARCHAR(255),
|
||||
`caller_name` VARCHAR(255),
|
||||
@@ -189,96 +135,112 @@ CREATE TABLE IF NOT EXISTS `calls`
|
||||
`dest_address` VARCHAR(255),
|
||||
`dest_port` INTEGER UNSIGNED,
|
||||
`url` VARCHAR(255),
|
||||
PRIMARY KEY (`id`)
|
||||
PRIMARY KEY (`call_sid`)
|
||||
) 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 ,
|
||||
`service_provider_sid` CHAR(36) NOT NULL UNIQUE ,
|
||||
`name` VARCHAR(255) NOT NULL UNIQUE ,
|
||||
`description` VARCHAR(255),
|
||||
PRIMARY KEY (`id`)
|
||||
PRIMARY KEY (`service_provider_sid`)
|
||||
) ENGINE=InnoDB COMMENT='An organization that provides communication services to its ';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `accounts`
|
||||
CREATE TABLE IF NOT EXISTS `api_keys`
|
||||
(
|
||||
`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';
|
||||
`api_key_sid` CHAR(36) NOT NULL UNIQUE ,
|
||||
`token` CHAR(36) NOT NULL UNIQUE ,
|
||||
`account_sid` CHAR(36),
|
||||
`service_provider_sid` CHAR(36),
|
||||
PRIMARY KEY (`api_key_sid`)
|
||||
) ENGINE=InnoDB COMMENT='An authorization token that is used to access the REST api';
|
||||
|
||||
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,
|
||||
`subscription_sid` CHAR(36) NOT NULL UNIQUE ,
|
||||
`registration_sid` CHAR(36) NOT NULL,
|
||||
`event` VARCHAR(255),
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB COMMENT='An active sip subscription';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `accounts`
|
||||
(
|
||||
`account_sid` CHAR(36) NOT NULL UNIQUE ,
|
||||
`name` VARCHAR(255) NOT NULL,
|
||||
`sip_realm` VARCHAR(255),
|
||||
`service_provider_sid` CHAR(36) NOT NULL,
|
||||
`registration_hook` VARCHAR(255),
|
||||
PRIMARY KEY (`account_sid`)
|
||||
) ENGINE=InnoDB COMMENT='A single end-user of the platform';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `voip_carriers`
|
||||
(
|
||||
`id` INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE ,
|
||||
`uuid` CHAR(36) NOT NULL UNIQUE ,
|
||||
`voip_carrier_sid` CHAR(36) NOT NULL UNIQUE ,
|
||||
`name` VARCHAR(255) NOT NULL UNIQUE ,
|
||||
PRIMARY KEY (`id`)
|
||||
`description` VARCHAR(255),
|
||||
PRIMARY KEY (`voip_carrier_sid`)
|
||||
) 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 ,
|
||||
`phone_number_inventory_sid` CHAR(36) NOT NULL UNIQUE ,
|
||||
`number` VARCHAR(255) NOT NULL UNIQUE ,
|
||||
`voip_carrier_id` INTEGER(10) UNSIGNED NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
`voip_carrier_sid` CHAR(36) NOT NULL,
|
||||
PRIMARY KEY (`phone_number_inventory_sid`)
|
||||
) 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_application_sid_idx` ON `applications` (`application_sid`);
|
||||
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 `applications` ADD FOREIGN KEY account_sid_idxfk (`account_sid`) REFERENCES `accounts` (`account_sid`);
|
||||
|
||||
ALTER TABLE `call_routes` ADD FOREIGN KEY account_id_idxfk_2 (`account_id`) REFERENCES `accounts` (`id`);
|
||||
CREATE INDEX `call_routes_call_route_sid_idx` ON `call_routes` (`call_route_sid`);
|
||||
ALTER TABLE `call_routes` ADD FOREIGN KEY account_sid_idxfk_1 (`account_sid`) REFERENCES `accounts` (`account_sid`);
|
||||
|
||||
ALTER TABLE `call_routes` ADD FOREIGN KEY application_id_idxfk (`application_id`) REFERENCES `applications` (`id`);
|
||||
ALTER TABLE `call_routes` ADD FOREIGN KEY application_sid_idxfk (`application_sid`) REFERENCES `applications` (`application_sid`);
|
||||
|
||||
ALTER TABLE `conference_participants` ADD FOREIGN KEY call_id_idxfk (`call_id`) REFERENCES `calls` (`id`);
|
||||
CREATE INDEX `conferences_conference_sid_idx` ON `conferences` (`conference_sid`);
|
||||
CREATE INDEX `conference_participants_conference_participant_sid_idx` ON `conference_participants` (`conference_participant_sid`);
|
||||
ALTER TABLE `conference_participants` ADD FOREIGN KEY call_sid_idxfk (`call_sid`) REFERENCES `calls` (`call_sid`);
|
||||
|
||||
ALTER TABLE `conference_participants` ADD FOREIGN KEY conference_id_idxfk (`conference_id`) REFERENCES `conferences` (`id`);
|
||||
ALTER TABLE `conference_participants` ADD FOREIGN KEY conference_sid_idxfk (`conference_sid`) REFERENCES `conferences` (`conference_sid`);
|
||||
|
||||
ALTER TABLE `old_call` ADD FOREIGN KEY parent_call_id_idxfk (`parent_call_id`) REFERENCES `old_call` (`id`);
|
||||
CREATE INDEX `phone_numbers_phone_number_sid_idx` ON `phone_numbers` (`phone_number_sid`);
|
||||
ALTER TABLE `phone_numbers` ADD FOREIGN KEY account_sid_idxfk_2 (`account_sid`) REFERENCES `accounts` (`account_sid`);
|
||||
|
||||
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 application_sid_idxfk_1 (`application_sid`) REFERENCES `applications` (`application_sid`);
|
||||
|
||||
ALTER TABLE `phone_numbers` ADD FOREIGN KEY account_id_idxfk_3 (`account_id`) REFERENCES `accounts` (`id`);
|
||||
CREATE INDEX `queues_queue_sid_idx` ON `queues` (`queue_sid`);
|
||||
CREATE INDEX `registrations_registration_sid_idx` ON `registrations` (`registration_sid`);
|
||||
CREATE INDEX `queue_members_queue_member_sid_idx` ON `queue_members` (`queue_member_sid`);
|
||||
ALTER TABLE `queue_members` ADD FOREIGN KEY call_sid_idxfk_1 (`call_sid`) REFERENCES `calls` (`call_sid`);
|
||||
|
||||
ALTER TABLE `phone_numbers` ADD FOREIGN KEY application_id_idxfk_2 (`application_id`) REFERENCES `applications` (`id`);
|
||||
ALTER TABLE `queue_members` ADD FOREIGN KEY queue_sid_idxfk (`queue_sid`) REFERENCES `queues` (`queue_sid`);
|
||||
|
||||
ALTER TABLE `phone_numbers` ADD FOREIGN KEY phone_number_inventory_id_idxfk (`phone_number_inventory_id`) REFERENCES `phone_number_inventory` (`id`);
|
||||
CREATE INDEX `calls_call_sid_idx` ON `calls` (`call_sid`);
|
||||
ALTER TABLE `calls` ADD FOREIGN KEY parent_call_sid_idxfk (`parent_call_sid`) REFERENCES `calls` (`call_sid`);
|
||||
|
||||
ALTER TABLE `queue_members` ADD FOREIGN KEY call_id_idxfk_1 (`call_id`) REFERENCES `calls` (`id`);
|
||||
ALTER TABLE `calls` ADD FOREIGN KEY application_sid_idxfk_2 (`application_sid`) REFERENCES `applications` (`application_sid`);
|
||||
|
||||
ALTER TABLE `queue_members` ADD FOREIGN KEY queue_id_idxfk (`queue_id`) REFERENCES `queues` (`id`);
|
||||
ALTER TABLE `calls` ADD FOREIGN KEY phone_number_sd_idxfk (`phone_number_sd`) REFERENCES `phone_numbers` (`phone_number_sid`);
|
||||
|
||||
ALTER TABLE `calls` ADD FOREIGN KEY parent_call_id_idxfk_1 (`parent_call_id`) REFERENCES `calls` (`id`);
|
||||
ALTER TABLE `calls` ADD FOREIGN KEY inbound_user_sid_idxfk (`inbound_user_sid`) REFERENCES `registrations` (`registration_sid`);
|
||||
|
||||
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`);
|
||||
ALTER TABLE `calls` ADD FOREIGN KEY outbound_user_sid_idxfk (`outbound_user_sid`) REFERENCES `registrations` (`registration_sid`);
|
||||
|
||||
CREATE INDEX `service_providers_service_provider_sid_idx` ON `service_providers` (`service_provider_sid`);
|
||||
CREATE INDEX `service_providers_name_idx` ON `service_providers` (`name`);
|
||||
CREATE INDEX `api_keys_api_key_sid_idx` ON `api_keys` (`api_key_sid`);
|
||||
ALTER TABLE `api_keys` ADD FOREIGN KEY account_sid_idxfk_3 (`account_sid`) REFERENCES `accounts` (`account_sid`);
|
||||
|
||||
ALTER TABLE `api_keys` ADD FOREIGN KEY service_provider_sid_idxfk (`service_provider_sid`) REFERENCES `service_providers` (`service_provider_sid`);
|
||||
|
||||
ALTER TABLE `subscriptions` ADD FOREIGN KEY registration_sid_idxfk (`registration_sid`) REFERENCES `registrations` (`registration_sid`);
|
||||
|
||||
CREATE INDEX `accounts_account_sid_idx` ON `accounts` (`account_sid`);
|
||||
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 `accounts` ADD FOREIGN KEY service_provider_sid_idxfk_1 (`service_provider_sid`) REFERENCES `service_providers` (`service_provider_sid`);
|
||||
|
||||
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`);
|
||||
CREATE INDEX `voip_carriers_voip_carrier_sid_idx` ON `voip_carriers` (`voip_carrier_sid`);
|
||||
CREATE INDEX `phone_number_inventory_sid_idx` ON `phone_number_inventory` (`phone_number_inventory_sid`);
|
||||
ALTER TABLE `phone_number_inventory` ADD FOREIGN KEY voip_carrier_sid_idxfk (`voip_carrier_sid`) REFERENCES `voip_carriers` (`voip_carrier_sid`);
|
||||
|
||||
BIN
db/jambones.png
Normal file
BIN
db/jambones.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 310 KiB |
568
db/jambones.sqs
568
db/jambones.sqs
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,9 @@
|
||||
const Strategy = require('passport-http-bearer').Strategy;
|
||||
const {getMysqlConnection} = require('../db');
|
||||
const sql = `
|
||||
SELECT api_keys.uuid, accounts.uuid
|
||||
SELECT *
|
||||
FROM api_keys
|
||||
LEFT JOIN accounts
|
||||
ON api_keys.account_id = accounts.id`;
|
||||
WHERE api_keys.token = ?`;
|
||||
|
||||
function makeStrategy(logger) {
|
||||
return new Strategy(
|
||||
@@ -15,7 +14,7 @@ function makeStrategy(logger) {
|
||||
logger.error(err, 'Error retrieving mysql connection');
|
||||
return done(err);
|
||||
}
|
||||
conn.query({sql, nestTables: '_'}, [token], (err, results, fields) => {
|
||||
conn.query(sql, [token], (err, results, fields) => {
|
||||
conn.release();
|
||||
if (err) {
|
||||
logger.error(err, 'Error querying for api key');
|
||||
@@ -28,9 +27,20 @@ function makeStrategy(logger) {
|
||||
}
|
||||
|
||||
// found api key
|
||||
return done(null,
|
||||
{accountSid: results[0].accounts_uuid},
|
||||
{scope: results[0].accounts_uuid ? ['user'] : ['admin']});
|
||||
logger.info(results, 'api key lookup');
|
||||
const user = {
|
||||
account_sid: results[0].account_sid,
|
||||
service_provider_sid: results[0].service_provider_sid,
|
||||
isAdmin: results[0].account_sid === null && results[0].service_provider_sid === null,
|
||||
isServiceProvider: results[0].service_provider_sid !== null,
|
||||
isUser: results[0].account_sid != null
|
||||
};
|
||||
const scope = [];
|
||||
if (user.isAdmin) scope.push('admin');
|
||||
else if (user.isServiceProvider) scope.push('service_provider');
|
||||
else scope.push('user');
|
||||
logger.info(user, `successfully validated with scope ${scope}`);
|
||||
return done(null, user, {scope});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
31
lib/models/api-key.js
Normal file
31
lib/models/api-key.js
Normal file
@@ -0,0 +1,31 @@
|
||||
const Model = require('./model');
|
||||
|
||||
class ApiKey extends Model {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
ApiKey.table = 'api_keys';
|
||||
ApiKey.fields = [
|
||||
{
|
||||
name: 'api_key_sid',
|
||||
type: 'string',
|
||||
primaryKey: true
|
||||
},
|
||||
{
|
||||
name: 'token',
|
||||
type: 'string',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'account_sid',
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
name: 'service_provider_sid',
|
||||
type: 'string'
|
||||
}
|
||||
];
|
||||
|
||||
module.exports = ApiKey;
|
||||
141
lib/models/model.js
Normal file
141
lib/models/model.js
Normal file
@@ -0,0 +1,141 @@
|
||||
const Emitter = require('events');
|
||||
const uuidv4 = require('uuid/v4');
|
||||
const assert = require('assert');
|
||||
const {getMysqlConnection} = require('../db');
|
||||
const {DbErrorBadRequest} = require('../utils/errors');
|
||||
|
||||
class Model extends Emitter {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
static getPrimaryKey() {
|
||||
return this.fields.find((f) => f.primaryKey === true);
|
||||
}
|
||||
|
||||
/**
|
||||
* check validity of object to be inserted into db
|
||||
*/
|
||||
static checkIsInsertable(obj) {
|
||||
// check all required fields are present
|
||||
const required = this.fields.filter((f) => f.required === true);
|
||||
const missing = required.find((f) => !(f.name in obj));
|
||||
if (missing) throw new DbErrorBadRequest(`missing field ${missing.name}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* insert object into the database
|
||||
*/
|
||||
static make(obj) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const pk = this.getPrimaryKey();
|
||||
const uuid = uuidv4();
|
||||
obj[pk.name] = uuid;
|
||||
this.checkIsInsertable(obj);
|
||||
getMysqlConnection((err, conn) => {
|
||||
if (err) return reject(err);
|
||||
conn.query(`INSERT into ${this.table} SET ?`,
|
||||
obj,
|
||||
(err, results, fields) => {
|
||||
conn.release();
|
||||
if (err) return reject(err);
|
||||
resolve(uuid);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* delete object from database
|
||||
*/
|
||||
static remove(uuid) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const pk = this.getPrimaryKey();
|
||||
getMysqlConnection((err, conn) => {
|
||||
if (err) return reject(err);
|
||||
conn.query(`DELETE from ${this.table} WHERE ${pk.name} = ?`, uuid, (err, results) => {
|
||||
conn.release();
|
||||
if (err) return reject(err);
|
||||
resolve(results.affectedRows);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* retrieve all objects
|
||||
*/
|
||||
static retrieveAll() {
|
||||
return new Promise((resolve, reject) => {
|
||||
getMysqlConnection((err, conn) => {
|
||||
if (err) return reject(err);
|
||||
conn.query(`SELECT * from ${this.table}`, (err, results, fields) => {
|
||||
conn.release();
|
||||
if (err) return reject(err);
|
||||
resolve(results);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* retrieve a specific object
|
||||
*/
|
||||
static retrieve(sid) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const pk = this.getPrimaryKey();
|
||||
assert.ok(pk, 'field definitions must include the primary key');
|
||||
getMysqlConnection((err, conn) => {
|
||||
if (err) return reject(err);
|
||||
conn.query(`SELECT * from ${this.table} WHERE ${pk.name} = ?`, sid, (err, results, fields) => {
|
||||
conn.release();
|
||||
if (err) return reject(err);
|
||||
resolve(results);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* update an object
|
||||
*/
|
||||
static update(sid, obj) {
|
||||
const pk = this.getPrimaryKey();
|
||||
assert.ok(pk, 'field definitions must include the primary key');
|
||||
return new Promise((resolve, reject) => {
|
||||
if (pk.name in obj) throw new DbErrorBadRequest(`primary key ${pk.name} is immutable`);
|
||||
getMysqlConnection((err, conn) => {
|
||||
if (err) return reject(err);
|
||||
conn.query(`UPDATE ${this.table} SET ? WHERE ${pk.name} = '${sid}'`, obj, (err, results, fields) => {
|
||||
conn.release();
|
||||
if (err) return reject(err);
|
||||
resolve(results.affectedRows);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
static getForeignKeyReferences(fk, sid) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const arr = /(.*)\.(.*)/.exec(fk);
|
||||
assert.ok(arr, `foreign key must be written as table.column: ${fk}`);
|
||||
const table = arr[1];
|
||||
const column = arr[2];
|
||||
getMysqlConnection((err, conn) => {
|
||||
if (err) return reject(err);
|
||||
conn.query(`SELECT COUNT(*) as count from ${table} WHERE ${column} = ?`,
|
||||
sid, (err, results, fields) => {
|
||||
conn.release();
|
||||
if (err) return reject(err);
|
||||
resolve(results[0].count);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Model.table = 'subclassResponsibility';
|
||||
Model.fields = [];
|
||||
|
||||
module.exports = Model;
|
||||
@@ -1,23 +1,27 @@
|
||||
const Emitter = require('events');
|
||||
const {getMysqlConnection} = require('../db');
|
||||
const scrubIds = require('../utils/scrub-ids');
|
||||
const Model = require('./model');
|
||||
|
||||
class ServiceProvider extends Emitter {
|
||||
class ServiceProvider extends Model {
|
||||
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));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ServiceProvider.table = 'service_providers';
|
||||
ServiceProvider.fields = [
|
||||
{
|
||||
name: 'service_provider_sid',
|
||||
type: 'string',
|
||||
primaryKey: true
|
||||
},
|
||||
{
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
type: 'string'
|
||||
}
|
||||
];
|
||||
|
||||
module.exports = ServiceProvider;
|
||||
|
||||
80
lib/routes/api/api-keys.js
Normal file
80
lib/routes/api/api-keys.js
Normal file
@@ -0,0 +1,80 @@
|
||||
const router = require('express').Router();
|
||||
const {DbErrorBadRequest} = require('../../utils/errors');
|
||||
const ApiKey = require('../../models/api-key');
|
||||
const {isAdmin, isServiceProvider, isUser} = require('../../utils/scopes');
|
||||
const decorate = require('./decorate');
|
||||
const uuidv4 = require('uuid/v4');
|
||||
const assert = require('assert');
|
||||
const sysError = require('./error');
|
||||
const preconditions = {
|
||||
'add': validateAddToken,
|
||||
'delete': validateDeleteToken
|
||||
};
|
||||
|
||||
/**
|
||||
* if user scope, add to the associated account
|
||||
* if admin scope, only admin-level tokens may be created
|
||||
*/
|
||||
function validateAddToken(req) {
|
||||
if (isAdmin(req) && ('account_sid' in req.body)) {
|
||||
throw new DbErrorBadRequest('admin users may not create account-level tokens');
|
||||
}
|
||||
else if (isServiceProvider(req) && (!('account_sid' in req.body) && !('service_provider_sid' in req.body))) {
|
||||
req.body['service_provider_sid'] = req.user.service_provider_sid
|
||||
}
|
||||
else if (isUser(req)) {
|
||||
delete req.body['service_provider_sid'];
|
||||
req.body['account_sid'] = req.user.account_sid;
|
||||
}
|
||||
req.body.token = uuidv4();
|
||||
}
|
||||
|
||||
/**
|
||||
* admin users can only delete admin tokens or service provider tokens
|
||||
* service_provider users can delete service provider or user tokens
|
||||
* user-scope may only delete their own tokens
|
||||
*/
|
||||
async function validateDeleteToken(req, sid) {
|
||||
const results = await ApiKey.retrieve(sid);
|
||||
if (0 == results.length) return;
|
||||
if (isAdmin(req)) {
|
||||
if (results[0].account_sid) {
|
||||
throw new DbErrorBadRequest('an admin user may not delete account level api keys');
|
||||
}
|
||||
}
|
||||
else if (isServiceProvider(req)) {
|
||||
if (results[0].service_provider_sid === null && results[0].account_sid === null) {
|
||||
throw new DbErrorBadRequest('a service provider user may not delete an admin token');
|
||||
}
|
||||
if (results[0].service_provider_sid && results[0].service_provider_sid != req.user.service_provider_sid) {
|
||||
throw new DbErrorBadRequest('a service provider user may not delete api key from another service provider');
|
||||
}
|
||||
}
|
||||
else if (isUser(req)) {
|
||||
if (results[0].account_sid !== req.user.account_sid) {
|
||||
throw new DbErrorBadRequest('a user may not delete a token associated with a different account');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* need to handle here because response is slightly different than standard for an insert
|
||||
* (returning the token generated along with the sid)
|
||||
*/
|
||||
router.post('/', async(req, res) => {
|
||||
const logger = req.app.locals.logger;
|
||||
try {
|
||||
if ('add' in preconditions) {
|
||||
assert(typeof preconditions.add === 'function');
|
||||
await preconditions.add(req);
|
||||
}
|
||||
const uuid = await ApiKey.make(req.body);
|
||||
res.status(201).json({sid: uuid, token: req.body.token});
|
||||
} catch (err) {
|
||||
sysError(logger, res, err);
|
||||
}
|
||||
});
|
||||
|
||||
decorate(router, ApiKey, ['delete'], preconditions);
|
||||
|
||||
module.exports = router;
|
||||
101
lib/routes/api/decorate.js
Normal file
101
lib/routes/api/decorate.js
Normal file
@@ -0,0 +1,101 @@
|
||||
const assert = require('assert');
|
||||
const sysError = require('./error');
|
||||
|
||||
module.exports = decorate;
|
||||
|
||||
const decorators = {
|
||||
'list': list,
|
||||
'add': add,
|
||||
'retrieve': retrieve,
|
||||
'update': update,
|
||||
'delete': remove
|
||||
};
|
||||
|
||||
function decorate(router, klass, methods, preconditions) {
|
||||
const decs = methods && Array.isArray(methods) && methods[0] !== '*' ? methods : Object.keys(decorators);
|
||||
decs.forEach((m) => {
|
||||
assert(m in decorators);
|
||||
decorators[m](router, klass, preconditions);
|
||||
});
|
||||
}
|
||||
|
||||
function list(router, klass) {
|
||||
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 klass.retrieveAll();
|
||||
res.status(200).json(results);
|
||||
} catch (err) {
|
||||
sysError(logger, res, err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function add(router, klass, preconditions) {
|
||||
router.post('/', async(req, res) => {
|
||||
const logger = req.app.locals.logger;
|
||||
try {
|
||||
if ('add' in preconditions) {
|
||||
assert(typeof preconditions.add === 'function');
|
||||
await preconditions.add(req);
|
||||
}
|
||||
const uuid = await klass.make(req.body);
|
||||
res.status(201).json({sid: uuid});
|
||||
} catch (err) {
|
||||
sysError(logger, res, err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function retrieve(router, klass) {
|
||||
router.get('/:sid', async(req, res) => {
|
||||
const logger = req.app.locals.logger;
|
||||
try {
|
||||
const results = await klass.retrieve(req.params.sid);
|
||||
if (results.length === 0) return res.status(404).end();
|
||||
return res.status(200).json(results[0]);
|
||||
}
|
||||
catch (err) {
|
||||
sysError(logger, res, err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function update(router, klass) {
|
||||
router.put('/:sid', async(req, res) => {
|
||||
const sid = req.params.sid;
|
||||
const logger = req.app.locals.logger;
|
||||
try {
|
||||
const rowsAffected = await klass.update(sid, req.body);
|
||||
if (rowsAffected === 0) {
|
||||
return res.status(404).end();
|
||||
}
|
||||
res.status(204).end();
|
||||
} catch (err) {
|
||||
sysError(logger, res, err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function remove(router, klass, preconditions) {
|
||||
router.delete('/:sid', async(req, res) => {
|
||||
const sid = req.params.sid;
|
||||
const logger = req.app.locals.logger;
|
||||
try {
|
||||
if ('delete' in preconditions) {
|
||||
assert(typeof preconditions.delete === 'function');
|
||||
await preconditions.delete(req, sid);
|
||||
}
|
||||
const rowsAffected = await klass.remove(sid);
|
||||
if (rowsAffected === 0) {
|
||||
logger.info(`unable to delete ${klass.name} with sid ${sid}: not found`);
|
||||
return res.status(404).end();
|
||||
}
|
||||
res.status(204).end();
|
||||
} catch (err) {
|
||||
sysError(logger, res, err);
|
||||
}
|
||||
});
|
||||
}
|
||||
20
lib/routes/api/error.js
Normal file
20
lib/routes/api/error.js
Normal file
@@ -0,0 +1,20 @@
|
||||
const {DbErrorBadRequest, DbErrorUnprocessableRequest} = require('../../utils/errors');
|
||||
|
||||
function sysError(logger, res, err) {
|
||||
if (err instanceof DbErrorBadRequest) {
|
||||
logger.error(err, 'invalid client request');
|
||||
return res.status(400).json({msg: err.message});
|
||||
}
|
||||
if (err instanceof DbErrorUnprocessableRequest) {
|
||||
logger.error(err, 'unprocessable request');
|
||||
return res.status(422).json({msg: err.message});
|
||||
}
|
||||
if (err.message.includes('ER_DUP_ENTRY')) {
|
||||
logger.error(err, 'duplicate entry on insert');
|
||||
return res.status(422).json({msg: err.message});
|
||||
}
|
||||
logger.error(err, 'Database error');
|
||||
res.status(500).json({msg: err.message});
|
||||
}
|
||||
|
||||
module.exports = sysError;
|
||||
@@ -1,21 +1,15 @@
|
||||
const api = require('express').Router();
|
||||
const {isAdmin} = require('../../utils/scopes');
|
||||
|
||||
function isAdmin(req, res, next) {
|
||||
if (req.authInfo.scope.includes('admin')) return next();
|
||||
function isAdminScope(req, res, next) {
|
||||
if (isAdmin(req)) 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'));
|
||||
api.use('/ServiceProviders', isAdminScope, require('./service-providers'));
|
||||
api.use('/ApiKeys', require('./api-keys'));
|
||||
|
||||
module.exports = api;
|
||||
|
||||
@@ -1,28 +1,17 @@
|
||||
const router = require('express').Router();
|
||||
const {DbErrorUnprocessableRequest} = require('../../utils/errors');
|
||||
const ServiceProvider = require('../../models/service-provider');
|
||||
const decorate = require('./decorate');
|
||||
const preconditions = {
|
||||
'delete': noActiveAccounts
|
||||
};
|
||||
|
||||
function sysError(logger, res, err) {
|
||||
logger.error(err, 'Database error');
|
||||
res.status(500).end();
|
||||
/* can not delete a service provider if it has any active accounts */
|
||||
async function noActiveAccounts(req, sid) {
|
||||
const activeAccounts = await ServiceProvider.getForeignKeyReferences('accounts.service_provider_sid', sid);
|
||||
if (activeAccounts > 0) throw new DbErrorUnprocessableRequest('cannot delete service provider with active accounts');
|
||||
}
|
||||
|
||||
/* 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) => {
|
||||
|
||||
});
|
||||
decorate(router, ServiceProvider, ['*'], preconditions);
|
||||
|
||||
module.exports = router;
|
||||
|
||||
@@ -12,6 +12,58 @@ servers:
|
||||
- url: /v1
|
||||
description: development server
|
||||
paths:
|
||||
/Apikeys:
|
||||
post:
|
||||
summary: create an api key
|
||||
operationId: createApikey
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
service_provider_sid:
|
||||
type: string
|
||||
description: service provider scope for the generated api key
|
||||
account_sid:
|
||||
type: string
|
||||
description: account scope for the generated api key
|
||||
responses:
|
||||
201:
|
||||
description: api key successfully created
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/SuccessfulApiKeyAdd'
|
||||
400:
|
||||
description: bad request
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GeneralError'
|
||||
|
||||
500:
|
||||
description: bad request
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GeneralError'
|
||||
/Apikeys/{ApiKeySid}:
|
||||
parameters:
|
||||
- name: ApiKeySid
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
delete:
|
||||
summary: delete api key
|
||||
operationId: deleteApiKey
|
||||
responses:
|
||||
200:
|
||||
description: api key deleted
|
||||
404:
|
||||
description: api key or account not found
|
||||
|
||||
/ServiceProviders:
|
||||
post:
|
||||
summary: create service provider
|
||||
@@ -35,17 +87,14 @@ paths:
|
||||
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
|
||||
$ref: '#/components/schemas/SuccessfulAdd'
|
||||
422:
|
||||
description: unprocessable entity
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GeneralError'
|
||||
|
||||
get:
|
||||
summary: list service providers
|
||||
operationId: listServiceProviders
|
||||
@@ -71,12 +120,16 @@ paths:
|
||||
summary: delete a service provider
|
||||
operationId: deleteServiceProvider
|
||||
responses:
|
||||
200:
|
||||
204:
|
||||
description: service provider successfully deleted
|
||||
404:
|
||||
description: service provider not found
|
||||
409:
|
||||
description: service provider with active accounts can not be deleted
|
||||
422:
|
||||
description: unprocessable entity
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GeneralError'
|
||||
get:
|
||||
summary: retrieve service provider
|
||||
operationId: getServiceProvider
|
||||
@@ -98,14 +151,16 @@ paths:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ServiceProvider'
|
||||
responses:
|
||||
200:
|
||||
204:
|
||||
description: service provider updated
|
||||
404:
|
||||
description: service provider not found
|
||||
422:
|
||||
description: unprocessable entity
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ServiceProvider'
|
||||
404:
|
||||
description: service provider not found
|
||||
$ref: '#/components/schemas/GeneralError'
|
||||
/ServiceProviders/{ServiceProviderSid}/Accounts:
|
||||
parameters:
|
||||
- name: ServiceProviderSid
|
||||
@@ -640,8 +695,8 @@ paths:
|
||||
|
||||
/Accounts/{AccountSid}/Apikeys:
|
||||
post:
|
||||
summary: create api key
|
||||
operationId: createApikey
|
||||
summary: create an account level api key
|
||||
operationId: createAccountApikey
|
||||
parameters:
|
||||
- name: AccountSid
|
||||
in: path
|
||||
@@ -660,22 +715,22 @@ paths:
|
||||
- apiKey
|
||||
- apiKeySid
|
||||
properties:
|
||||
apiKeySid:
|
||||
api_key_sid:
|
||||
type: string
|
||||
description: system identifier for api key that was created
|
||||
format: uuid
|
||||
example: 7531328e-eb08-4eff-887e-84e648214872
|
||||
apiKey:
|
||||
token:
|
||||
type: string
|
||||
description: api key
|
||||
description: api key authorization token
|
||||
format: uuid
|
||||
example: 2531329f-fb09-4ef7-887e-84e648214436
|
||||
404:
|
||||
description: Account not found
|
||||
/Accounts/{AccountSid}/Apikeys/{ApiKeySid}:
|
||||
delete:
|
||||
summary: delete api key
|
||||
operationId: deleteApiKey
|
||||
summary: delete account-level api key
|
||||
operationId: deleteAccountApiKey
|
||||
parameters:
|
||||
- name: AccountSid
|
||||
in: path
|
||||
@@ -775,121 +830,171 @@ components:
|
||||
scheme: bearer
|
||||
bearerFormat: token
|
||||
schemas:
|
||||
SuccessfulApiKeyAdd:
|
||||
type: object
|
||||
required:
|
||||
- sid
|
||||
- token
|
||||
properties:
|
||||
sid:
|
||||
type: string
|
||||
token:
|
||||
type: string
|
||||
example:
|
||||
sid: 9d26a637-1679-471f-8da8-7150266e1254
|
||||
token: 589cead6-de24-4689-8ac3-08ffaf102811
|
||||
SuccessfulAdd:
|
||||
type: object
|
||||
required:
|
||||
- sid
|
||||
properties:
|
||||
sid:
|
||||
type: string
|
||||
example:
|
||||
sid: 9d26a637-1679-471f-8da8-7150266e1254
|
||||
GeneralError:
|
||||
type: object
|
||||
required:
|
||||
- msg
|
||||
properties:
|
||||
msg:
|
||||
type: string
|
||||
example:
|
||||
msg: cannot delete service provider with active accounts
|
||||
ServiceProvider:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
uuid:
|
||||
service_provider_sid:
|
||||
type: string
|
||||
format: uuid
|
||||
name:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
required:
|
||||
- service_provider_sid
|
||||
- name
|
||||
VoipCarrier:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
uuid:
|
||||
voip_carrier_sid:
|
||||
type: string
|
||||
format: uuid
|
||||
name:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
required:
|
||||
- voip_carrier_sid
|
||||
- name
|
||||
Account:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
uuid:
|
||||
account_sid:
|
||||
type: string
|
||||
format: uuid
|
||||
sipRealm:
|
||||
name:
|
||||
type: string
|
||||
registrationHook:
|
||||
sip_realm:
|
||||
type: string
|
||||
registration_hook:
|
||||
type: string
|
||||
format: url
|
||||
serviceProvider:
|
||||
service_provider:
|
||||
$ref: '#/components/schemas/ServiceProvider'
|
||||
required:
|
||||
- account_sid
|
||||
- name
|
||||
- service_provider
|
||||
Application:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
uuid:
|
||||
application_sid:
|
||||
type: string
|
||||
format: uuid
|
||||
name:
|
||||
type: string
|
||||
account:
|
||||
$ref: '#/components/schemas/Account'
|
||||
callHook:
|
||||
call_hook:
|
||||
type: string
|
||||
format: url
|
||||
callBackupHook:
|
||||
type: string
|
||||
format: url
|
||||
callStatusChangeHook:
|
||||
call_status_hook:
|
||||
type: string
|
||||
format: url
|
||||
required:
|
||||
- application_sid
|
||||
- name
|
||||
- account
|
||||
- call_hook
|
||||
- call_status_hook
|
||||
PhoneNumber:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
uuid:
|
||||
phone_number_sid:
|
||||
type: string
|
||||
format: uuid
|
||||
number:
|
||||
type: string
|
||||
voipCarrier:
|
||||
voip_carrier:
|
||||
$ref: '#/components/schemas/VoipCarrier'
|
||||
account:
|
||||
$ref: '#/components/schemas/Account'
|
||||
application:
|
||||
$ref: '#/components/schemas/Application'
|
||||
RegisteredUser:
|
||||
required:
|
||||
- phone_number_sid
|
||||
- number
|
||||
- voip_carrier
|
||||
- account
|
||||
Registration:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
uuid:
|
||||
registration_sid:
|
||||
type: string
|
||||
format: uuid
|
||||
username:
|
||||
type: string
|
||||
domain:
|
||||
type: string
|
||||
sip_contact:
|
||||
type: string
|
||||
sip_user_agent:
|
||||
type: string
|
||||
required:
|
||||
- registration_sid
|
||||
- username
|
||||
- domain
|
||||
- sip_contact
|
||||
Call:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
uuid:
|
||||
call_sid:
|
||||
type: string
|
||||
format: uuid
|
||||
application:
|
||||
$ref: '#/components/schemas/Application'
|
||||
parentCall:
|
||||
parent_call:
|
||||
$ref: '#/components/schemas/Call'
|
||||
direction:
|
||||
type: string
|
||||
enum:
|
||||
- inbound
|
||||
- outbound
|
||||
phoneNumber:
|
||||
phone_number:
|
||||
$ref: '#/components/schemas/PhoneNumber'
|
||||
inboundUser:
|
||||
inbound_user:
|
||||
$ref: '#/components/schemas/RegisteredUser'
|
||||
outboundUser:
|
||||
outbound_user:
|
||||
$ref: '#/components/schemas/RegisteredUser'
|
||||
callingNumber:
|
||||
calling_number:
|
||||
type: string
|
||||
calledNumber:
|
||||
called_number:
|
||||
type: string
|
||||
required:
|
||||
- call_sid
|
||||
- application
|
||||
- direction
|
||||
|
||||
security:
|
||||
- bearerAuth: []
|
||||
23
lib/utils/errors.js
Normal file
23
lib/utils/errors.js
Normal file
@@ -0,0 +1,23 @@
|
||||
class DbError extends Error {
|
||||
constructor(msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
class DbErrorBadRequest extends DbError {
|
||||
constructor(msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
class DbErrorUnprocessableRequest extends DbError {
|
||||
constructor(msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
DbError,
|
||||
DbErrorBadRequest,
|
||||
DbErrorUnprocessableRequest
|
||||
};
|
||||
17
lib/utils/scopes.js
Normal file
17
lib/utils/scopes.js
Normal file
@@ -0,0 +1,17 @@
|
||||
function isAdmin(req) {
|
||||
return req.authInfo.scope.includes('admin');
|
||||
}
|
||||
|
||||
function isServiceProvider(req) {
|
||||
return req.authInfo.scope.includes('service_provider');
|
||||
}
|
||||
|
||||
function isUser(req) {
|
||||
return req.authInfo.scope.includes('user');
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
isAdmin,
|
||||
isServiceProvider,
|
||||
isUser
|
||||
};
|
||||
@@ -1,6 +0,0 @@
|
||||
module.exports = function(tuples) {
|
||||
return tuples.map((t) => {
|
||||
delete t.id;
|
||||
return t;
|
||||
});
|
||||
};
|
||||
23
lib/utils/transforms.js
Normal file
23
lib/utils/transforms.js
Normal file
@@ -0,0 +1,23 @@
|
||||
function scrubIds(tuples) {
|
||||
return tuples.map((t) => {
|
||||
delete t.id;
|
||||
return t;
|
||||
});
|
||||
}
|
||||
|
||||
function rewriteKeys(tuples, obj) {
|
||||
return tuples.map((t) => {
|
||||
Object.keys(obj).forEach((k) => {
|
||||
if (k in t) {
|
||||
t[obj[k]] = t[k];
|
||||
delete t[k];
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
scrubIds,
|
||||
rewriteKeys
|
||||
};
|
||||
@@ -19,6 +19,7 @@
|
||||
"request": "^2.88.0",
|
||||
"request-debug": "^0.2.0",
|
||||
"swagger-ui-express": "^4.1.2",
|
||||
"uuid": "^3.3.3",
|
||||
"yamljs": "^0.3.0"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user