mirror of
https://github.com/jambonz/jambonz-api-server.git
synced 2026-01-25 02:08:24 +00:00
Merge a77d1d3052 into 48e1a72ef3
This commit is contained in:
@@ -17,8 +17,33 @@ insert into smpp_addresses (smpp_address_sid, ipv4, port, use_tls, is_primary)
|
||||
values('049078a0', '3.209.58.102', 3550, 1, 1);
|
||||
|
||||
-- create one service provider and account
|
||||
insert into api_keys (api_key_sid, token)
|
||||
values ('3f35518f-5a0d-4c2e-90a5-2407bb3b36f0', '38700987-c7a4-4685-a5bb-af378f9734de');
|
||||
insert into api_keys (api_key_sid, token, account_sid)
|
||||
('3f35518f-5a0d-4c2e-90a5-2407bb3b36fa', '38700987-c7a4-4685-a5bb-af378f9734da', '9351f46a-678c-43f5-b8a6-d4eb58d131af'),
|
||||
('9a9f220e-1c64-4aa4-a94f-4221b8486f11', '32c687eb-f57e-476a-bbec-cd20ec1d840d', '9351f46a-678c-43f5-b8a6-d4eb58d131af'),
|
||||
('417110d1-ab8c-48f9-9f3a-be69eff2c6f8', '04868c2c-f187-4555-b847-19fe64507566', '9351f46a-678c-43f5-b8a6-d4eb58d131af'),
|
||||
('a89fd59b-f98b-4682-9d43-d78b6b5a2adb', 'd95188e7-fbda-4e92-86a9-dd7e90e6ba99', '9351f46a-678c-43f5-b8a6-d4eb58d131af'),
|
||||
('7e3f0f53-fff7-48f7-bd7f-07087e94b83b', '67484932-3207-4199-b172-5b25cab73912', '9351f46a-678c-43f5-b8a6-d4eb58d131af'),
|
||||
('cddaf2c0-e2a2-46ce-89b9-8e172b1a7f34', 'f8c44bf2-010d-4150-b198-98a10101eb1a', '9351f46a-678c-43f5-b8a6-d4eb58d131af'),
|
||||
('6a312d2e-cdbc-47f5-83ea-f2e704be43d8', '30067c66-c55b-4e1f-a4a3-7b5674bfa7fb', '9351f46a-678c-43f5-b8a6-d4eb58d131af'),
|
||||
('53a3aef1-ef93-4c5c-b89e-b45360ebd087', 'ce1cde34-0f83-4179-8e1f-11452376e12b', '9351f46a-678c-43f5-b8a6-d4eb58d131af'),
|
||||
('14aa8867-aa08-480b-b1d1-7dea30e63ffa', 'eef78786-0e4b-4ed5-aa6a-2d5ef836cb87', '9351f46a-678c-43f5-b8a6-d4eb58d131af'),
|
||||
('5be9494c-132a-48d2-b928-576ab0fbc144', 'a8ac120e-1178-40a4-990e-c173d8bf97ba', '9351f46a-678c-43f5-b8a6-d4eb58d131af'),
|
||||
('a8df3d6a-1f51-4efd-8a95-4925e9d00fa7', '841c2cff-5d6b-4776-9fc8-b87190bc0c9f', '9351f46a-678c-43f5-b8a6-d4eb58d131af'),
|
||||
('16c62198-5f1e-4aff-bda7-5a0121ce8cb0', '04be0f91-3772-47ae-a467-9c29da3a8a79', '9351f46a-678c-43f5-b8a6-d4eb58d131af'),
|
||||
('ca96f1cf-0158-4b20-bfcc-60b5a3b96461', '105dedda-fa3c-4146-b51e-5ca253bb6b88', '9351f46a-678c-43f5-b8a6-d4eb58d131af'),
|
||||
('1db27613-e5c3-49b3-970e-edfd15f97ab0', 'ee4b03b4-bdec-4b37-8d36-3cdd70e4a93b', '9351f46a-678c-43f5-b8a6-d4eb58d131af'),
|
||||
('d2482a65-2895-4131-9818-34331d9a512e', 'e6f1e1ef-9d66-4cab-b047-5dcdddcc5b4a', '9351f46a-678c-43f5-b8a6-d4eb58d131af'),
|
||||
('88851e5e-3241-4cdc-bede-8cf0740e8d4c', 'ee6f22bd-59e9-453a-8bb2-909ec1e7204c', '9351f46a-678c-43f5-b8a6-d4eb58d131af'),
|
||||
('787e7aec-163f-4b48-8bca-bd96c435a06e', 'f131067b-587e-456a-b432-ae389b6ded2c', '9351f46a-678c-43f5-b8a6-d4eb58d131af'),
|
||||
('d883ef06-582d-40cb-92da-63b1831dd170', 'c25699d5-7bc6-4892-b589-f744ae47b3dc', '9351f46a-678c-43f5-b8a6-d4eb58d131af'),
|
||||
('bed6d98b-2912-46d1-8830-fd1c0659b3b7', '1aef8599-6043-455c-b009-21b2266db7e4', '9351f46a-678c-43f5-b8a6-d4eb58d131af'),
|
||||
('6f4d5047-d1f0-4df0-b5c3-032dae1e784b', '08a3eac4-c399-43ce-855f-f268e58bee30', '9351f46a-678c-43f5-b8a6-d4eb58d131af'),
|
||||
('1d689103-f04b-4cc1-85d2-99e48e4d6b2c', 'd858643f-3cd7-4efb-992f-d293b791c623', '9351f46a-678c-43f5-b8a6-d4eb58d131af'),
|
||||
('9b67593f-29bc-4651-8412-2ad94c982115', '7c2e0eb8-7ebb-45bc-b5d0-2eb78eddd67a', '9351f46a-678c-43f5-b8a6-d4eb58d131af'),
|
||||
('7b377326-9c1b-44ae-b3af-0702689c9f8f', '5a887940-39c1-4ad4-92b4-1099e548a5bf', '9351f46a-678c-43f5-b8a6-d4eb58d131af'),
|
||||
('7f306b65-de74-4e17-8d1d-602908fc823a', '61f0bd74-71cd-4a12-85eb-9a2ce423749d', '9351f46a-678c-43f5-b8a6-d4eb58d131af'),
|
||||
('c3277d25-5c06-4ee1-bfda-102fb69785e4', 'ece4b921-440c-452d-9747-5ae7cc8268e5', '9351f46a-678c-43f5-b8a6-d4eb58d131af'),
|
||||
('62f3a6d7-b930-46e9-9687-eea65e80eb1e', '83378993-1dac-4880-9a42-a49da150c533', '9351f46a-678c-43f5-b8a6-d4eb58d131af');
|
||||
|
||||
-- create one service provider and one account
|
||||
insert into service_providers (service_provider_sid, name, root_domain)
|
||||
|
||||
5
lib/config.js
Normal file
5
lib/config.js
Normal file
@@ -0,0 +1,5 @@
|
||||
const isPaginationEnabled = process.env.PAGINATION === 'true';
|
||||
|
||||
module.exports = {
|
||||
isPaginationEnabled,
|
||||
};
|
||||
@@ -55,6 +55,13 @@ WHERE account_sid = ?
|
||||
AND effective_end_date IS NULL
|
||||
AND pending = 0`;
|
||||
|
||||
const retrieveAllSqlCount = `SELECT COUNT(*) AS total_items
|
||||
FROM accounts acc
|
||||
LEFT JOIN webhooks AS rh
|
||||
ON acc.registration_hook_sid = rh.webhook_sid
|
||||
LEFT JOIN webhooks AS qh
|
||||
ON acc.queue_event_hook_sid = qh.webhook_sid`;
|
||||
|
||||
const extractBucketCredential = (obj) => {
|
||||
const {bucket_credential} = obj;
|
||||
if (bucket_credential) {
|
||||
@@ -107,10 +114,48 @@ class Account extends Model {
|
||||
sql = `${sql} WHERE acc.service_provider_sid = ?`;
|
||||
args.push(service_provider_sid);
|
||||
}
|
||||
return this.wrapPromise(sql, args);
|
||||
}
|
||||
|
||||
static async retrieveAllPaginated(service_provider_sid, account_sid, limit, page) {
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
const args = [];
|
||||
let sql = retrieveSql;
|
||||
let sqlCount = retrieveAllSqlCount;
|
||||
|
||||
if (account_sid) {
|
||||
sql = `${sql} WHERE acc.account_sid = ?`;
|
||||
sqlCount = `${sqlCount} WHERE acc.account_sid = ?`;
|
||||
args.push(account_sid);
|
||||
|
||||
} else if (service_provider_sid) {
|
||||
sql = `${sql} WHERE acc.service_provider_sid = ?`;
|
||||
sqlCount = `${sqlCount} WHERE acc.service_provider_sid = ?`;
|
||||
args.push(service_provider_sid);
|
||||
}
|
||||
|
||||
const [row] = await promisePool.query(sqlCount, args);
|
||||
const [{ total_items = 0 }] = row;
|
||||
const total_pages = Math.ceil(total_items / limit) || 1;
|
||||
|
||||
sql = `${sql} LIMIT ? OFFSET ?`;
|
||||
args.push(limit, offset);
|
||||
const data = await this.wrapPromise(sql, args);
|
||||
|
||||
return {
|
||||
total_items,
|
||||
total_pages,
|
||||
page,
|
||||
data,
|
||||
};
|
||||
}
|
||||
|
||||
static wrapPromise(sql, args) {
|
||||
return new Promise((resolve, reject) => {
|
||||
getMysqlConnection((err, conn) => {
|
||||
if (err) return reject(err);
|
||||
conn.query({sql, nestTables: true}, args, (err, results, fields) => {
|
||||
conn.query({ sql, nestTables: true }, args, (err, results, fields) => {
|
||||
conn.release();
|
||||
if (err) return reject(err);
|
||||
const r = transmogrifyResults(results);
|
||||
@@ -130,17 +175,7 @@ class Account extends Model {
|
||||
sql = `${retrieveSql} WHERE acc.account_sid = ? AND acc.service_provider_sid = ?`;
|
||||
args.push(service_provider_sid);
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
getMysqlConnection((err, conn) => {
|
||||
if (err) return reject(err);
|
||||
conn.query({sql, nestTables: true}, args, (err, results, fields) => {
|
||||
conn.release();
|
||||
if (err) return reject(err);
|
||||
const r = transmogrifyResults(results);
|
||||
resolve(r);
|
||||
});
|
||||
});
|
||||
});
|
||||
return this.wrapPromise(sql, args);
|
||||
}
|
||||
|
||||
static async updateStripeCustomerId(sid, customerId) {
|
||||
@@ -248,6 +283,16 @@ class Account extends Model {
|
||||
}));
|
||||
return account_subscription_sid;
|
||||
}
|
||||
|
||||
static async isPropertyAvailable(property, args) {
|
||||
const sql = `SELECT *
|
||||
FROM accounts acc
|
||||
WHERE acc.service_provider_sid = ?
|
||||
AND acc.${property} = ?`;
|
||||
|
||||
const result = await this.wrapPromise(sql, args);
|
||||
return result.length === 0;
|
||||
}
|
||||
}
|
||||
|
||||
Account.table = 'accounts';
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
const Model = require('./model');
|
||||
const {getMysqlConnection} = require('../db');
|
||||
|
||||
const sqlCountSP = 'SELECT COUNT(*) AS total_items from api_keys WHERE service_provider_sid = ?';
|
||||
const sqlCountAcc = 'SELECT COUNT(*) AS total_items from api_keys WHERE account_sid = ?';
|
||||
|
||||
class ApiKey extends Model {
|
||||
constructor() {
|
||||
super();
|
||||
@@ -15,6 +18,31 @@ class ApiKey extends Model {
|
||||
'SELECT * from api_keys WHERE account_sid IS NULL';
|
||||
const args = account_sid ? [account_sid] : [];
|
||||
|
||||
return this.wrapPromise(sql, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* list all api keys for an account paginated
|
||||
*/
|
||||
static async retrieveAllPaginated(account_sid, limit, page) {
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
const [{ total_items = 0 }] = await this.wrapPromise(sqlCountAcc, [account_sid]);
|
||||
const total_pages = Math.ceil(total_items / limit) || 1;
|
||||
|
||||
const sql = 'SELECT * from api_keys WHERE account_sid = ? LIMIT ? OFFSET ?';
|
||||
const args = [account_sid, limit, offset];
|
||||
const data = await this.wrapPromise(sql, args);
|
||||
|
||||
return {
|
||||
total_items,
|
||||
total_pages,
|
||||
page,
|
||||
data
|
||||
};
|
||||
}
|
||||
|
||||
static wrapPromise(sql, args) {
|
||||
return new Promise((resolve, reject) => {
|
||||
getMysqlConnection((err, conn) => {
|
||||
if (err) return reject(err);
|
||||
@@ -30,20 +58,31 @@ class ApiKey extends Model {
|
||||
/**
|
||||
* list all api keys for a service provider
|
||||
*/
|
||||
static retrieveAllForSP(service_provider_sid) {
|
||||
static async retrieveAllForSP(service_provider_sid) {
|
||||
const sql = 'SELECT * from api_keys WHERE service_provider_sid = ?';
|
||||
const args = [service_provider_sid];
|
||||
return this.wrapPromise(sql, args);
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
getMysqlConnection((err, conn) => {
|
||||
if (err) return reject(err);
|
||||
conn.query(sql, args, (err, results) => {
|
||||
conn.release();
|
||||
if (err) return reject(err);
|
||||
resolve(results);
|
||||
});
|
||||
});
|
||||
});
|
||||
/**
|
||||
* list all api keys for a service provider paginated
|
||||
*/
|
||||
static async retrieveAllForSPPaginated(service_provider_sid, limit = 25, page = 1) {
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
const [{total_items = 0}] = await this.wrapPromise(sqlCountSP, [service_provider_sid]);
|
||||
const total_pages = Math.ceil(total_items / limit) || 1;
|
||||
|
||||
const sql = 'SELECT * from api_keys WHERE service_provider_sid = ? LIMIT ? OFFSET ?';
|
||||
const args = [service_provider_sid, limit, offset];
|
||||
const data = await this.wrapPromise(sql, args);
|
||||
|
||||
return {
|
||||
total_items,
|
||||
total_pages,
|
||||
page,
|
||||
data
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,16 +92,7 @@ class ApiKey extends Model {
|
||||
const sql = 'UPDATE api_keys SET last_used = NOW() WHERE account_sid = ?';
|
||||
const args = [account_sid];
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
getMysqlConnection((err, conn) => {
|
||||
if (err) return reject(err);
|
||||
conn.query(sql, args, (err, results) => {
|
||||
conn.release();
|
||||
if (err) return reject(err);
|
||||
resolve(results);
|
||||
});
|
||||
});
|
||||
});
|
||||
return this.wrapPromise(sql, args);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
const Model = require('./model');
|
||||
const {getMysqlConnection} = require('../db');
|
||||
const { promisePool } = require('../db');
|
||||
|
||||
const retrieveSql = `SELECT * from applications app
|
||||
LEFT JOIN webhooks AS ch
|
||||
@@ -9,6 +10,15 @@ ON app.call_status_hook_sid = sh.webhook_sid
|
||||
LEFT JOIN webhooks AS mh
|
||||
ON app.messaging_hook_sid = mh.webhook_sid`;
|
||||
|
||||
const retrieveAllSqlCount = `SELECT COUNT(*) as total_items
|
||||
FROM applications app
|
||||
LEFT JOIN webhooks AS ch
|
||||
ON app.call_hook_sid = ch.webhook_sid
|
||||
LEFT JOIN webhooks AS sh
|
||||
ON app.call_status_hook_sid = sh.webhook_sid
|
||||
LEFT JOIN webhooks AS mh
|
||||
ON app.messaging_hook_sid = mh.webhook_sid`;
|
||||
|
||||
function transmogrifyResults(results) {
|
||||
return results.map((row) => {
|
||||
const obj = row.app;
|
||||
@@ -50,6 +60,44 @@ class Application extends Model {
|
||||
sql = `${sql} WHERE account_sid in (SELECT account_sid from accounts WHERE service_provider_sid = ?)`;
|
||||
args.push(service_provider_sid);
|
||||
}
|
||||
return this.wrapPromise(sql, args);
|
||||
}
|
||||
|
||||
static async retrieveAllPaginated(service_provider_sid, account_sid, limit, page) {
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
const args = [];
|
||||
let sql = retrieveSql;
|
||||
let sqlCount = retrieveAllSqlCount;
|
||||
|
||||
if (account_sid) {
|
||||
sql = `${sql} WHERE app.account_sid = ?`;
|
||||
sqlCount = `${sqlCount} WHERE app.account_sid = ?`;
|
||||
args.push(account_sid);
|
||||
|
||||
} else if (service_provider_sid) {
|
||||
sql = `${sql} WHERE account_sid in (SELECT account_sid from accounts WHERE service_provider_sid = ?)`;
|
||||
sqlCount = `${sqlCount} WHERE account_sid in (SELECT account_sid from accounts WHERE service_provider_sid = ?)`;
|
||||
args.push(service_provider_sid);
|
||||
}
|
||||
|
||||
const [row] = await promisePool.query(sqlCount, args);
|
||||
const [{total_items = 0}] = row;
|
||||
const total_pages = Math.ceil(total_items / limit) || 1;
|
||||
|
||||
sql = `${sql} LIMIT ? OFFSET ?`;
|
||||
args.push(limit, offset);
|
||||
const data = await this.wrapPromise(sql, args);
|
||||
|
||||
return {
|
||||
total_items,
|
||||
total_pages,
|
||||
page,
|
||||
data,
|
||||
};
|
||||
}
|
||||
|
||||
static wrapPromise(sql, args) {
|
||||
return new Promise((resolve, reject) => {
|
||||
getMysqlConnection((err, conn) => {
|
||||
if (err) return reject(err);
|
||||
@@ -77,17 +125,7 @@ class Application extends Model {
|
||||
sql = `${sql} AND account_sid in (SELECT account_sid from accounts WHERE service_provider_sid = ?)`;
|
||||
args.push(service_provider_sid);
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
getMysqlConnection((err, conn) => {
|
||||
if (err) return reject(err);
|
||||
conn.query({sql, nestTables: true}, args, (err, results, fields) => {
|
||||
conn.release();
|
||||
if (err) return reject(err);
|
||||
const r = transmogrifyResults(results);
|
||||
resolve(r);
|
||||
});
|
||||
});
|
||||
});
|
||||
return this.wrapPromise(sql, args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ class LcrCarrierSetEntry extends Model {
|
||||
}
|
||||
|
||||
static async retrieveAllByLcrRouteSid(sid) {
|
||||
const sql = `SELECT * FROM ${this.table} WHERE lcr_route_sid = ? ORDER BY priority`;
|
||||
const sql = `(SELECT * FROM ${this.table} WHERE lcr_route_sid = ?) ORDER BY priority`;
|
||||
const [rows] = await promisePool.query(sql, sid);
|
||||
return rows;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ class LcrRoutes extends Model {
|
||||
}
|
||||
|
||||
static async retrieveAllByLcrSid(sid) {
|
||||
const sql = `SELECT * FROM ${this.table} WHERE lcr_sid = ? ORDER BY priority`;
|
||||
const sql = `(SELECT * FROM ${this.table} WHERE lcr_sid = ?) ORDER BY priority`;
|
||||
const [rows] = await promisePool.query(sql, sid);
|
||||
return rows;
|
||||
}
|
||||
@@ -24,6 +24,25 @@ class LcrRoutes extends Model {
|
||||
const [rows] = await promisePool.query(sql, sid);
|
||||
return rows.length ? rows[0].count : 0;
|
||||
}
|
||||
|
||||
static async retrieveAllByLcrSidPaginated(sid, limit, page) {
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
const sql = `(SELECT * FROM ${this.table} WHERE lcr_sid = ? LIMIT ? OFFSET ? ) ORDER BY priority`;
|
||||
const [rows] = await promisePool.query(sql, [sid, limit, offset]);
|
||||
|
||||
const countSql = `SELECT COUNT(*) AS total_items FROM ${this.table} WHERE lcr_sid = ?`;
|
||||
const [row] = await promisePool.query(countSql, [sid]);
|
||||
const [{ total_items = 0 }] = row;
|
||||
const total_pages = Math.ceil(total_items / limit) || 1;
|
||||
|
||||
return {
|
||||
total_items,
|
||||
total_pages,
|
||||
page,
|
||||
data: rows,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
LcrRoutes.table = 'lcr_routes';
|
||||
|
||||
@@ -6,18 +6,86 @@ class Lcr extends Model {
|
||||
super();
|
||||
}
|
||||
|
||||
static async countAll() {
|
||||
const countSql = `SELECT COUNT(*) AS total_items FROM ${this.table}`;
|
||||
const [row] = await promisePool.query(countSql);
|
||||
const [{ total_items = 0 }] = row;
|
||||
return total_items;
|
||||
}
|
||||
|
||||
static async retrieveAllPaginated(limit, page) {
|
||||
const offset = (page - 1) * limit;
|
||||
const data = await super.retrieveAllPaginated([limit, offset]);
|
||||
|
||||
const total_items = await this.countAll();
|
||||
const total_pages = Math.ceil(total_items / limit) || 1;
|
||||
|
||||
return {
|
||||
total_items,
|
||||
total_pages,
|
||||
page,
|
||||
data,
|
||||
};
|
||||
}
|
||||
|
||||
static async retrieveAllByAccountSid(account_sid) {
|
||||
const sql = `SELECT * FROM ${this.table} WHERE account_sid = ?`;
|
||||
const [rows] = await promisePool.query(sql, account_sid);
|
||||
return rows;
|
||||
}
|
||||
|
||||
static async countAcc(sid) {
|
||||
const countSql = `SELECT COUNT(*) AS total_items FROM ${this.table} WHERE account_sid = ?`;
|
||||
const [row] = await promisePool.query(countSql, [sid]);
|
||||
const [{ total_items = 0 }] = row;
|
||||
return total_items;
|
||||
}
|
||||
|
||||
static async retrieveAllByAccountSidPaginated(sid, limit, page) {
|
||||
const offset = (page - 1) * limit;
|
||||
const sql = `SELECT * FROM ${this.table} WHERE account_sid = ? LIMIT ? OFFSET ?`;
|
||||
const [rows] = await promisePool.query(sql, [sid, limit, offset]);
|
||||
|
||||
const total_items = await this.countAcc(sid);
|
||||
const total_pages = Math.ceil(total_items / limit) || 1;
|
||||
|
||||
return {
|
||||
total_items,
|
||||
total_pages,
|
||||
page,
|
||||
data: rows,
|
||||
};
|
||||
}
|
||||
|
||||
static async retrieveAllByServiceProviderSid(sid) {
|
||||
const sql = `SELECT * FROM ${this.table} WHERE service_provider_sid = ?`;
|
||||
const [rows] = await promisePool.query(sql, sid);
|
||||
return rows;
|
||||
}
|
||||
|
||||
static async countSP(sid) {
|
||||
const countSql = `SELECT COUNT(*) AS total_items FROM ${this.table} WHERE service_provider_sid = ?`;
|
||||
const [row] = await promisePool.query(countSql, [sid]);
|
||||
const [{ total_items = 0 }] = row;
|
||||
return total_items;
|
||||
}
|
||||
|
||||
static async retrieveAllByServiceProviderSidPaginated(sid, limit, page) {
|
||||
const offset = (page - 1) * limit;
|
||||
const sql = `SELECT * FROM ${this.table} WHERE account_sid = ? LIMIT ? OFFSET ?`;
|
||||
const [rows] = await promisePool.query(sql, [sid, limit, offset]);
|
||||
|
||||
const total_items = await this.countSP(sid);
|
||||
const total_pages = Math.ceil(total_items / limit) || 1;
|
||||
|
||||
return {
|
||||
total_items,
|
||||
total_pages,
|
||||
page,
|
||||
data: rows,
|
||||
};
|
||||
}
|
||||
|
||||
static async releaseDefaultEntry(sid) {
|
||||
const sql = `UPDATE ${this.table} SET default_carrier_set_entry_sid = null WHERE lcr_sid = ?`;
|
||||
const [rows] = await promisePool.query(sql, sid);
|
||||
|
||||
@@ -79,6 +79,22 @@ class Model extends Emitter {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* retrieve all objects paginated
|
||||
*/
|
||||
static retrieveAllPaginated(args) {
|
||||
return new Promise((resolve, reject) => {
|
||||
getMysqlConnection((err, conn) => {
|
||||
if (err) return reject(err);
|
||||
conn.query(`SELECT * from ${this.table} LIMIT ? OFFSET ?`, args, (err, results, fields) => {
|
||||
conn.release();
|
||||
if (err) return reject(err);
|
||||
resolve(results);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* retrieve a specific object
|
||||
*/
|
||||
|
||||
@@ -10,6 +10,21 @@ WHERE account_sid IN
|
||||
FROM accounts
|
||||
WHERE service_provider_sid = ?
|
||||
) ORDER BY number`;
|
||||
const retrieveSqlCount = `SELECT COUNT(*) AS total_items
|
||||
FROM phone_numbers pn`;
|
||||
|
||||
const retrieveSqlCountForAcc = `SELECT COUNT(*) AS total_items
|
||||
FROM phone_numbers pn
|
||||
WHERE pn.account_sid = ?`;
|
||||
|
||||
const retrieveSqlCountForSP = `SELECT COUNT(*) as total_items
|
||||
FROM phone_numbers
|
||||
WHERE account_sid IN
|
||||
(
|
||||
SELECT account_sid
|
||||
FROM accounts
|
||||
WHERE service_provider_sid = ?
|
||||
) ORDER BY number`;
|
||||
|
||||
class PhoneNumber extends Model {
|
||||
constructor() {
|
||||
@@ -21,11 +36,70 @@ class PhoneNumber extends Model {
|
||||
const [rows] = await promisePool.query(sqlRetrieveAll, account_sid);
|
||||
return rows;
|
||||
}
|
||||
|
||||
static async retrieveAllPaginated(account_sid, limit, page) {
|
||||
const offset = (page - 1) * limit;
|
||||
let data;
|
||||
const countArgs = [];
|
||||
let countSql;
|
||||
if (!account_sid) {
|
||||
data = await super.retrieveAllPaginated([limit, offset]);
|
||||
countSql = retrieveSqlCount;
|
||||
} else {
|
||||
countSql = retrieveSqlCountForAcc;
|
||||
countArgs.push(account_sid);
|
||||
const sql = `${sqlRetrieveAll} LIMIT ? OFFSET ?`;
|
||||
const [rows] = await promisePool.query(sql, [account_sid, limit, offset]);
|
||||
data = rows;
|
||||
}
|
||||
|
||||
const [row] = await promisePool.query(countSql, countArgs);
|
||||
const [{ total_items = 0 }] = row;
|
||||
const total_pages = Math.ceil(total_items / limit) || 1;
|
||||
|
||||
return {
|
||||
total_items,
|
||||
total_pages,
|
||||
page,
|
||||
data,
|
||||
};
|
||||
}
|
||||
|
||||
static async retrieveAllForSP(service_provider_sid) {
|
||||
const [rows] = await promisePool.query(sqlSP, service_provider_sid);
|
||||
return rows;
|
||||
}
|
||||
|
||||
|
||||
static async retrieveAllForSPPaginated(service_provider_sid, limit, page) {
|
||||
const offset = (page - 1) * limit;
|
||||
let data;
|
||||
const countArgs = [];
|
||||
let countSql;
|
||||
if (!service_provider_sid) {
|
||||
data = await super.retrieveAllPaginated([limit, offset]);
|
||||
countSql = retrieveSqlCount;
|
||||
|
||||
} else {
|
||||
countArgs.push(service_provider_sid);
|
||||
countSql = retrieveSqlCountForSP;
|
||||
const sql = `${sqlSP} LIMIT ? OFFSET ?`;
|
||||
const [rows] = await promisePool.query(sql, [service_provider_sid, limit, offset]);
|
||||
data = rows;
|
||||
}
|
||||
|
||||
const [row] = await promisePool.query(countSql, countArgs);
|
||||
const [{ total_items = 0 }] = row;
|
||||
const total_pages = Math.ceil(total_items / limit) || 1;
|
||||
|
||||
return {
|
||||
total_items,
|
||||
total_pages,
|
||||
page,
|
||||
data,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* retrieve a phone number
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const Model = require('./model');
|
||||
const {getMysqlConnection} = require('../db');
|
||||
const {getMysqlConnection, promisePool} = require('../db');
|
||||
|
||||
const retrieveSql = `SELECT * from service_providers sp
|
||||
LEFT JOIN webhooks AS rh
|
||||
@@ -29,25 +29,10 @@ class ServiceProvider extends Model {
|
||||
*/
|
||||
static retrieveAll() {
|
||||
const sql = retrieveSql;
|
||||
return new Promise((resolve, reject) => {
|
||||
getMysqlConnection((err, conn) => {
|
||||
if (err) return reject(err);
|
||||
conn.query({sql, nestTables: true}, [], (err, results, fields) => {
|
||||
conn.release();
|
||||
if (err) return reject(err);
|
||||
const r = transmogrifyResults(results);
|
||||
resolve(r);
|
||||
});
|
||||
});
|
||||
});
|
||||
return this.wrapPromise(sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* retrieve a service provider
|
||||
*/
|
||||
static retrieve(sid) {
|
||||
const args = [sid];
|
||||
const sql = `${retrieveSql} WHERE sp.service_provider_sid = ?`;
|
||||
static async wrapPromise(sql, args = []) {
|
||||
return new Promise((resolve, reject) => {
|
||||
getMysqlConnection((err, conn) => {
|
||||
if (err) return reject(err);
|
||||
@@ -60,6 +45,34 @@ class ServiceProvider extends Model {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
static async retrieveAllPaginated(limit, page) {
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
const sql = `${retrieveSql} LIMIT ? OFFSET ?`;
|
||||
const data = await this.wrapPromise(sql, [limit, offset]);
|
||||
|
||||
const countSql = 'SELECT COUNT(*) as total_items from service_providers sp';
|
||||
const [row] = await promisePool.query(countSql, []);
|
||||
const [{ total_items = 0 }] = row;
|
||||
const total_pages = Math.ceil(total_items / limit) || 1;
|
||||
|
||||
return {
|
||||
total_items,
|
||||
total_pages,
|
||||
page,
|
||||
data,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* retrieve a service provider
|
||||
*/
|
||||
static retrieve(sid) {
|
||||
const args = [sid];
|
||||
const sql = `${retrieveSql} WHERE sp.service_provider_sid = ?`;
|
||||
return this.wrapPromise(sql, args);
|
||||
}
|
||||
}
|
||||
|
||||
ServiceProvider.table = 'service_providers';
|
||||
|
||||
@@ -29,6 +29,10 @@ LEFT JOIN service_providers as sp ON u.service_provider_sid = sp.service_provide
|
||||
LEFT JOIN accounts acc ON u.account_sid = acc.account_sid
|
||||
WHERE u.service_provider_sid = ?
|
||||
`;
|
||||
const sqlCountSP = 'SELECT COUNT(*) AS total_items from users WHERE service_provider_sid = ?';
|
||||
const sqlCountAcc = 'SELECT COUNT(*) AS total_items from users WHERE account_sid = ?';
|
||||
const sqlCount = 'SELECT COUNT(*) AS total_items from users';
|
||||
|
||||
|
||||
class User extends Model {
|
||||
constructor() {
|
||||
@@ -40,15 +44,69 @@ class User extends Model {
|
||||
return rows;
|
||||
}
|
||||
|
||||
static async retrieveAllPaginated(limit, page) {
|
||||
const offset = (page - 1) * limit;
|
||||
const [row] = await promisePool.query(sqlCount);
|
||||
const [{total_items = 0}] = row;
|
||||
const total_pages = Math.ceil(total_items / limit) || 1;
|
||||
|
||||
const sql = `${sqlAll} LIMIT ? OFFSET ?`;
|
||||
const args = [limit, offset];
|
||||
const [data] = await promisePool.query(sql, args);
|
||||
|
||||
return {
|
||||
total_items,
|
||||
total_pages,
|
||||
page,
|
||||
data
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
static async retrieveAllForAccount(account_sid) {
|
||||
const [rows] = await promisePool.query(sqlAccount, [account_sid]);
|
||||
return rows;
|
||||
}
|
||||
|
||||
static async retrieveAllForAccountPaginated(account_sid, limit, page) {
|
||||
const offset = (page - 1) * limit;
|
||||
const [row] = await promisePool.query(sqlCountAcc, [account_sid]);
|
||||
const [{total_items = 0}] = row;
|
||||
const total_pages = Math.ceil(total_items / limit) || 1;
|
||||
|
||||
const sql = `${sqlAccount} LIMIT ? OFFSET ?`;
|
||||
const args = [account_sid, limit, offset];
|
||||
const [data] = await promisePool.query(sql, args);
|
||||
|
||||
return {
|
||||
total_items,
|
||||
total_pages,
|
||||
page,
|
||||
data
|
||||
};
|
||||
}
|
||||
|
||||
static async retrieveAllForServiceProvider(service_provider_sid) {
|
||||
const [rows] = await promisePool.query(sqlSP, [service_provider_sid]);
|
||||
return rows;
|
||||
}
|
||||
|
||||
static async retrieveAllForServiceProviderPaginated(service_provider_sid, limit, page) {
|
||||
const offset = (page - 1) * limit;
|
||||
const [{total_items}] = await promisePool.query(sqlCountSP, [service_provider_sid]);
|
||||
const total_pages = Math.ceil(total_items / limit) || 1;
|
||||
|
||||
const sql = `${sqlSP} LIMIT ? OFFSET ?`;
|
||||
const args = [service_provider_sid, limit, offset];
|
||||
const [data] = await promisePool.query(sql, args);
|
||||
|
||||
return {
|
||||
total_items,
|
||||
total_pages,
|
||||
page,
|
||||
data
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
User.table = 'users';
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
const Model = require('./model');
|
||||
const {promisePool} = require('../db');
|
||||
const retrieveSql = 'SELECT * from voip_carriers vc WHERE vc.account_sid = ?';
|
||||
const { promisePool } = require('../db');
|
||||
const retrieveSqlForAcc = 'SELECT * from voip_carriers vc WHERE vc.account_sid = ?';
|
||||
const retrieveSqlForSP = 'SELECT * from voip_carriers vc WHERE vc.service_provider_sid = ?';
|
||||
|
||||
const retrieveSqlCount = 'SELECT COUNT(*) AS total_items from voip_carriers vc';
|
||||
const retrieveSqlCountForAcc = 'SELECT COUNT(*) AS total_items from voip_carriers vc WHERE vc.account_sid = ?';
|
||||
const retrieveSqlCountForSP = 'SELECT COUNT(*) AS total_items from voip_carriers vc WHERE vc.service_provider_sid = ?';
|
||||
|
||||
class VoipCarrier extends Model {
|
||||
constructor() {
|
||||
@@ -10,12 +13,55 @@ class VoipCarrier extends Model {
|
||||
}
|
||||
static async retrieveAll(account_sid) {
|
||||
if (!account_sid) return super.retrieveAll();
|
||||
const [rows] = await promisePool.query(retrieveSql, account_sid);
|
||||
const [rows] = await promisePool.query(retrieveSqlForAcc, account_sid);
|
||||
if (rows) {
|
||||
rows.map((r) => r.register_status = JSON.parse(r.register_status || '{}'));
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
static async countAll(filter) {
|
||||
const {account_sid, service_provider_sid} = filter || {};
|
||||
let countSql = retrieveSqlCount;
|
||||
const countArgs = [];
|
||||
if (account_sid) {
|
||||
countArgs.push(account_sid);
|
||||
countSql = retrieveSqlCountForAcc;
|
||||
} else if (service_provider_sid) {
|
||||
countArgs.push(service_provider_sid);
|
||||
countSql = retrieveSqlCountForSP;
|
||||
}
|
||||
|
||||
const [row] = await promisePool.query(countSql, countArgs);
|
||||
const [{ total_items }] = row;
|
||||
return total_items;
|
||||
}
|
||||
|
||||
static async retrieveAllPaginated(account_sid, limit, page) {
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
let data;
|
||||
if (!account_sid) {
|
||||
data = await super.retrieveAllPaginated([limit, offset]);
|
||||
} else {
|
||||
const [rows] = await promisePool.query(`${retrieveSqlForAcc} LIMIT ? OFFSET ?`, [account_sid, limit, offset]);
|
||||
if (rows) {
|
||||
rows.map((r) => r.register_status = JSON.parse(r.register_status || '{}'));
|
||||
data = rows;
|
||||
}
|
||||
}
|
||||
|
||||
const total_items = await this.countAll({account_sid});
|
||||
const total_pages = Math.ceil(total_items / limit) || 1;
|
||||
|
||||
return {
|
||||
total_items,
|
||||
total_pages,
|
||||
page,
|
||||
data,
|
||||
};
|
||||
}
|
||||
|
||||
static async retrieveAllForSP(service_provider_sid) {
|
||||
const [rows] = await promisePool.query(retrieveSqlForSP, service_provider_sid);
|
||||
if (rows) {
|
||||
@@ -23,6 +69,34 @@ class VoipCarrier extends Model {
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
static async retrieveAllForSPPaginated(service_provider_sid, limit, page) {
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
let data;
|
||||
if (!service_provider_sid) {
|
||||
data = await super.retrieveAllPaginated([limit, offset]);
|
||||
|
||||
} else {
|
||||
const sql = `${retrieveSqlForSP} LIMIT ? OFFSET ?`;
|
||||
const [rows] = await promisePool.query(sql, [service_provider_sid, limit, offset]);
|
||||
|
||||
if (rows) {
|
||||
rows.map((r) => r.register_status = JSON.parse(r.register_status || '{}'));
|
||||
data = rows;
|
||||
}
|
||||
}
|
||||
|
||||
const total_items = await this.countAll({service_provider_sid});
|
||||
const total_pages = Math.ceil(total_items / limit) || 1;
|
||||
|
||||
return {
|
||||
total_items,
|
||||
total_pages,
|
||||
page,
|
||||
data,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
VoipCarrier.table = 'voip_carriers';
|
||||
|
||||
@@ -25,6 +25,8 @@ const short = require('short-uuid');
|
||||
const VoipCarrier = require('../../models/voip-carrier');
|
||||
const { encrypt } = require('../../utils/encrypt-decrypt');
|
||||
const { testS3Storage, testGoogleStorage, testAzureStorage } = require('../../utils/storage-utils');
|
||||
const {validateQuery} = require('../../utils/validate-query');
|
||||
const { isPaginationEnabled } = require('../../config');
|
||||
const translator = short();
|
||||
|
||||
let idx = 0;
|
||||
@@ -92,23 +94,41 @@ router.get('/:sid/Applications', async(req, res) => {
|
||||
try {
|
||||
const account_sid = parseAccountSid(req);
|
||||
await validateRequest(req, account_sid);
|
||||
const results = await Application.retrieveAll(null, account_sid);
|
||||
|
||||
let results;
|
||||
if (isPaginationEnabled) {
|
||||
validateQuery(req.query);
|
||||
results = await Application.retrieveAllPaginated(null, account_sid, req.query.limit, req.query.page);
|
||||
} else {
|
||||
results = await Application.retrieveAll(null, account_sid);
|
||||
}
|
||||
|
||||
res.status(200).json(results);
|
||||
} catch (err) {
|
||||
sysError(logger, res, err);
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/:sid/VoipCarriers', async(req, res) => {
|
||||
const logger = req.app.locals.logger;
|
||||
try {
|
||||
const account_sid = parseAccountSid(req);
|
||||
await validateRequest(req, account_sid);
|
||||
const results = await VoipCarrier.retrieveAll(account_sid);
|
||||
|
||||
let results;
|
||||
if (isPaginationEnabled) {
|
||||
validateQuery(req.query);
|
||||
results = await VoipCarrier.retrieveAllPaginated(account_sid, req.query.limit, req.query.page);
|
||||
} else {
|
||||
results = await VoipCarrier.retrieveAll(account_sid);
|
||||
}
|
||||
|
||||
res.status(200).json(results);
|
||||
} catch (err) {
|
||||
sysError(logger, res, err);
|
||||
}
|
||||
});
|
||||
|
||||
router.put('/:sid/VoipCarriers/:voip_carrier_sid', async(req, res) => {
|
||||
const logger = req.app.locals.logger;
|
||||
|
||||
@@ -540,7 +560,14 @@ router.get('/', async(req, res) => {
|
||||
try {
|
||||
const service_provider_sid = req.user.hasServiceProviderAuth ? req.user.service_provider_sid : null;
|
||||
const account_sid = req.user.hasAccountAuth ? req.user.account_sid : null;
|
||||
const results = await Account.retrieveAll(service_provider_sid, account_sid);
|
||||
let results;
|
||||
const {limit, page} = req.query || {};
|
||||
if (isPaginationEnabled && limit && page) {
|
||||
validateQuery(req.query);
|
||||
results = await Account.retrieveAllPaginated(service_provider_sid, account_sid, req.query.limit, req.query.page);
|
||||
} else {
|
||||
results = await Account.retrieveAll(service_provider_sid, account_sid);
|
||||
}
|
||||
res.status(200).json(results);
|
||||
} catch (err) {
|
||||
sysError(logger, res, err);
|
||||
@@ -877,8 +904,13 @@ router.get('/:sid/ApiKeys', async(req, res) => {
|
||||
try {
|
||||
const sid = parseAccountSid(req);
|
||||
await validateRequest(req, sid);
|
||||
|
||||
const results = await ApiKey.retrieveAll(sid);
|
||||
let results;
|
||||
if (isPaginationEnabled) {
|
||||
validateQuery(req.query);
|
||||
results = await ApiKey.retrieveAllPaginated(sid, req.query.limit, req.query.page);
|
||||
} else {
|
||||
results = await ApiKey.retrieveAll(sid);
|
||||
}
|
||||
res.status(200).json(results);
|
||||
updateLastUsed(logger, sid, req).catch((err) => {});
|
||||
} catch (err) {
|
||||
@@ -943,8 +975,20 @@ router.get('/:sid/Calls', async(req, res) => {
|
||||
to,
|
||||
callStatus
|
||||
});
|
||||
|
||||
let results = coerceNumbers(snakeCase(calls));
|
||||
if (isPaginationEnabled) {
|
||||
validateQuery(req.query);
|
||||
results = {
|
||||
total_items: results.length,
|
||||
total_pages: results.length / req.query.limit || 1,
|
||||
page: req.query.page,
|
||||
data: results,
|
||||
};
|
||||
}
|
||||
|
||||
logger.debug(`retrieved ${calls.length} calls for account sid ${accountSid}`);
|
||||
res.status(200).json(coerceNumbers(snakeCase(calls)));
|
||||
res.status(200).json(results);
|
||||
updateLastUsed(logger, accountSid, req).catch((err) => {});
|
||||
} catch (err) {
|
||||
sysError(logger, res, err);
|
||||
|
||||
@@ -8,6 +8,8 @@ const decorate = require('./decorate');
|
||||
const sysError = require('../error');
|
||||
const { validate } = require('@jambonz/verb-specifications');
|
||||
const { parseApplicationSid } = require('./utils');
|
||||
const { isPaginationEnabled } = require('../../config');
|
||||
const { validateQuery } = require('../../utils/validate-query');
|
||||
const preconditions = {
|
||||
'add': validateAdd,
|
||||
'update': validateUpdate
|
||||
@@ -156,7 +158,20 @@ router.get('/', async(req, res) => {
|
||||
try {
|
||||
const service_provider_sid = req.user.hasServiceProviderAuth ? req.user.service_provider_sid : null;
|
||||
const account_sid = req.user.hasAccountAuth ? req.user.account_sid : null;
|
||||
const results = await Application.retrieveAll(service_provider_sid, account_sid);
|
||||
let results;
|
||||
|
||||
if (isPaginationEnabled) {
|
||||
validateQuery(req.query);
|
||||
results = await Application.retrieveAllPaginated(
|
||||
service_provider_sid,
|
||||
account_sid,
|
||||
req.query.limit,
|
||||
req.query.page
|
||||
);
|
||||
} else {
|
||||
results = await Application.retrieveAll(service_provider_sid, account_sid);
|
||||
}
|
||||
|
||||
res.status(200).json(results);
|
||||
} catch (err) {
|
||||
sysError(logger, res, err);
|
||||
|
||||
@@ -5,6 +5,8 @@ const LcrCarrierSetEntry = require('../../models/lcr-carrier-set-entry');
|
||||
const decorate = require('./decorate');
|
||||
const {DbErrorBadRequest} = require('../../utils/errors');
|
||||
const sysError = require('../error');
|
||||
const { isPaginationEnabled } = require('../../config');
|
||||
const { validateQuery } = require('../../utils/validate-query');
|
||||
|
||||
const validateAdd = async(req) => {
|
||||
// check if lcr sid is available
|
||||
@@ -68,10 +70,18 @@ router.get('/', async(req, res) => {
|
||||
const lcr_sid = req.query.lcr_sid;
|
||||
try {
|
||||
await checkUserScope(req, lcr_sid);
|
||||
const results = await LcrRoute.retrieveAllByLcrSid(lcr_sid);
|
||||
for (const r of results) {
|
||||
let results;
|
||||
if (isPaginationEnabled) {
|
||||
validateQuery(req.query);
|
||||
results = await LcrRoute.retrieveAllByLcrSidPaginated(lcr_sid, req.query.limit, req.query.page);
|
||||
} else {
|
||||
results = await LcrRoute.retrieveAllByLcrSid(lcr_sid);
|
||||
}
|
||||
|
||||
for (const r of results?.data || results) {
|
||||
r.lcr_carrier_set_entries = await LcrCarrierSetEntry.retrieveAllByLcrRouteSid(r.lcr_route_sid);
|
||||
}
|
||||
|
||||
res.status(200).json(results);
|
||||
} catch (err) {
|
||||
sysError(logger, res, err);
|
||||
|
||||
@@ -6,6 +6,8 @@ const decorate = require('./decorate');
|
||||
const {DbErrorBadRequest} = require('../../utils/errors');
|
||||
const sysError = require('../error');
|
||||
const ServiceProvider = require('../../models/service-provider');
|
||||
const { isPaginationEnabled } = require('../../config');
|
||||
const { validateQuery } = require('../../utils/validate-query');
|
||||
|
||||
const validateAssociatedTarget = async(req, sid) => {
|
||||
const {lookupAccountBySid} = req.app.locals;
|
||||
@@ -190,14 +192,59 @@ router.put('/:sid/Routes', async(req, res) => {
|
||||
router.get('/', async(req, res) => {
|
||||
const logger = req.app.locals.logger;
|
||||
try {
|
||||
const results = req.user.hasAdminAuth ?
|
||||
await Lcr.retrieveAll() : req.user.hasAccountAuth ?
|
||||
await Lcr.retrieveAllByAccountSid(req.user.hasAccountAuth ? req.user.account_sid : null) :
|
||||
await Lcr.retrieveAllByServiceProviderSid(req.user.service_provider_sid);
|
||||
let results;
|
||||
|
||||
for (const lcr of results) {
|
||||
lcr.number_routes = await LcrRoutes.countAllByLcrSid(lcr.lcr_sid);
|
||||
if (req.user.hasAdminAuth) {
|
||||
if (isPaginationEnabled) {
|
||||
validateQuery(req.query);
|
||||
results = await Lcr.retrieveAllPaginated(req.query.limit, req.query.page);
|
||||
} else {
|
||||
results = await Lcr.retrieveAll();
|
||||
}
|
||||
|
||||
} else if (req.user.hasAccountAuth) {
|
||||
if (isPaginationEnabled) {
|
||||
validateQuery(req.query);
|
||||
results = await Lcr.retrieveAllByAccountSidPaginated(
|
||||
req.user.account_sid,
|
||||
req.query.limit,
|
||||
req.query.page
|
||||
);
|
||||
} else {
|
||||
results = await Lcr.retrieveAllByAccountSid(req.user.account_sid);
|
||||
}
|
||||
|
||||
} else if (req.user.hasServiceProviderAuth) {
|
||||
if (isPaginationEnabled) {
|
||||
validateQuery(req.query);
|
||||
results = await Lcr.retrieveAllByServiceProviderSidPaginated(
|
||||
req.user.service_provider_sid,
|
||||
req.query.limit,
|
||||
req.query.page
|
||||
);
|
||||
|
||||
if (results.data.length) {
|
||||
for (const lcr of results?.data || results) {
|
||||
lcr.number_routes = await LcrRoutes.countAllByLcrSid(lcr.lcr_sid);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
results = await Lcr.retrieveAllByServiceProviderSid(req.user.service_provider_sid);
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(results)) {
|
||||
for (const lcr of results) {
|
||||
lcr.number_routes = await LcrRoutes.countAllByLcrSid(lcr.lcr_sid);
|
||||
}
|
||||
} else {
|
||||
for (const lcr of results.data) {
|
||||
lcr.number_routes = await LcrRoutes.countAllByLcrSid(lcr.lcr_sid);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
res.status(200).json(results);
|
||||
} catch (err) {
|
||||
sysError(logger, res, err);
|
||||
|
||||
@@ -13,7 +13,8 @@ const preconditions = {
|
||||
};
|
||||
const sysError = require('../error');
|
||||
const { parsePhoneNumberSid } = require('./utils');
|
||||
|
||||
const { isPaginationEnabled } = require('../../config');
|
||||
const { validateQuery } = require('../../utils/validate-query');
|
||||
|
||||
/* check for required fields when adding */
|
||||
async function validateAdd(req) {
|
||||
@@ -94,9 +95,31 @@ decorate(router, PhoneNumber, ['add', 'update', 'delete'], preconditions);
|
||||
router.get('/', async(req, res) => {
|
||||
const logger = req.app.locals.logger;
|
||||
try {
|
||||
const results = req.user.hasServiceProviderAuth ?
|
||||
await PhoneNumber.retrieveAllForSP(req.user.service_provider_sid) :
|
||||
await PhoneNumber.retrieveAll(req.user.hasAccountAuth ? req.user.account_sid : null);
|
||||
let results;
|
||||
if (req.user.hasServiceProviderAuth) {
|
||||
if (isPaginationEnabled) {
|
||||
validateQuery(req.query);
|
||||
results = await PhoneNumber.retrieveAllForSPPaginated(
|
||||
req.user.service_provider_sid,
|
||||
req.query.limit,
|
||||
req.query.page
|
||||
);
|
||||
} else {
|
||||
results = await PhoneNumber.retrieveAllForSP(req.user.service_provider_sid);
|
||||
}
|
||||
} else {
|
||||
if (isPaginationEnabled) {
|
||||
validateQuery(req.query);
|
||||
results = await PhoneNumber.retrieveAllPaginated(
|
||||
req.user.account_sid,
|
||||
req.query.limit,
|
||||
req.query.page
|
||||
);
|
||||
} else {
|
||||
results = await PhoneNumber.retrieveAll(req.user.account_sid);
|
||||
}
|
||||
}
|
||||
|
||||
res.status(200).json(results);
|
||||
} catch (err) {
|
||||
sysError(logger, res, err);
|
||||
|
||||
@@ -15,6 +15,8 @@ const {
|
||||
} = require('./utils');
|
||||
const sysError = require('../error');
|
||||
const decorate = require('./decorate');
|
||||
const { validateQuery } = require('../../utils/validate-query');
|
||||
const { isPaginationEnabled } = require('../../config');
|
||||
const preconditions = {
|
||||
'delete': noActiveAccountsOrUsers
|
||||
};
|
||||
@@ -103,13 +105,24 @@ router.use('/:sid/Limits', hasServiceProviderPermissions, require('./limits'));
|
||||
router.use('/:sid/PredefinedCarriers', hasServiceProviderPermissions, require('./add-from-predefined-carrier'));
|
||||
router.get('/:sid/Accounts', async(req, res) => {
|
||||
const logger = req.app.locals.logger;
|
||||
const {limit, page} = req.query;
|
||||
try {
|
||||
await validateRetrieve(req);
|
||||
const service_provider_sid = parseServiceProviderSid(req);
|
||||
let results = await Account.retrieveAll(service_provider_sid);
|
||||
if (req.user.hasScope('account')) {
|
||||
results = results.filter((r) => r.account_sid === req.user.account_sid);
|
||||
const account_sid = req.user.hasScope('account') ? req.user.account_sid : null;
|
||||
let results;
|
||||
if (isPaginationEnabled && limit && page) {
|
||||
validateQuery(req.query);
|
||||
results = await Account.retrieveAllPaginated(
|
||||
service_provider_sid,
|
||||
account_sid,
|
||||
req.query.limit,
|
||||
req.query.page
|
||||
);
|
||||
} else {
|
||||
results = await Account.retrieveAll(service_provider_sid);
|
||||
}
|
||||
|
||||
res.status(200).json(results);
|
||||
} catch (err) {
|
||||
sysError(logger, res, err);
|
||||
@@ -121,41 +134,98 @@ router.get('/:sid/Applications', async(req, res) => {
|
||||
try {
|
||||
await validateRetrieve(req);
|
||||
const service_provider_sid = parseServiceProviderSid(req);
|
||||
let results = await Application.retrieveAll(service_provider_sid);
|
||||
if (req.user.hasScope('account')) {
|
||||
results = results.filter((r) => r.account_sid === req.user.account_sid);
|
||||
const account_sid = req.user.hasScope('account') ? req.user.account_sid : null;
|
||||
|
||||
let results;
|
||||
if (isPaginationEnabled) {
|
||||
validateQuery(req.query);
|
||||
results = await Application.retrieveAllPaginated(
|
||||
service_provider_sid,
|
||||
account_sid,
|
||||
req.query.limit,
|
||||
req.query.page
|
||||
);
|
||||
|
||||
} else {
|
||||
results = await Application.retrieveAll(service_provider_sid, account_sid);
|
||||
}
|
||||
|
||||
res.status(200).json(results);
|
||||
} catch (err) {
|
||||
sysError(logger, res, err);
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/:sid/Accounts/Availability', async(req, res) => {
|
||||
const logger = req.app.locals.logger;
|
||||
const { sid } = req.params;
|
||||
const { property, value } = req.query;
|
||||
try {
|
||||
const results = await Account.isPropertyAvailable(property, [sid, value]);
|
||||
|
||||
res.status(200).json(results);
|
||||
} catch (err) {
|
||||
sysError(logger, res, err);
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/:sid/PhoneNumbers', async(req, res) => {
|
||||
const logger = req.app.locals.logger;
|
||||
try {
|
||||
await validateRetrieve(req);
|
||||
let results;
|
||||
const service_provider_sid = parseServiceProviderSid(req);
|
||||
let results = await PhoneNumber.retrieveAllForSP(service_provider_sid);
|
||||
if (req.user.hasScope('account')) {
|
||||
results = results.filter((r) => r.account_sid === req.user.account_sid);
|
||||
if (isPaginationEnabled) {
|
||||
validateQuery(req.query);
|
||||
results = await PhoneNumber.retrieveAllForSPPaginated(service_provider_sid, req.query.limit, req.query.page);
|
||||
if (req.user.hasScope('account')) {
|
||||
results.data = results.data.filter((r) => r.account_sid === req.user.account_sid);
|
||||
}
|
||||
} else {
|
||||
results = await PhoneNumber.retrieveAllForSP(service_provider_sid);
|
||||
if (req.user.hasScope('account')) {
|
||||
results = results.filter((r) => r.account_sid === req.user.account_sid);
|
||||
}
|
||||
}
|
||||
|
||||
res.status(200).json(results);
|
||||
} catch (err) {
|
||||
sysError(logger, res, err);
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/:sid/VoipCarriers', async(req, res) => {
|
||||
const logger = req.app.locals.logger;
|
||||
try {
|
||||
await validateRetrieve(req);
|
||||
const service_provider_sid = parseServiceProviderSid(req);
|
||||
const carriers = await VoipCarrier.retrieveAllForSP(service_provider_sid);
|
||||
const account_sid = req.user.hasScope('account') ? req.user.account_sid : null;
|
||||
let carriers;
|
||||
if (isPaginationEnabled) {
|
||||
validateQuery(req.query);
|
||||
if (account_sid) {
|
||||
carriers = await VoipCarrier.retrieveAllPaginated(
|
||||
account_sid,
|
||||
req.query.limit,
|
||||
req.query.page
|
||||
);
|
||||
} else {
|
||||
carriers = await VoipCarrier.retrieveAllForSPPaginated(
|
||||
service_provider_sid,
|
||||
req.query.limit,
|
||||
req.query.page
|
||||
);
|
||||
}
|
||||
|
||||
if (req.user.hasScope('account')) {
|
||||
return res.status(200).json(carriers.filter((c) => c.account_sid === req.user.account_sid || !c.account_sid));
|
||||
} else {
|
||||
if (account_sid) {
|
||||
carriers = await VoipCarrier.retrieveAll(account_sid);
|
||||
} else {
|
||||
carriers = await VoipCarrier.retrieveAllForSP(service_provider_sid);
|
||||
}
|
||||
}
|
||||
|
||||
res.status(200).json(carriers);
|
||||
return res.status(200).json(carriers);
|
||||
} catch (err) {
|
||||
sysError(logger, res, err);
|
||||
}
|
||||
@@ -185,15 +255,31 @@ router.put('/:sid/VoipCarriers/:voip_carrier_sid', async(req, res) => {
|
||||
sysError(logger, res, err);
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/:sid/ApiKeys', async(req, res) => {
|
||||
const logger = req.app.locals.logger;
|
||||
const {sid} = req.params;
|
||||
const { sid } = req.params;
|
||||
try {
|
||||
await validateRetrieve(req);
|
||||
let results = await ApiKey.retrieveAllForSP(sid);
|
||||
if (req.user.hasScope('account')) {
|
||||
results = results.filter((r) => r.account_sid === req.user.account_sid);
|
||||
|
||||
const account_sid = req.user.hasScope('account') ? req.user.account_sid : null;
|
||||
let results;
|
||||
if (isPaginationEnabled) {
|
||||
validateQuery(req.query);
|
||||
if (account_sid) {
|
||||
results = await ApiKey.retrieveAllPaginated(account_sid, req.query.limit, req.query.page);
|
||||
} else {
|
||||
results = await ApiKey.retrieveAllForSPPaginated(sid, req.query.limit, req.query.page);
|
||||
}
|
||||
|
||||
} else {
|
||||
if (account_sid) {
|
||||
results = await ApiKey.retrieveAll(account_sid);
|
||||
} else {
|
||||
results = await ApiKey.retrieveAllForSP(sid);
|
||||
}
|
||||
}
|
||||
|
||||
res.status(200).json(results);
|
||||
await ApiKey.updateLastUsed(sid);
|
||||
} catch (err) {
|
||||
@@ -226,14 +312,27 @@ router.post('/', async(req, res) => {
|
||||
/* list */
|
||||
router.get('/', async(req, res) => {
|
||||
const logger = req.app.locals.logger;
|
||||
const { limit, page } = req.query;
|
||||
try {
|
||||
const results = await ServiceProvider.retrieveAll();
|
||||
logger.debug({results, user: req.user}, 'ServiceProvider.retrieveAll');
|
||||
if (req.user.hasScope('service_provider') || req.user.hasScope('account')) {
|
||||
logger.debug(`Filtering results for ${req.user.service_provider_sid}`);
|
||||
return res.status(200).json(results.filter((e) => req.user.service_provider_sid === e.service_provider_sid));
|
||||
let results;
|
||||
if (isPaginationEnabled && limit && page) {
|
||||
validateQuery(req.query);
|
||||
results = await ServiceProvider.retrieveAllPaginated(req.query.limit, req.query.page);
|
||||
if (req.user.hasScope('service_provider') || req.user.hasScope('account')) {
|
||||
logger.debug(`Filtering results for ${req.user.service_provider_sid}`);
|
||||
results.data = results.data.filter((e) => req.user.service_provider_sid === e.service_provider_sid);
|
||||
}
|
||||
|
||||
} else {
|
||||
results = await ServiceProvider.retrieveAll();
|
||||
if (req.user.hasScope('service_provider') || req.user.hasScope('account')) {
|
||||
logger.debug(`Filtering results for ${req.user.service_provider_sid}`);
|
||||
results = results.filter((e) => req.user.service_provider_sid === e.service_provider_sid);
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug({ results, user: req.user }, 'ServiceProvider.retrieveAll');
|
||||
|
||||
res.status(200).json(results);
|
||||
} catch (err) {
|
||||
sysError(logger, res, err);
|
||||
|
||||
@@ -3,10 +3,13 @@ const User = require('../../models/user');
|
||||
const {DbErrorBadRequest, BadRequestError, DbErrorForbidden} = require('../../utils/errors');
|
||||
const {generateHashedPassword, verifyPassword} = require('../../utils/password-utils');
|
||||
const {promisePool} = require('../../db');
|
||||
const {validatePasswordSettings, parseUserSid} = require('./utils');
|
||||
const {validatePasswordSettings, parseUserSid, hasUserScope} = require('./utils');
|
||||
const {decrypt} = require('../../utils/encrypt-decrypt');
|
||||
const {cacheClient} = require('../../helpers');
|
||||
const sysError = require('../error');
|
||||
const { isPaginationEnabled } = require('../../config');
|
||||
const { validateQuery } = require('../../utils/validate-query');
|
||||
|
||||
const retrieveMyDetails = `SELECT *
|
||||
FROM users user
|
||||
JOIN accounts AS account ON account.account_sid = user.account_sid
|
||||
@@ -141,63 +144,88 @@ const ensureUserRetrievalIsAllowed = (req, user) => {
|
||||
}
|
||||
};
|
||||
|
||||
const buildUser = (user) => {
|
||||
const {
|
||||
user_sid,
|
||||
name,
|
||||
email,
|
||||
force_change,
|
||||
is_active,
|
||||
account_sid,
|
||||
service_provider_sid,
|
||||
account_name,
|
||||
service_provider_name,
|
||||
cognigy_user_id,
|
||||
} = user || {};
|
||||
let scope;
|
||||
if (account_sid && service_provider_sid) {
|
||||
scope = 'account';
|
||||
} else if (service_provider_sid) {
|
||||
scope = 'service_provider';
|
||||
} else {
|
||||
scope = 'admin';
|
||||
}
|
||||
|
||||
const obj = {
|
||||
user_sid,
|
||||
name,
|
||||
email,
|
||||
scope,
|
||||
force_change,
|
||||
is_active,
|
||||
...(account_sid && { account_sid }),
|
||||
...(account_name && { account_name }),
|
||||
...(service_provider_sid && { service_provider_sid }),
|
||||
...(service_provider_name && { service_provider_name }),
|
||||
...(cognigy_user_id && { cognigy_user_id }),
|
||||
};
|
||||
return obj;
|
||||
};
|
||||
|
||||
router.get('/', async(req, res) => {
|
||||
const logger = req.app.locals.logger;
|
||||
|
||||
let usersList;
|
||||
let results;
|
||||
try {
|
||||
let results;
|
||||
if (req.user.hasAdminAuth) {
|
||||
results = await User.retrieveAll();
|
||||
}
|
||||
else if (req.user.hasAccountAuth) {
|
||||
results = await User.retrieveAllForAccount(req.user.account_sid, true);
|
||||
}
|
||||
else if (req.user.hasServiceProviderAuth) {
|
||||
results = await User.retrieveAllForServiceProvider(req.user.service_provider_sid, true);
|
||||
if (hasUserScope('admin', req)) {
|
||||
if (isPaginationEnabled) {
|
||||
validateQuery(req.query);
|
||||
results = await User.retrieveAllPaginated(req.query.limit, req.query.page);
|
||||
} else {
|
||||
results = await User.retrieveAll();
|
||||
}
|
||||
} else if (hasUserScope('account', req)) {
|
||||
if (isPaginationEnabled) {
|
||||
validateQuery(req.query);
|
||||
results = await User.retrieveAllForAccountPaginated(req.user.account_sid, req.query.limit, req.query.page);
|
||||
} else {
|
||||
results = await User.retrieveAllForAccount(req.user.account_sid, true);
|
||||
}
|
||||
} else if (hasUserScope('sp', req)) {
|
||||
if (isPaginationEnabled) {
|
||||
validateQuery(req.query);
|
||||
results = await User.retrieveAllForServiceProviderPaginated(
|
||||
req.user.service_provider_sid,
|
||||
req.query.limit,
|
||||
req.query.page
|
||||
);
|
||||
} else {
|
||||
results = await User.retrieveAllForServiceProvider(req.user.service_provider_sid, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (results.length === 0) throw new Error('failure retrieving users list');
|
||||
|
||||
usersList = results.map((user) => {
|
||||
const {
|
||||
user_sid,
|
||||
name,
|
||||
email,
|
||||
force_change,
|
||||
is_active,
|
||||
account_sid,
|
||||
service_provider_sid,
|
||||
account_name,
|
||||
service_provider_name
|
||||
} = user;
|
||||
let scope;
|
||||
if (account_sid && service_provider_sid) {
|
||||
scope = 'account';
|
||||
} else if (service_provider_sid) {
|
||||
scope = 'service_provider';
|
||||
} else {
|
||||
scope = 'admin';
|
||||
}
|
||||
if (isPaginationEnabled) {
|
||||
results.data = results.data.map((user) => buildUser(user));
|
||||
} else {
|
||||
results = results.map((user) => buildUser(user));
|
||||
}
|
||||
|
||||
const obj = {
|
||||
user_sid,
|
||||
name,
|
||||
email,
|
||||
scope,
|
||||
force_change,
|
||||
is_active,
|
||||
...(account_sid && {account_sid}),
|
||||
...(account_name && {account_name}),
|
||||
...(service_provider_sid && {service_provider_sid}),
|
||||
...(service_provider_name && {service_provider_name})
|
||||
};
|
||||
return obj;
|
||||
});
|
||||
} catch (err) {
|
||||
sysError(logger, res, err);
|
||||
}
|
||||
res.status(200).json(usersList);
|
||||
res.status(200).json(results);
|
||||
});
|
||||
|
||||
router.get('/me', async(req, res) => {
|
||||
|
||||
@@ -313,6 +313,22 @@ const hasServiceProviderPermissions = (req, res, next) => {
|
||||
}
|
||||
};
|
||||
|
||||
const hasUserScope = (scope, req) => {
|
||||
const {user} = req || {};
|
||||
const {hasServiceProviderAuth, hasAccountAuth, hasAdminAuth} = user || {};
|
||||
|
||||
switch (scope) {
|
||||
case 'admin':
|
||||
return hasAdminAuth;
|
||||
case 'sp':
|
||||
return hasServiceProviderAuth;
|
||||
case 'account':
|
||||
return hasAccountAuth;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const checkLimits = async(req, res, next) => {
|
||||
const logger = req.app.locals.logger;
|
||||
if (process.env.APPLY_JAMBONZ_DB_LIMITS && req.user.hasScope('account')) {
|
||||
@@ -460,5 +476,6 @@ module.exports = {
|
||||
checkLimits,
|
||||
enableSubspace,
|
||||
disableSubspace,
|
||||
validatePasswordSettings
|
||||
validatePasswordSettings,
|
||||
hasUserScope,
|
||||
};
|
||||
|
||||
@@ -5,6 +5,8 @@ const {promisePool} = require('../../db');
|
||||
const decorate = require('./decorate');
|
||||
const sysError = require('../error');
|
||||
const { parseVoipCarrierSid } = require('./utils');
|
||||
const { isPaginationEnabled } = require('../../config');
|
||||
const { validateQuery } = require('../../utils/validate-query');
|
||||
|
||||
const validate = async(req) => {
|
||||
const {lookupAppBySid, lookupAccountBySid} = req.app.locals;
|
||||
@@ -74,9 +76,33 @@ decorate(router, VoipCarrier, ['add', 'update', 'delete'], preconditions);
|
||||
router.get('/', async(req, res) => {
|
||||
const logger = req.app.locals.logger;
|
||||
try {
|
||||
const results = req.user.hasAdminAuth ?
|
||||
await VoipCarrier.retrieveAll(req.user.hasAccountAuth ? req.user.account_sid : null) :
|
||||
await VoipCarrier.retrieveAllForSP(req.user.service_provider_sid);
|
||||
let results;
|
||||
|
||||
if (req.user.hasAdminAuth) {
|
||||
const account_sid = req.user.hasAccountAuth ? req.user.account_sid : null;
|
||||
if (isPaginationEnabled) {
|
||||
validateQuery(req.query);
|
||||
results = await VoipCarrier.retrieveAllPaginated(
|
||||
account_sid,
|
||||
req.query.limit,
|
||||
req.query.page
|
||||
);
|
||||
} else {
|
||||
results = await VoipCarrier.retrieveAll(account_sid);
|
||||
}
|
||||
|
||||
} else {
|
||||
if (isPaginationEnabled) {
|
||||
validateQuery(req.query);
|
||||
results = await VoipCarrier.retrieveAllForSPPaginated(
|
||||
req.user.service_provider_sid,
|
||||
req.query.limit,
|
||||
req.query.page
|
||||
);
|
||||
} else {
|
||||
results = await VoipCarrier.retrieveAllForSP(req.user.service_provider_sid);
|
||||
}
|
||||
}
|
||||
|
||||
if (req.user.hasScope('account')) {
|
||||
return res.status(200).json(results.filter((c) => c.account_sid === req.user.account_sid || !c.account_sid));
|
||||
|
||||
@@ -2,7 +2,13 @@ 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 { isPaginationEnabled } = require('../config');
|
||||
|
||||
let swaggerFilepath = '../swagger/swagger.yaml';
|
||||
if (isPaginationEnabled) {
|
||||
swaggerFilepath = '../swagger/swagger-pagination.yaml';
|
||||
}
|
||||
const swaggerDocument = YAML.load(path.resolve(__dirname, swaggerFilepath));
|
||||
const api = require('./api');
|
||||
const stripe = require('./stripe');
|
||||
const {checkLimits} = require('./api/utils');
|
||||
|
||||
5557
lib/swagger/swagger-pagination.yaml
Normal file
5557
lib/swagger/swagger-pagination.yaml
Normal file
File diff suppressed because it is too large
Load Diff
27
lib/utils/validate-query.js
Normal file
27
lib/utils/validate-query.js
Normal file
@@ -0,0 +1,27 @@
|
||||
const { DbErrorBadRequest } = require('../utils/errors');
|
||||
|
||||
const validateQuery = (query) => {
|
||||
const { page = 1, limit = 25 } = query || {};
|
||||
|
||||
query.page = Number(page);
|
||||
query.limit = Number(limit);
|
||||
|
||||
if (query.page < 1) {
|
||||
throw new DbErrorBadRequest('invalid "page" query parameter');
|
||||
}
|
||||
|
||||
switch (query.limit) {
|
||||
case 25:
|
||||
case 50:
|
||||
case 100:
|
||||
break;
|
||||
default:
|
||||
throw new DbErrorBadRequest('invalid "limit" query parameter');
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
validateQuery
|
||||
};
|
||||
Reference in New Issue
Block a user