mirror of
https://github.com/jambonz/jambonz-api-server.git
synced 2026-02-09 02:29:59 +00:00
Compare commits
27 Commits
v0.8.4-rc3
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6fed082e2d | ||
|
|
7d16bdd774 | ||
|
|
79e1bc8d12 | ||
|
|
9d24ef6238 | ||
|
|
042ad9f629 | ||
|
|
7351f0ad68 | ||
|
|
de7b74f898 | ||
|
|
d361f1aeb1 | ||
|
|
f3d002cfca | ||
|
|
3121c2a197 | ||
|
|
b7bdf300c6 | ||
|
|
c96159268e | ||
|
|
8e200251ca | ||
|
|
898f3aec4a | ||
|
|
6f85752352 | ||
|
|
fe7cc9ad58 | ||
|
|
1ffdfebdb2 | ||
|
|
dcf1895920 | ||
|
|
c509b9d277 | ||
|
|
eff8474997 | ||
|
|
b4237beeeb | ||
|
|
0406e42c19 | ||
|
|
533cd2f47d | ||
|
|
742884cc72 | ||
|
|
9fccfa2a73 | ||
|
|
3356b7302a | ||
|
|
9f533ed17c |
@@ -334,6 +334,7 @@ last_tested DATETIME,
|
||||
tts_tested_ok BOOLEAN,
|
||||
stt_tested_ok BOOLEAN,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
label VARCHAR(64),
|
||||
PRIMARY KEY (speech_credential_sid)
|
||||
);
|
||||
|
||||
@@ -478,8 +479,18 @@ app_json TEXT,
|
||||
speech_synthesis_vendor VARCHAR(64) NOT NULL DEFAULT 'google',
|
||||
speech_synthesis_language VARCHAR(12) NOT NULL DEFAULT 'en-US',
|
||||
speech_synthesis_voice VARCHAR(64),
|
||||
speech_synthesis_label VARCHAR(64),
|
||||
speech_recognizer_vendor VARCHAR(64) NOT NULL DEFAULT 'google',
|
||||
speech_recognizer_language VARCHAR(64) NOT NULL DEFAULT 'en-US',
|
||||
speech_recognizer_label VARCHAR(64),
|
||||
use_for_fallback_speech BOOLEAN DEFAULT false,
|
||||
fallback_speech_synthesis_vendor VARCHAR(64) NOT NULL DEFAULT 'google',
|
||||
fallback_speech_synthesis_language VARCHAR(12) NOT NULL DEFAULT 'en-US',
|
||||
fallback_speech_synthesis_voice VARCHAR(64),
|
||||
fallback_speech_synthesis_label VARCHAR(64),
|
||||
fallback_speech_recognizer_vendor VARCHAR(64) NOT NULL DEFAULT 'google',
|
||||
fallback_speech_recognizer_language VARCHAR(64) NOT NULL DEFAULT 'en-US',
|
||||
fallback_speech_recognizer_label VARCHAR(64),
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
record_all_calls BOOLEAN NOT NULL DEFAULT false,
|
||||
PRIMARY KEY (application_sid)
|
||||
@@ -609,8 +620,6 @@ CREATE INDEX smpp_address_sid_idx ON smpp_addresses (smpp_address_sid);
|
||||
CREATE INDEX service_provider_sid_idx ON smpp_addresses (service_provider_sid);
|
||||
ALTER TABLE smpp_addresses ADD FOREIGN KEY service_provider_sid_idxfk_4 (service_provider_sid) REFERENCES service_providers (service_provider_sid);
|
||||
|
||||
CREATE UNIQUE INDEX speech_credentials_idx_1 ON speech_credentials (vendor,account_sid);
|
||||
|
||||
CREATE INDEX speech_credential_sid_idx ON speech_credentials (speech_credential_sid);
|
||||
CREATE INDEX service_provider_sid_idx ON speech_credentials (service_provider_sid);
|
||||
ALTER TABLE speech_credentials ADD FOREIGN KEY service_provider_sid_idxfk_5 (service_provider_sid) REFERENCES service_providers (service_provider_sid);
|
||||
|
||||
110
db/jambones.sqs
110
db/jambones.sqs
@@ -884,7 +884,7 @@
|
||||
<y>958.00</y>
|
||||
</location>
|
||||
<size>
|
||||
<width>368.00</width>
|
||||
<width>302.00</width>
|
||||
<height>280.00</height>
|
||||
</size>
|
||||
<zorder>14</zorder>
|
||||
@@ -981,24 +981,11 @@
|
||||
<notNull><![CDATA[1]]></notNull>
|
||||
<uid><![CDATA[8860648C-4790-4A01-9E2E-60DC52A287FA]]></uid>
|
||||
</SQLField>
|
||||
<SQLIndex>
|
||||
<name><![CDATA[speech_credentials_idx_1]]></name>
|
||||
<fieldName><![CDATA[vendor]]></fieldName>
|
||||
<fieldName><![CDATA[account_sid]]></fieldName>
|
||||
<SQLIndexEntry>
|
||||
<name><![CDATA[vendor]]></name>
|
||||
<prefixSize><![CDATA[]]></prefixSize>
|
||||
<fieldUid><![CDATA[9D8FCF55-D68E-44D3-90DF-27B5ABD1D0BE]]></fieldUid>
|
||||
</SQLIndexEntry>
|
||||
<SQLIndexEntry>
|
||||
<name><![CDATA[account_sid]]></name>
|
||||
<prefixSize><![CDATA[]]></prefixSize>
|
||||
<fieldUid><![CDATA[7E964ED2-EC2E-4BCB-8DEC-C455B87FAC07]]></fieldUid>
|
||||
</SQLIndexEntry>
|
||||
<indexNamePrefix><![CDATA[speech_credentials]]></indexNamePrefix>
|
||||
<indexType><![CDATA[UNIQUE]]></indexType>
|
||||
<uid><![CDATA[554ABEC2-3E1B-41B1-BF07-25F403D5E3B4]]></uid>
|
||||
</SQLIndex>
|
||||
<SQLField>
|
||||
<name><![CDATA[label]]></name>
|
||||
<type><![CDATA[VARCHAR(64)]]></type>
|
||||
<uid><![CDATA[0D42A22C-DF14-42A1-BDE2-A53AC8B0D8D6]]></uid>
|
||||
</SQLField>
|
||||
<labelWindowIndex><![CDATA[21]]></labelWindowIndex>
|
||||
<ui.treeExpanded><![CDATA[1]]></ui.treeExpanded>
|
||||
<uid><![CDATA[49A68E1C-DEE2-446C-A4EB-9850E16155CC]]></uid>
|
||||
@@ -2031,8 +2018,8 @@
|
||||
<name><![CDATA[dns_records]]></name>
|
||||
<schema><![CDATA[]]></schema>
|
||||
<location>
|
||||
<x>947.00</x>
|
||||
<y>1303.00</y>
|
||||
<x>1270.00</x>
|
||||
<y>1425.00</y>
|
||||
</location>
|
||||
<size>
|
||||
<width>262.00</width>
|
||||
@@ -2197,8 +2184,8 @@
|
||||
<name><![CDATA[clients]]></name>
|
||||
<schema><![CDATA[]]></schema>
|
||||
<location>
|
||||
<x>916.00</x>
|
||||
<y>1447.00</y>
|
||||
<x>974.00</x>
|
||||
<y>1496.00</y>
|
||||
</location>
|
||||
<size>
|
||||
<width>228.00</width>
|
||||
@@ -2367,8 +2354,8 @@
|
||||
<y>917.00</y>
|
||||
</location>
|
||||
<size>
|
||||
<width>345.00</width>
|
||||
<height>340.00</height>
|
||||
<width>363.00</width>
|
||||
<height>540.00</height>
|
||||
</size>
|
||||
<zorder>0</zorder>
|
||||
<SQLField>
|
||||
@@ -2487,6 +2474,12 @@
|
||||
<notNull><![CDATA[0]]></notNull>
|
||||
<uid><![CDATA[929D66F0-64B9-4D7C-AB4B-24F131E1178F]]></uid>
|
||||
</SQLField>
|
||||
<SQLField>
|
||||
<name><![CDATA[speech_synthesis_label]]></name>
|
||||
<type><![CDATA[VARCHAR(64)]]></type>
|
||||
<notNull><![CDATA[0]]></notNull>
|
||||
<uid><![CDATA[BFA24DF2-9CF5-47B0-848D-8B685B7C6750]]></uid>
|
||||
</SQLField>
|
||||
<SQLField>
|
||||
<name><![CDATA[speech_recognizer_vendor]]></name>
|
||||
<type><![CDATA[VARCHAR(64)]]></type>
|
||||
@@ -2501,10 +2494,69 @@
|
||||
<notNull><![CDATA[1]]></notNull>
|
||||
<uid><![CDATA[A03AFB7B-492F-48E3-AE3C-B1416D5B6B12]]></uid>
|
||||
</SQLField>
|
||||
<SQLField>
|
||||
<name><![CDATA[speech_recognizer_label]]></name>
|
||||
<type><![CDATA[VARCHAR(64)]]></type>
|
||||
<notNull><![CDATA[0]]></notNull>
|
||||
<uid><![CDATA[A247A784-CCD6-40B4-9D0A-2F0EF8F8AFD2]]></uid>
|
||||
</SQLField>
|
||||
<SQLField>
|
||||
<name><![CDATA[use_for_fallback_speech]]></name>
|
||||
<type><![CDATA[BOOLEAN]]></type>
|
||||
<defaultValue><![CDATA[false]]></defaultValue>
|
||||
<uid><![CDATA[DDA48DD6-4B0F-4AD5-9B32-D508BBA1A8EE]]></uid>
|
||||
</SQLField>
|
||||
<SQLField>
|
||||
<name><![CDATA[fallback_speech_synthesis_vendor]]></name>
|
||||
<type><![CDATA[VARCHAR(64)]]></type>
|
||||
<defaultValue><![CDATA[google]]></defaultValue>
|
||||
<notNull><![CDATA[1]]></notNull>
|
||||
<uid><![CDATA[26BBDEEF-E179-4280-9917-6F2BD6367459]]></uid>
|
||||
</SQLField>
|
||||
<SQLField>
|
||||
<name><![CDATA[fallback_speech_synthesis_language]]></name>
|
||||
<type><![CDATA[VARCHAR(12)]]></type>
|
||||
<defaultValue><![CDATA[en-US]]></defaultValue>
|
||||
<notNull><![CDATA[1]]></notNull>
|
||||
<uid><![CDATA[E008D6D7-9BB7-4372-8B46-F92C0EB15082]]></uid>
|
||||
</SQLField>
|
||||
<SQLField>
|
||||
<name><![CDATA[fallback_speech_synthesis_voice]]></name>
|
||||
<type><![CDATA[VARCHAR(64)]]></type>
|
||||
<notNull><![CDATA[0]]></notNull>
|
||||
<uid><![CDATA[6A0E92C9-32B9-4179-A893-3DADF5DD7728]]></uid>
|
||||
</SQLField>
|
||||
<SQLField>
|
||||
<name><![CDATA[fallback_speech_synthesis_label]]></name>
|
||||
<type><![CDATA[VARCHAR(64)]]></type>
|
||||
<notNull><![CDATA[0]]></notNull>
|
||||
<uid><![CDATA[8576DEF6-D81A-4D4D-8980-00580779D164]]></uid>
|
||||
</SQLField>
|
||||
<SQLField>
|
||||
<name><![CDATA[fallback_speech_recognizer_vendor]]></name>
|
||||
<type><![CDATA[VARCHAR(64)]]></type>
|
||||
<defaultValue><![CDATA[google]]></defaultValue>
|
||||
<notNull><![CDATA[1]]></notNull>
|
||||
<uid><![CDATA[14ECF5EA-81C5-4EAE-9575-9785CEB672E6]]></uid>
|
||||
</SQLField>
|
||||
<SQLField>
|
||||
<name><![CDATA[fallback_speech_recognizer_language]]></name>
|
||||
<type><![CDATA[VARCHAR(64)]]></type>
|
||||
<defaultValue><![CDATA[en-US]]></defaultValue>
|
||||
<notNull><![CDATA[1]]></notNull>
|
||||
<uid><![CDATA[EC792500-6B2B-4E54-AA89-43E7A0FD8642]]></uid>
|
||||
</SQLField>
|
||||
<SQLField>
|
||||
<name><![CDATA[fallback_speech_recognizer_label]]></name>
|
||||
<type><![CDATA[VARCHAR(64)]]></type>
|
||||
<notNull><![CDATA[0]]></notNull>
|
||||
<uid><![CDATA[65AA5173-6523-49F7-9D95-78C4B3A7C7E6]]></uid>
|
||||
</SQLField>
|
||||
<SQLField>
|
||||
<name><![CDATA[created_at]]></name>
|
||||
<type><![CDATA[DATETIME]]></type>
|
||||
<defaultValue><![CDATA[CURRENT_TIMESTAMP]]></defaultValue>
|
||||
<forcedUnique><![CDATA[0]]></forcedUnique>
|
||||
<noQuoteDefault><![CDATA[1]]></noQuoteDefault>
|
||||
<notNull><![CDATA[1]]></notNull>
|
||||
<uid><![CDATA[C09B1BDB-8390-4B8A-B70A-642EC5E12899]]></uid>
|
||||
@@ -2959,16 +3011,16 @@
|
||||
<overviewPanelHidden><![CDATA[0]]></overviewPanelHidden>
|
||||
<pageBoundariesVisible><![CDATA[0]]></pageBoundariesVisible>
|
||||
<PageGridVisible><![CDATA[0]]></PageGridVisible>
|
||||
<RightSidebarWidth><![CDATA[1681.000000]]></RightSidebarWidth>
|
||||
<RightSidebarWidth><![CDATA[1405.000000]]></RightSidebarWidth>
|
||||
<sidebarIndex><![CDATA[2]]></sidebarIndex>
|
||||
<snapToGrid><![CDATA[0]]></snapToGrid>
|
||||
<SourceSidebarWidth><![CDATA[0.000000]]></SourceSidebarWidth>
|
||||
<SQLEditorFileFormatVersion><![CDATA[4]]></SQLEditorFileFormatVersion>
|
||||
<uid><![CDATA[58C99A00-06C9-478C-A667-C63842E088F3]]></uid>
|
||||
<windowHeight><![CDATA[1055.000000]]></windowHeight>
|
||||
<windowLocationX><![CDATA[0.000000]]></windowLocationX>
|
||||
<windowLocationY><![CDATA[24.000000]]></windowLocationY>
|
||||
<windowScrollOrigin><![CDATA[{157, 832}]]></windowScrollOrigin>
|
||||
<windowLocationX><![CDATA[1728.000000]]></windowLocationX>
|
||||
<windowLocationY><![CDATA[37.000000]]></windowLocationY>
|
||||
<windowScrollOrigin><![CDATA[{0, 597}]]></windowScrollOrigin>
|
||||
<windowWidth><![CDATA[1682.000000]]></windowWidth>
|
||||
</SQLDocumentInfo>
|
||||
<AllowsIndexRenamingOnInsert><![CDATA[1]]></AllowsIndexRenamingOnInsert>
|
||||
|
||||
@@ -22,7 +22,7 @@ values ('3f35518f-5a0d-4c2e-90a5-2407bb3b36f0', '38700987-c7a4-4685-a5bb-af378f9
|
||||
|
||||
-- create one service provider and one account
|
||||
insert into service_providers (service_provider_sid, name, root_domain)
|
||||
values ('2708b1b3-2736-40ea-b502-c53d8396247f', 'default service provider', 'sip.jambonz.us');
|
||||
values ('2708b1b3-2736-40ea-b502-c53d8396247f', 'default service provider', 'sip.jambonz.cloud');
|
||||
|
||||
insert into accounts (account_sid, service_provider_sid, name, webhook_secret)
|
||||
values ('9351f46a-678c-43f5-b8a6-d4eb58d131af','2708b1b3-2736-40ea-b502-c53d8396247f', 'default account', 'wh_secret_cJqgtMDPzDhhnjmaJH6Mtk');
|
||||
@@ -38,9 +38,9 @@ values ('3f35518f-5a0d-4c2e-90a5-2407bb3b36fs', '38700987-c7a4-4685-a5bb-af378f9
|
||||
-- create two applications
|
||||
insert into webhooks(webhook_sid, url, method)
|
||||
values
|
||||
('84e3db00-b172-4e46-b54b-a503fdb19e4a', 'https://public-apps.jambonz.us/call-status', 'POST'),
|
||||
('d31568d0-b193-4a05-8ff6-778369bc6efe', 'https://public-apps.jambonz.us/hello-world', 'POST'),
|
||||
('81844b05-714d-4295-8bf3-3b0640a4bf02', 'https://public-apps.jambonz.us/dial-time', 'POST');
|
||||
('84e3db00-b172-4e46-b54b-a503fdb19e4a', 'https://public-apps.jambonz.cloud/call-status', 'POST'),
|
||||
('d31568d0-b193-4a05-8ff6-778369bc6efe', 'https://public-apps.jambonz.cloud/hello-world', 'POST'),
|
||||
('81844b05-714d-4295-8bf3-3b0640a4bf02', 'https://public-apps.jambonz.cloud/dial-time', 'POST');
|
||||
|
||||
insert into applications (application_sid, account_sid, name, call_hook_sid, call_status_hook_sid, speech_synthesis_vendor, speech_synthesis_language, speech_synthesis_voice, speech_recognizer_vendor, speech_recognizer_language)
|
||||
VALUES
|
||||
|
||||
@@ -24,9 +24,9 @@ values ('09e92f3c-9d73-4303-b63f-3668574862ce', '1cf2f4f4-64c4-4249-9a3e-5bb4cb5
|
||||
-- create two applications
|
||||
insert into webhooks(webhook_sid, url, method)
|
||||
values
|
||||
('84e3db00-b172-4e46-b54b-a503fdb19e4a', 'https://public-apps.jambonz.us/call-status', 'POST'),
|
||||
('d31568d0-b193-4a05-8ff6-778369bc6efe', 'https://public-apps.jambonz.us/hello-world', 'POST'),
|
||||
('81844b05-714d-4295-8bf3-3b0640a4bf02', 'https://public-apps.jambonz.us/dial-time', 'POST');
|
||||
('84e3db00-b172-4e46-b54b-a503fdb19e4a', 'https://public-apps.jambonz.cloud/call-status', 'POST'),
|
||||
('d31568d0-b193-4a05-8ff6-778369bc6efe', 'https://public-apps.jambonz.cloud/hello-world', 'POST'),
|
||||
('81844b05-714d-4295-8bf3-3b0640a4bf02', 'https://public-apps.jambonz.cloud/dial-time', 'POST');
|
||||
|
||||
insert into applications (application_sid, account_sid, name, call_hook_sid, call_status_hook_sid, speech_synthesis_vendor, speech_synthesis_language, speech_synthesis_voice, speech_recognizer_vendor, speech_recognizer_language)
|
||||
VALUES
|
||||
|
||||
@@ -143,7 +143,7 @@ const sql = {
|
||||
8004: [
|
||||
'alter table accounts add column record_all_calls BOOLEAN NOT NULL DEFAULT false',
|
||||
'alter table accounts add column bucket_credential VARCHAR(8192)',
|
||||
'alter table accounts add column record_format VARCHAR(16) NOT NULL DEFAULT `mp3`',
|
||||
'alter table accounts add column record_format VARCHAR(16) NOT NULL DEFAULT \'mp3\'',
|
||||
'alter table applications add column record_all_calls BOOLEAN NOT NULL DEFAULT false',
|
||||
'alter table phone_numbers DROP INDEX number',
|
||||
'create unique index phone_numbers_unique_idx_voip_carrier_number ON phone_numbers (number,voip_carrier_sid)',
|
||||
@@ -157,7 +157,22 @@ const sql = {
|
||||
PRIMARY KEY (client_sid)
|
||||
)`,
|
||||
'CREATE INDEX client_sid_idx ON clients (client_sid)',
|
||||
'ALTER TABLE clients ADD CONSTRAINT account_sid_idxfk_13 FOREIGN KEY account_sid_idxfk_13 (account_sid) REFERENCES accounts (account_sid)'
|
||||
'ALTER TABLE clients ADD CONSTRAINT account_sid_idxfk_13 FOREIGN KEY account_sid_idxfk_13 (account_sid) REFERENCES accounts (account_sid)',
|
||||
'ALTER TABLE sip_gateways ADD COLUMN protocol ENUM(\'udp\',\'tcp\',\'tls\', \'tls/srtp\') DEFAULT \'udp\''
|
||||
],
|
||||
8005: [
|
||||
'DROP INDEX speech_credentials_idx_1 ON speech_credentials',
|
||||
'ALTER TABLE speech_credentials ADD COLUMN label VARCHAR(64)',
|
||||
'ALTER TABLE applications ADD COLUMN speech_synthesis_label VARCHAR(64)',
|
||||
'ALTER TABLE applications ADD COLUMN speech_recognizer_label VARCHAR(64)',
|
||||
'ALTER TABLE applications ADD COLUMN use_for_fallback_speech BOOLEAN DEFAULT false',
|
||||
'ALTER TABLE applications ADD COLUMN fallback_speech_synthesis_vendor VARCHAR(64)',
|
||||
'ALTER TABLE applications ADD COLUMN fallback_speech_synthesis_language VARCHAR(12)',
|
||||
'ALTER TABLE applications ADD COLUMN fallback_speech_synthesis_voice VARCHAR(64)',
|
||||
'ALTER TABLE applications ADD COLUMN fallback_speech_synthesis_label VARCHAR(64)',
|
||||
'ALTER TABLE applications ADD COLUMN fallback_speech_recognizer_vendor VARCHAR(64)',
|
||||
'ALTER TABLE applications ADD COLUMN fallback_speech_recognizer_language VARCHAR(64)',
|
||||
'ALTER TABLE applications ADD COLUMN fallback_speech_recognizer_label VARCHAR(64)',
|
||||
]
|
||||
};
|
||||
|
||||
@@ -188,6 +203,8 @@ const doIt = async() => {
|
||||
if (val < 7007) upgrades.push(...sql['7007']);
|
||||
if (val < 8000) upgrades.push(...sql['8000']);
|
||||
if (val < 8003) upgrades.push(...sql['8003']);
|
||||
if (val < 8004) upgrades.push(...sql['8004']);
|
||||
if (val < 8005) upgrades.push(...sql['8005']);
|
||||
|
||||
// perform all upgrades
|
||||
logger.info({upgrades}, 'applying schema upgrades..');
|
||||
|
||||
@@ -2,7 +2,7 @@ SET FOREIGN_KEY_CHECKS=0;
|
||||
|
||||
-- create one service provider
|
||||
insert into service_providers (service_provider_sid, name, description, root_domain)
|
||||
values ('2708b1b3-2736-40ea-b502-c53d8396247f', 'jambonz.us', 'jambonz.us service provider', 'sip.yakeeda.com');
|
||||
values ('2708b1b3-2736-40ea-b502-c53d8396247f', 'jambonz.cloud', 'jambonz.cloud service provider', 'sip.yakeeda.com');
|
||||
insert into api_keys (api_key_sid, token)
|
||||
values ('3f35518f-5a0d-4c2e-90a5-2407bb3b36f0', '38700987-c7a4-4685-a5bb-af378f9734de');
|
||||
|
||||
@@ -19,8 +19,8 @@ insert into sip_gateways (sip_gateway_sid, voip_carrier_sid, ipv4, port, inbound
|
||||
values ('46b727eb-c7dc-44fa-b063-96e48d408e4a', '5145b436-2f38-4029-8d4c-fd8c67831c7a', '3.3.3.3', 5060, 1, 1, 1);
|
||||
|
||||
-- create the test application and test phone number
|
||||
insert into webhooks (webhook_sid, url, method) values ('d9c205c6-a129-443e-a9c0-d1bb437d4bb7', 'https://flows.jambonz.us/testCall', 'POST');
|
||||
insert into webhooks (webhook_sid, url, method) values ('6ac36aeb-6bd0-428a-80a1-aed95640a296', 'https://flows.jambonz.us/callStatus', 'POST');
|
||||
insert into webhooks (webhook_sid, url, method) values ('d9c205c6-a129-443e-a9c0-d1bb437d4bb7', 'https://flows.jambonz.cloud/testCall', 'POST');
|
||||
insert into webhooks (webhook_sid, url, method) values ('6ac36aeb-6bd0-428a-80a1-aed95640a296', 'https://flows.jambonz.cloud/callStatus', 'POST');
|
||||
insert into applications (application_sid, name, service_provider_sid, call_hook_sid, call_status_hook_sid,
|
||||
speech_synthesis_vendor, speech_synthesis_language, speech_synthesis_voice, speech_recognizer_vendor, speech_recognizer_language)
|
||||
values ('7a489343-02ed-471e-8df0-fc5e1b98ce8f', 'Test application', '2708b1b3-2736-40ea-b502-c53d8396247f',
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
const opts = Object.assign({
|
||||
timestamp: () => {
|
||||
return `, "time": "${new Date().toISOString()}"`;
|
||||
}
|
||||
}, {
|
||||
const opts = {
|
||||
level: process.env.JAMBONES_LOGLEVEL || 'info'
|
||||
});
|
||||
|
||||
const logger = require('pino')(opts);
|
||||
};
|
||||
const pino = require('pino');
|
||||
const logger = pino(opts, pino.destination(1, {sync: false}));
|
||||
|
||||
module.exports = logger;
|
||||
|
||||
@@ -34,7 +34,7 @@ AND effective_end_date IS NULL
|
||||
AND pending=0`;
|
||||
|
||||
const updatePaymentInfoSql = `UPDATE account_subscriptions
|
||||
SET last4 = ?, exp_month = ?, exp_year = ?, card_type = ?
|
||||
SET last4 = ?, stripe_payment_method_id=?, exp_month = ?, exp_year = ?, card_type = ?
|
||||
WHERE account_sid = ?
|
||||
AND effective_end_date IS NULL`;
|
||||
|
||||
@@ -206,10 +206,10 @@ class Account extends Model {
|
||||
}
|
||||
|
||||
static async updatePaymentInfo(logger, account_sid, pm) {
|
||||
const {card} = pm;
|
||||
const {id, card} = pm;
|
||||
const last4_encrypted = encrypt(card.last4);
|
||||
await promisePool.execute(updatePaymentInfoSql,
|
||||
[last4_encrypted, card.exp_month, card.exp_year, card.brand, account_sid]);
|
||||
[last4_encrypted, id, card.exp_month, card.exp_year, card.brand, account_sid]);
|
||||
}
|
||||
|
||||
static async provisionPendingSubscription(logger, account_sid, products, payment_method, subscription_id) {
|
||||
|
||||
@@ -20,6 +20,17 @@ class SpeechCredential extends Model {
|
||||
return rows;
|
||||
}
|
||||
|
||||
static async isAvailableVendorAndLabel(service_provider_sid, account_sid, vendor, label) {
|
||||
let sql;
|
||||
if (account_sid) {
|
||||
sql = 'SELECT * FROM speech_credentials WHERE account_sid = ? AND vendor = ? AND label = ?';
|
||||
} else {
|
||||
sql = 'SELECT * FROM speech_credentials WHERE service_provider_sid = ? AND vendor = ? AND label = ?';
|
||||
}
|
||||
const [rows] = await promisePool.query(sql, [account_sid ? account_sid : service_provider_sid, vendor, label]);
|
||||
return rows;
|
||||
}
|
||||
|
||||
static async disableStt(account_sid) {
|
||||
await promisePool.execute('UPDATE speech_credentials SET use_for_stt = 0 WHERE account_sid = ?', [account_sid]);
|
||||
}
|
||||
@@ -86,6 +97,10 @@ SpeechCredential.fields = [
|
||||
{
|
||||
name: 'last_tested',
|
||||
type: 'date'
|
||||
},
|
||||
{
|
||||
name: 'label',
|
||||
type: 'string'
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
41
lib/record/azure-storage.js
Normal file
41
lib/record/azure-storage.js
Normal file
@@ -0,0 +1,41 @@
|
||||
const { Writable } = require('stream');
|
||||
const { BlobServiceClient } = require('@azure/storage-blob');
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
|
||||
class AzureStorageUploadStream extends Writable {
|
||||
constructor(logger, opts) {
|
||||
super(opts);
|
||||
const blobServiceClient = BlobServiceClient.fromConnectionString(opts.connection_string);
|
||||
this.blockBlobClient = blobServiceClient.getContainerClient(opts.bucketName).getBlockBlobClient(opts.Key);
|
||||
this.metadata = opts.metadata;
|
||||
this.blocks = [];
|
||||
}
|
||||
|
||||
async _write(chunk, encoding, callback) {
|
||||
const blockID = uuidv4().replace(/-/g, '');
|
||||
this.blocks.push(blockID);
|
||||
try {
|
||||
await this.blockBlobClient.stageBlock(blockID, chunk, chunk.length);
|
||||
callback();
|
||||
} catch (error) {
|
||||
callback(error);
|
||||
}
|
||||
}
|
||||
|
||||
async _final(callback) {
|
||||
try {
|
||||
await this.blockBlobClient.commitBlockList(this.blocks);
|
||||
// remove all null/undefined props
|
||||
const filteredObj = Object.entries(this.metadata).reduce((acc, [key, val]) => {
|
||||
if (val !== undefined && val !== null) acc[key] = val;
|
||||
return acc;
|
||||
}, {});
|
||||
await this.blockBlobClient.setMetadata(filteredObj);
|
||||
callback();
|
||||
} catch (error) {
|
||||
callback(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AzureStorageUploadStream;
|
||||
41
lib/record/google-storage.js
Normal file
41
lib/record/google-storage.js
Normal file
@@ -0,0 +1,41 @@
|
||||
const { Storage } = require('@google-cloud/storage');
|
||||
const { Writable } = require('stream');
|
||||
|
||||
class GoogleStorageUploadStream extends Writable {
|
||||
|
||||
constructor(logger, opts) {
|
||||
super(opts);
|
||||
this.logger = logger;
|
||||
this.metadata = opts.metadata;
|
||||
|
||||
const storage = new Storage(opts.bucketCredential);
|
||||
this.gcsFile = storage.bucket(opts.bucketName).file(opts.Key);
|
||||
this.writeStream = this.gcsFile.createWriteStream();
|
||||
|
||||
this.writeStream.on('error', (err) => this.logger.error(err));
|
||||
this.writeStream.on('finish', () => {
|
||||
this.logger.info('google storage Upload completed.');
|
||||
this._addMetadata();
|
||||
});
|
||||
}
|
||||
|
||||
_write(chunk, encoding, callback) {
|
||||
this.writeStream.write(chunk, encoding, callback);
|
||||
}
|
||||
|
||||
_final(callback) {
|
||||
this.writeStream.end();
|
||||
this.writeStream.once('finish', callback);
|
||||
}
|
||||
|
||||
async _addMetadata() {
|
||||
try {
|
||||
await this.gcsFile.setMetadata({metadata: this.metadata});
|
||||
this.logger.info('Google storage Upload and metadata setting completed.');
|
||||
} catch (err) {
|
||||
this.logger.error(err, 'Google storage An error occurred while setting metadata');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = GoogleStorageUploadStream;
|
||||
@@ -1,17 +1,6 @@
|
||||
|
||||
const path = require('node:path');
|
||||
async function record(logger, socket, url) {
|
||||
const p = path.basename(url);
|
||||
const idx = p.lastIndexOf('/');
|
||||
const vendor = p.substring(idx + 1);
|
||||
switch (vendor) {
|
||||
case 'aws_s3':
|
||||
return require('./s3')(logger, socket);
|
||||
default:
|
||||
logger.info(`unknown bucket vendor: ${vendor}`);
|
||||
socket.send(`unknown bucket vendor: ${vendor}`);
|
||||
socket.close();
|
||||
}
|
||||
async function record(logger, socket) {
|
||||
return require('./upload')(logger, socket);
|
||||
}
|
||||
|
||||
module.exports = record;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
const Account = require('../models/account');
|
||||
const Websocket = require('ws');
|
||||
const PCMToMP3Encoder = require('./encoder');
|
||||
const S3MultipartUploadStream = require('./s3-multipart-upload-stream');
|
||||
const wav = require('wav');
|
||||
const { getUploader } = require('./utils');
|
||||
|
||||
async function upload(logger, socket) {
|
||||
|
||||
@@ -11,6 +11,7 @@ async function upload(logger, socket) {
|
||||
try {
|
||||
if (!isBinary && !socket._recvInitialMetadata) {
|
||||
socket._recvInitialMetadata = true;
|
||||
logger.debug(`initial metadata: ${data}`);
|
||||
const obj = JSON.parse(data.toString());
|
||||
logger.info({obj}, 'received JSON message from jambonz');
|
||||
const {sampleRate, accountSid, callSid, direction, from, to,
|
||||
@@ -42,23 +43,15 @@ async function upload(logger, socket) {
|
||||
Key += `/${day.getDate().toString().padStart(2, '0')}/${callSid}.${account[0].record_format}`;
|
||||
|
||||
// Uploader
|
||||
const uploaderOpts = {
|
||||
bucketName: obj.name,
|
||||
Key,
|
||||
metadata,
|
||||
bucketCredential: {
|
||||
credentials: {
|
||||
accessKeyId: obj.access_key_id,
|
||||
secretAccessKey: obj.secret_access_key,
|
||||
},
|
||||
region: obj.region || 'us-east-1'
|
||||
}
|
||||
};
|
||||
const uploadStream = new S3MultipartUploadStream(logger, uploaderOpts);
|
||||
const uploadStream = getUploader(Key, metadata, obj, logger);
|
||||
if (!uploadStream) {
|
||||
logger.info('There is no available record uploader, close the socket.');
|
||||
socket.close();
|
||||
}
|
||||
|
||||
/**encoder */
|
||||
let encoder;
|
||||
if (obj.output_format === 'wav') {
|
||||
if (account[0].record_format === 'wav') {
|
||||
encoder = new wav.Writer({ channels: 2, sampleRate, bitDepth: 16 });
|
||||
} else {
|
||||
// default is mp3
|
||||
@@ -77,7 +70,7 @@ async function upload(logger, socket) {
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error({err}, 'error parsing message during connection');
|
||||
logger.error({err, data}, 'error parsing message during connection');
|
||||
}
|
||||
});
|
||||
socket.on('error', function(err) {
|
||||
44
lib/record/utils.js
Normal file
44
lib/record/utils.js
Normal file
@@ -0,0 +1,44 @@
|
||||
const AzureStorageUploadStream = require('./azure-storage');
|
||||
const GoogleStorageUploadStream = require('./google-storage');
|
||||
const S3MultipartUploadStream = require('./s3-multipart-upload-stream');
|
||||
|
||||
const getUploader = (Key, metadata, bucket_credential, logger) => {
|
||||
const uploaderOpts = {
|
||||
bucketName: bucket_credential.name,
|
||||
Key,
|
||||
metadata
|
||||
};
|
||||
switch (bucket_credential.vendor) {
|
||||
case 'aws_s3':
|
||||
uploaderOpts.bucketCredential = {
|
||||
credentials: {
|
||||
accessKeyId: bucket_credential.access_key_id,
|
||||
secretAccessKey: bucket_credential.secret_access_key,
|
||||
},
|
||||
region: bucket_credential.region || 'us-east-1'
|
||||
};
|
||||
return new S3MultipartUploadStream(logger, uploaderOpts);
|
||||
case 'google':
|
||||
const serviceKey = JSON.parse(bucket_credential.service_key);
|
||||
uploaderOpts.bucketCredential = {
|
||||
projectId: serviceKey.project_id,
|
||||
credentials: {
|
||||
client_email: serviceKey.client_email,
|
||||
private_key: serviceKey.private_key
|
||||
}
|
||||
};
|
||||
return new GoogleStorageUploadStream(logger, uploaderOpts);
|
||||
case 'azure':
|
||||
uploaderOpts.connection_string = bucket_credential.connection_string;
|
||||
return new AzureStorageUploadStream(logger, uploaderOpts);
|
||||
|
||||
default:
|
||||
logger.error(`unknown bucket vendor: ${bucket_credential.vendor}`);
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getUploader
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
const router = require('express').Router();
|
||||
const assert = require('assert');
|
||||
const request = require('request');
|
||||
const {DbErrorBadRequest, DbErrorForbidden, DbErrorUnprocessableRequest, DbError} = require('../../utils/errors');
|
||||
const {DbErrorBadRequest, DbErrorForbidden, DbErrorUnprocessableRequest} = require('../../utils/errors');
|
||||
const Account = require('../../models/account');
|
||||
const Application = require('../../models/application');
|
||||
const Webhook = require('../../models/webhook');
|
||||
@@ -23,8 +23,8 @@ const {
|
||||
} = require('./utils');
|
||||
const short = require('short-uuid');
|
||||
const VoipCarrier = require('../../models/voip-carrier');
|
||||
const { encrypt, decrypt } = require('../../utils/encrypt-decrypt');
|
||||
const { testAwsS3 } = require('../../utils/storage-utils');
|
||||
const { encrypt } = require('../../utils/encrypt-decrypt');
|
||||
const { testAwsS3, testGoogleStorage, testAzureStorage } = require('../../utils/storage-utils');
|
||||
const translator = short();
|
||||
|
||||
let idx = 0;
|
||||
@@ -41,20 +41,14 @@ const getFsUrl = async(logger, retrieveSet, setName) => {
|
||||
logger.info('No available feature servers to handle createCall API request');
|
||||
return ;
|
||||
}
|
||||
const ip = stripPort(fs[idx++ % fs.length]);
|
||||
logger.info({fs}, `feature servers available for createCall API request, selecting ${ip}`);
|
||||
return `http://${ip}:3000/v1/createCall`;
|
||||
const f = fs[idx++ % fs.length];
|
||||
logger.info({fs}, `feature servers available for createCall API request, selecting ${f}`);
|
||||
return `${f}/v1/createCall`;
|
||||
} catch (err) {
|
||||
logger.error({err}, 'getFsUrl: error retreving feature servers from redis');
|
||||
}
|
||||
};
|
||||
|
||||
const stripPort = (hostport) => {
|
||||
const arr = /^(.*):(.*)$/.exec(hostport);
|
||||
if (arr) return arr[1];
|
||||
return hostport;
|
||||
};
|
||||
|
||||
const validateRequest = async(req, account_sid) => {
|
||||
try {
|
||||
if (req.user.hasScope('admin')) {
|
||||
@@ -222,8 +216,8 @@ function validateUpdateCall(opts) {
|
||||
throw new DbErrorBadRequest('invalid conf_mute_status');
|
||||
}
|
||||
if (opts.sip_request &&
|
||||
(!opts.sip_request.method && !opts.sip_request.content_type || !opts.sip_request.content_type)) {
|
||||
throw new DbErrorBadRequest('sip_request requires content_type and content properties');
|
||||
(!opts.sip_request.method || !opts.sip_request.content_type || !opts.sip_request.content)) {
|
||||
throw new DbErrorBadRequest('sip_request requires method, content_type and content properties');
|
||||
}
|
||||
if (opts.record && !opts.record.action) {
|
||||
throw new DbErrorBadRequest('record requires action property');
|
||||
@@ -547,7 +541,9 @@ function encryptBucketCredential(obj) {
|
||||
name,
|
||||
access_key_id,
|
||||
secret_access_key,
|
||||
tags
|
||||
tags,
|
||||
service_key,
|
||||
connection_string
|
||||
} = obj.bucket_credential;
|
||||
|
||||
switch (vendor) {
|
||||
@@ -560,11 +556,22 @@ function encryptBucketCredential(obj) {
|
||||
secret_access_key, tags});
|
||||
obj.bucket_credential = encrypt(awsData);
|
||||
break;
|
||||
case 'google':
|
||||
assert(service_key, 'invalid google cloud storage credential: service_key is required');
|
||||
const googleData = JSON.stringify({vendor, name, service_key, tags});
|
||||
obj.bucket_credential = encrypt(googleData);
|
||||
break;
|
||||
case 'azure':
|
||||
assert(name, 'invalid azure container name: name is required');
|
||||
assert(connection_string, 'invalid azure cloud storage credential: connection_string is required');
|
||||
const azureData = JSON.stringify({vendor, name, connection_string, tags});
|
||||
obj.bucket_credential = encrypt(azureData);
|
||||
break;
|
||||
case 'none':
|
||||
obj.bucket_credential = null;
|
||||
break;
|
||||
default:
|
||||
throw DbErrorBadRequest(`unknow storage vendor: ${vendor}`);
|
||||
throw new DbErrorBadRequest(`unknow storage vendor: ${vendor}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -714,35 +721,24 @@ router.post('/:sid/BucketCredentialTest', async(req, res) => {
|
||||
try {
|
||||
const account_sid = parseAccountSid(req);
|
||||
await validateRequest(req, account_sid);
|
||||
let {vendor, name, region, access_key_id, secret_access_key} = req.body;
|
||||
const {vendor, name, region, access_key_id, secret_access_key, service_key, connection_string} = req.body;
|
||||
const ret = {
|
||||
status: 'not tested'
|
||||
};
|
||||
|
||||
if (secret_access_key.endsWith('XXXXXX')) {
|
||||
// this is when the password already saved in account
|
||||
const service_provider_sid = req.user.hasServiceProviderAuth ? req.user.service_provider_sid : null;
|
||||
const results = await Account.retrieve(account_sid, service_provider_sid);
|
||||
if (results.length === 0) throw new DbError('Invalid Account Sid');
|
||||
const {bucket_credential} = results[0];
|
||||
if (bucket_credential) {
|
||||
const o = JSON.parse(decrypt(bucket_credential));
|
||||
vendor = o.vendor;
|
||||
switch (vendor) {
|
||||
case 'aws_s3':
|
||||
name = o.name;
|
||||
region = o.region;
|
||||
access_key_id = o.access_key_id;
|
||||
secret_access_key = o.secret_access_key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
switch (vendor) {
|
||||
case 'aws_s3':
|
||||
await testAwsS3(logger, {vendor, name, region, access_key_id, secret_access_key});
|
||||
ret.status = 'ok';
|
||||
break;
|
||||
case 'google':
|
||||
await testGoogleStorage(logger, {vendor, name, service_key});
|
||||
ret.status = 'ok';
|
||||
break;
|
||||
case 'azure':
|
||||
await testAzureStorage(logger, {vendor, name, connection_string});
|
||||
ret.status = 'ok';
|
||||
break;
|
||||
default:
|
||||
throw new DbErrorBadRequest(`Does not support test for ${vendor}`);
|
||||
}
|
||||
@@ -776,7 +772,7 @@ router.get('/:sid/ApiKeys', async(req, res) => {
|
||||
*/
|
||||
router.post('/:sid/Calls', async(req, res) => {
|
||||
const {retrieveSet, logger} = req.app.locals;
|
||||
const setName = `${(process.env.JAMBONES_CLUSTER_ID || 'default')}:active-fs`;
|
||||
const setName = `${(process.env.JAMBONES_CLUSTER_ID || 'default')}:fs-service-url`;
|
||||
const serviceUrl = await getFsUrl(logger, retrieveSet, setName);
|
||||
|
||||
if (!serviceUrl) {
|
||||
@@ -937,7 +933,7 @@ router.post('/:sid/Messages', async(req, res) => {
|
||||
const account_sid = parseAccountSid(req);
|
||||
await validateRequest(req, account_sid);
|
||||
|
||||
const setName = `${(process.env.JAMBONES_CLUSTER_ID || 'default')}:active-fs`;
|
||||
const setName = `${(process.env.JAMBONES_CLUSTER_ID || 'default')}:fs-service-url`;
|
||||
const serviceUrl = await getFsUrl(logger, retrieveSet, setName);
|
||||
if (!serviceUrl) res.json({msg: 'no available feature servers at this time'}).status(480);
|
||||
await validateCreateMessage(logger, account_sid, req);
|
||||
|
||||
@@ -6,6 +6,7 @@ const {validateEmail, emailSimpleText} = require('../../utils/email-utils');
|
||||
const {promisePool} = require('../../db');
|
||||
const {cacheClient} = require('../../helpers');
|
||||
const sysError = require('../error');
|
||||
const assert = require('assert');
|
||||
const sql = `SELECT * from users user
|
||||
LEFT JOIN accounts AS acc
|
||||
ON acc.account_sid = user.account_sid
|
||||
@@ -26,7 +27,8 @@ function createOauthEmailText(provider) {
|
||||
}
|
||||
|
||||
function createResetEmailText(link) {
|
||||
const baseUrl = 'http://localhost:3001';
|
||||
assert(process.env.JAMBONZ_BASE_URL, 'process.env.JAMBONZ_BASE_URL is missing');
|
||||
const baseUrl = process.env.JAMBONZ_BASE_URL;
|
||||
|
||||
return `Hi there!
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ const {DbErrorBadRequest} = require('../../utils/errors');
|
||||
const {getHomerApiKey, getHomerSipTrace, getHomerPcap} = require('../../utils/homer-utils');
|
||||
const {getJaegerTrace} = require('../../utils/jaeger-utils');
|
||||
const Account = require('../../models/account');
|
||||
const { getS3Object } = require('../../utils/storage-utils');
|
||||
const { getS3Object, getGoogleStorageObject, getAzureStorageObject } = require('../../utils/storage-utils');
|
||||
|
||||
const parseAccountSid = (url) => {
|
||||
const arr = /Accounts\/([^\/]*)/.exec(url);
|
||||
@@ -22,7 +22,7 @@ router.get('/', async(req, res) => {
|
||||
logger.debug({opts: req.query}, 'GET /RecentCalls');
|
||||
const account_sid = parseAccountSid(req.originalUrl);
|
||||
const service_provider_sid = account_sid ? null : parseServiceProviderSid(req.originalUrl);
|
||||
const {page, count, trunk, direction, days, answered, start, end, from, to} = req.query || {};
|
||||
const {page, count, trunk, direction, days, answered, start, end, filter} = req.query || {};
|
||||
if (!page || page < 1) throw new DbErrorBadRequest('missing or invalid "page" query arg');
|
||||
if (!count || count < 25 || count > 500) throw new DbErrorBadRequest('missing or invalid "count" query arg');
|
||||
|
||||
@@ -37,8 +37,7 @@ router.get('/', async(req, res) => {
|
||||
answered,
|
||||
start: days ? undefined : start,
|
||||
end: days ? undefined : end,
|
||||
from,
|
||||
to
|
||||
filter
|
||||
});
|
||||
res.status(200).json(data);
|
||||
}
|
||||
@@ -53,8 +52,7 @@ router.get('/', async(req, res) => {
|
||||
answered,
|
||||
start: days ? undefined : start,
|
||||
end: days ? undefined : end,
|
||||
from,
|
||||
to
|
||||
filter
|
||||
});
|
||||
res.status(200).json(data);
|
||||
}
|
||||
@@ -126,22 +124,29 @@ router.get('/:call_sid/record/:year/:month/:day/:format', async(req, res) => {
|
||||
const r = await Account.retrieve(account_sid);
|
||||
if (r.length === 0 || !r[0].bucket_credential) return res.sendStatus(404);
|
||||
const {bucket_credential} = r[0];
|
||||
const getOptions = {
|
||||
...bucket_credential,
|
||||
key: `${year}/${month}/${day}/${call_sid}.${format || 'mp3'}`
|
||||
};
|
||||
let stream;
|
||||
switch (bucket_credential.vendor) {
|
||||
case 'aws_s3':
|
||||
const getS3Options = {
|
||||
...bucket_credential,
|
||||
key: `${year}/${month}/${day}/${call_sid}.${format || 'mp3'}`
|
||||
};
|
||||
const stream = await getS3Object(logger, getS3Options);
|
||||
res.set({
|
||||
'Content-Type': `audio/${format || 'mp3'}`
|
||||
});
|
||||
stream.pipe(res);
|
||||
stream = await getS3Object(logger, getOptions);
|
||||
break;
|
||||
case 'google':
|
||||
stream = await getGoogleStorageObject(logger, getOptions);
|
||||
break;
|
||||
case 'azure':
|
||||
stream = await getAzureStorageObject(logger, getOptions);
|
||||
break;
|
||||
default:
|
||||
logger.error(`There is no handler for fetching record from ${bucket_credential.vendor}`);
|
||||
return res.sendStatus(500);
|
||||
}
|
||||
res.set({
|
||||
'Content-Type': `audio/${format || 'mp3'}`
|
||||
});
|
||||
stream.pipe(res);
|
||||
} catch (err) {
|
||||
logger.error({err}, ` error retrieving recording ${call_sid}`);
|
||||
res.sendStatus(404);
|
||||
|
||||
@@ -16,8 +16,9 @@ const insertUserSql = `INSERT into users
|
||||
(user_sid, account_sid, name, email, provider, provider_userid, email_validated)
|
||||
values (?, ?, ?, ?, ?, ?, 1)`;
|
||||
const insertUserLocalSql = `INSERT into users
|
||||
(user_sid, account_sid, name, email, email_activation_code, email_validated, provider, hashed_password)
|
||||
values (?, ?, ?, ?, ?, 0, 'local', ?)`;
|
||||
(user_sid, account_sid, name, email, email_activation_code, email_validated, provider,
|
||||
hashed_password, service_provider_sid)
|
||||
values (?, ?, ?, ?, ?, 0, 'local', ?, ?)`;
|
||||
const insertAccountSql = `INSERT into accounts
|
||||
(account_sid, service_provider_sid, name, is_active, webhook_secret, trial_end_date)
|
||||
values (?, ?, ?, ?, ?, CURDATE() + INTERVAL 21 DAY)`;
|
||||
@@ -36,7 +37,7 @@ const insertSignupHistorySql = `INSERT into signup_history
|
||||
values (?, ?)`;
|
||||
|
||||
const addLocalUser = async(logger, user_sid, account_sid,
|
||||
name, email, email_activation_code, passwordHash) => {
|
||||
name, email, email_activation_code, passwordHash, service_provider_sid) => {
|
||||
const [r] = await promisePool.execute(insertUserLocalSql,
|
||||
[
|
||||
user_sid,
|
||||
@@ -44,7 +45,8 @@ const addLocalUser = async(logger, user_sid, account_sid,
|
||||
name,
|
||||
email,
|
||||
email_activation_code,
|
||||
passwordHash
|
||||
passwordHash,
|
||||
service_provider_sid
|
||||
]);
|
||||
debug({r}, 'Result from adding user');
|
||||
};
|
||||
@@ -145,7 +147,7 @@ router.post('/', async(req, res) => {
|
||||
const user = await doGithubAuth(logger, req.body);
|
||||
logger.info({user}, 'retrieved user details from github');
|
||||
Object.assign(userProfile, {
|
||||
name: user.name,
|
||||
name: user.email,
|
||||
email: user.email,
|
||||
email_validated: user.email_validated,
|
||||
avatar_url: user.avatar_url,
|
||||
@@ -157,7 +159,7 @@ router.post('/', async(req, res) => {
|
||||
const user = await doGoogleAuth(logger, req.body);
|
||||
logger.info({user}, 'retrieved user details from google');
|
||||
Object.assign(userProfile, {
|
||||
name: user.name || user.email,
|
||||
name: user.email || user.email,
|
||||
email: user.email,
|
||||
email_validated: user.verified_email,
|
||||
picture: user.picture,
|
||||
@@ -170,7 +172,7 @@ router.post('/', async(req, res) => {
|
||||
logger.info({user}, 'retrieved user details for local provider');
|
||||
debug({user}, 'retrieved user details for local provider');
|
||||
Object.assign(userProfile, {
|
||||
name: user.name,
|
||||
name: user.email,
|
||||
email: user.email,
|
||||
provider: 'local',
|
||||
email_activation_code: user.email_activation_code
|
||||
@@ -280,7 +282,8 @@ router.post('/', async(req, res) => {
|
||||
const passwordHash = await generateHashedPassword(req.body.password);
|
||||
debug(`hashed password: ${passwordHash}`);
|
||||
await addLocalUser(logger, userProfile.user_sid, userProfile.account_sid,
|
||||
userProfile.name, userProfile.email, userProfile.email_activation_code, passwordHash);
|
||||
userProfile.name, userProfile.email, userProfile.email_activation_code,
|
||||
passwordHash, req.body.service_provider_sid);
|
||||
debug('added local user');
|
||||
}
|
||||
else {
|
||||
@@ -295,9 +298,12 @@ router.post('/', async(req, res) => {
|
||||
const dialTimeSid = uuid();
|
||||
|
||||
/* 3 webhooks */
|
||||
await promisePool.execute(insertWebookSql, [callStatusSid, 'https://public-apps.jambonz.us/call-status', 'POST']);
|
||||
await promisePool.execute(insertWebookSql, [helloWordSid, 'https://public-apps.jambonz.us/hello-world', 'POST']);
|
||||
await promisePool.execute(insertWebookSql, [dialTimeSid, 'https://public-apps.jambonz.us/dial-time', 'POST']);
|
||||
await promisePool.execute(insertWebookSql, [callStatusSid,
|
||||
'https://public-apps.jambonz.cloud/call-status', 'POST']);
|
||||
await promisePool.execute(insertWebookSql, [helloWordSid,
|
||||
'https://public-apps.jambonz.cloud/hello-world', 'POST']);
|
||||
await promisePool.execute(insertWebookSql, [dialTimeSid,
|
||||
'https://public-apps.jambonz.cloud/dial-time', 'POST']);
|
||||
|
||||
/* 2 applications */
|
||||
await promisePool.execute(insertApplicationSql, [uuid(), userProfile.account_sid, 'hello world',
|
||||
@@ -327,7 +333,7 @@ router.post('/', async(req, res) => {
|
||||
|
||||
await addLocalUser(logger, userProfile.user_sid, userProfile.account_sid,
|
||||
userProfile.name, userProfile.email, userProfile.email_activation_code,
|
||||
passwordHash);
|
||||
passwordHash, req.body.service_provider_sid);
|
||||
|
||||
/* note: we deactivate the old user once the new email is validated */
|
||||
}
|
||||
@@ -349,6 +355,8 @@ router.post('/', async(req, res) => {
|
||||
const token = jwt.sign({
|
||||
user_sid: userProfile.user_sid,
|
||||
account_sid: userProfile.account_sid,
|
||||
service_provider_sid: req.body.service_provider_sid,
|
||||
scope: 'account',
|
||||
email: userProfile.email,
|
||||
name: userProfile.name
|
||||
}, process.env.JWT_SECRET, { expiresIn });
|
||||
|
||||
@@ -28,20 +28,13 @@ router.get('/', async(req, res) => {
|
||||
|
||||
if (req.user.hasAccountAuth) {
|
||||
const [r] = await promisePool.query('SELECT * from accounts WHERE account_sid = ?', req.user.account_sid);
|
||||
if (0 === r.length) throw new Error('invalid account_sid');
|
||||
if (0 === r.length) throw new DbErrorBadRequest('invalid account_sid');
|
||||
|
||||
service_provider_sid = r[0].service_provider_sid;
|
||||
}
|
||||
|
||||
if (req.user.hasServiceProviderAuth) {
|
||||
const [r] = await promisePool.query(
|
||||
'SELECT * from service_providers where service_provider_sid = ?',
|
||||
service_provider_sid);
|
||||
if (0 === r.length) throw new Error('invalid account_sid');
|
||||
|
||||
service_provider_sid = r[0].service_provider_sid;
|
||||
|
||||
if (!service_provider_sid) throw new DbErrorBadRequest('missing service_provider_sid in query');
|
||||
service_provider_sid = req.user.service_provider_sid;
|
||||
}
|
||||
|
||||
/** generally, we have a global set of SBCs that all accounts use.
|
||||
|
||||
@@ -14,20 +14,14 @@ const getFsUrl = async(logger, retrieveSet, setName, provider) => {
|
||||
logger.info('No available feature servers to handle createCall API request');
|
||||
return ;
|
||||
}
|
||||
const ip = stripPort(fs[idx++ % fs.length]);
|
||||
logger.info({fs}, `feature servers available for createCall API request, selecting ${ip}`);
|
||||
return `http://${ip}:3000/v1/messaging/${provider}`;
|
||||
const f = fs[idx++ % fs.length];
|
||||
logger.info({fs}, `feature servers available for createCall API request, selecting ${f}`);
|
||||
return `${f}/v1/messaging/${provider}`;
|
||||
} catch (err) {
|
||||
logger.error({err}, 'getFsUrl: error retreving feature servers from redis');
|
||||
}
|
||||
};
|
||||
|
||||
const stripPort = (hostport) => {
|
||||
const arr = /^(.*):(.*)$/.exec(hostport);
|
||||
if (arr) return arr[1];
|
||||
return hostport;
|
||||
};
|
||||
|
||||
const doSendResponse = async(res, respondFn, body) => {
|
||||
if (typeof respondFn === 'number') res.sendStatus(respondFn);
|
||||
else if (typeof respondFn !== 'function') res.sendStatus(200);
|
||||
@@ -44,7 +38,7 @@ router.post('/:provider', async(req, res) => {
|
||||
lookupAppByPhoneNumber,
|
||||
logger
|
||||
} = req.app.locals;
|
||||
const setName = `${process.env.JAMBONES_CLUSTER_ID || 'default'}:active-fs`;
|
||||
const setName = `${process.env.JAMBONES_CLUSTER_ID || 'default'}:fs-service-url`;
|
||||
logger.debug({path: req.path, body: req.body}, 'incomingSMS from carrier');
|
||||
|
||||
// search for provider module
|
||||
|
||||
@@ -207,6 +207,7 @@ router.post('/', async(req, res) => {
|
||||
use_for_stt,
|
||||
use_for_tts,
|
||||
vendor,
|
||||
label
|
||||
} = req.body;
|
||||
const account_sid = req.user.account_sid || req.body.account_sid;
|
||||
const service_provider_sid = req.user.service_provider_sid ||
|
||||
@@ -221,11 +222,21 @@ router.post('/', async(req, res) => {
|
||||
}
|
||||
}
|
||||
|
||||
// Check if vendor and label is already used for account or SP
|
||||
if (label) {
|
||||
const existingSpeech = await SpeechCredential.isAvailableVendorAndLabel(
|
||||
service_provider_sid, account_sid, vendor, label);
|
||||
if (existingSpeech.length > 0) {
|
||||
throw new DbErrorUnprocessableRequest(`Label ${label} is already in use for another speech credential`);
|
||||
}
|
||||
}
|
||||
|
||||
const encrypted_credential = encryptCredential(req.body);
|
||||
const uuid = await SpeechCredential.make({
|
||||
account_sid,
|
||||
service_provider_sid,
|
||||
vendor,
|
||||
label,
|
||||
use_for_tts,
|
||||
use_for_stt,
|
||||
credential: encrypted_credential
|
||||
@@ -478,7 +489,9 @@ router.put('/:sid', async(req, res) => {
|
||||
use_custom_tts,
|
||||
custom_tts_endpoint,
|
||||
use_custom_stt,
|
||||
custom_stt_endpoint
|
||||
custom_stt_endpoint,
|
||||
custom_stt_url,
|
||||
custom_tts_url
|
||||
} = req.body;
|
||||
|
||||
const newCred = {
|
||||
@@ -494,7 +507,9 @@ router.put('/:sid', async(req, res) => {
|
||||
tts_region,
|
||||
riva_server_uri,
|
||||
nuance_stt_uri,
|
||||
nuance_tts_uri
|
||||
nuance_tts_uri,
|
||||
custom_stt_url,
|
||||
custom_tts_url
|
||||
};
|
||||
logger.info({o, newCred}, 'updating speech credential with this new credential');
|
||||
obj.credential = encryptCredential(newCred);
|
||||
|
||||
@@ -338,8 +338,8 @@ router.put('/:user_sid', async(req, res) => {
|
||||
//if (req.user.user_sid && req.user.user_sid !== user_sid) return res.sendStatus(403);
|
||||
|
||||
if (!hasAdminAuth &&
|
||||
!(hasAccountAuth && req.user.account_sid === user[0].account_sid) &&
|
||||
!(hasServiceProviderAuth && req.user.service_provider_sid === user[0].service_provider_sid) &&
|
||||
!(hasAccountAuth && user[0] && req.user.account_sid === user[0].account_sid) &&
|
||||
!(hasServiceProviderAuth && user[0] && req.user.service_provider_sid === user[0].service_provider_sid) &&
|
||||
(req.user.user_sid && req.user.user_sid !== user_sid)) {
|
||||
return res.sendStatus(403);
|
||||
}
|
||||
|
||||
@@ -61,8 +61,7 @@ router.post('/', express.raw({type: 'application/json'}), async(req, res) => {
|
||||
}
|
||||
|
||||
/* process event */
|
||||
logger.info(`received webhook: ${evt.type}`);
|
||||
if (evt.type.startsWith('invoice.')) handleInvoiceEvents(logger, evt);
|
||||
if (evt?.type?.startsWith('invoice.')) handleInvoiceEvents(logger, evt);
|
||||
else {
|
||||
logger.debug(evt, 'unhandled stripe webook');
|
||||
}
|
||||
|
||||
@@ -786,7 +786,7 @@ paths:
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
example: mycorp.sip.jambonz.us
|
||||
example: mycorp.sip.jambonz.cloud
|
||||
responses:
|
||||
200:
|
||||
description: indicates whether value is already in use
|
||||
@@ -3047,17 +3047,11 @@ paths:
|
||||
- inbound
|
||||
- outbound
|
||||
- in: query
|
||||
name: from
|
||||
name: filter
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
description: calling number to retrieve
|
||||
- in: query
|
||||
name: to
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
description: called number to retrieve
|
||||
description: Filter value can be caller ID, callee ID or call Sid
|
||||
get:
|
||||
tags:
|
||||
- Accounts
|
||||
|
||||
@@ -47,13 +47,15 @@ const sendEmailByCustomVendor = async(logger, from, to, subject, text) => {
|
||||
};
|
||||
|
||||
const sendEmailByMailgun = async(logger, from, to, subject, text) => {
|
||||
const mg = mailgun.client({
|
||||
username: 'api',
|
||||
key: process.env.MAILGUN_API_KEY
|
||||
});
|
||||
if (!process.env.MAILGUN_API_KEY) throw new Error('MAILGUN_API_KEY env variable is not defined!');
|
||||
if (!process.env.MAILGUN_DOMAIN) throw new Error('MAILGUN_DOMAIN env variable is not defined!');
|
||||
|
||||
const mg = mailgun.client({
|
||||
username: 'api',
|
||||
key: process.env.MAILGUN_API_KEY,
|
||||
...(process.env.MAILGUN_URL && {url: process.env.MAILGUN_URL})
|
||||
});
|
||||
|
||||
try {
|
||||
const res = await mg.messages.create(process.env.MAILGUN_DOMAIN, {
|
||||
from,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"trial": [
|
||||
{
|
||||
"category": "voice_call_session",
|
||||
"quantity": 20
|
||||
"quantity": 5
|
||||
},
|
||||
{
|
||||
"category": "device",
|
||||
|
||||
@@ -9,7 +9,8 @@ const getJaegerTrace = async(logger, traceId) => {
|
||||
try {
|
||||
return await getJSON(`/api/v3/traces/${traceId}`);
|
||||
} catch (err) {
|
||||
logger.error({err}, `getJaegerTrace: Error retrieving spans for traceId ${traceId}`);
|
||||
const url = `${process.env.JAEGER_BASE_URL}/api/traces/${traceId}`;
|
||||
logger.error({err, traceId}, `getJaegerTrace: Error retrieving spans from ${url}`);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
1
lib/utils/jambonz-sample.text
Normal file
1
lib/utils/jambonz-sample.text
Normal file
@@ -0,0 +1 @@
|
||||
Hello From Jambonz. This file was created because Record all call bucket credential test.
|
||||
@@ -1,4 +1,59 @@
|
||||
const { S3Client, PutObjectCommand, GetObjectCommand } = require('@aws-sdk/client-s3');
|
||||
const {Storage} = require('@google-cloud/storage');
|
||||
const fs = require('fs');
|
||||
const { BlobServiceClient } = require('@azure/storage-blob');
|
||||
|
||||
async function testAzureStorage(logger, opts) {
|
||||
const blobServiceClient = BlobServiceClient.fromConnectionString(opts.connection_string);
|
||||
const containerClient = blobServiceClient.getContainerClient(opts.name);
|
||||
const blockBlobClient = containerClient.getBlockBlobClient('jambonz-sample.text');
|
||||
|
||||
await blockBlobClient.uploadFile(`${__dirname}/jambonz-sample.text`);
|
||||
}
|
||||
|
||||
async function getAzureStorageObject(logger, opts) {
|
||||
const blobServiceClient = BlobServiceClient.fromConnectionString(opts.connection_string);
|
||||
const containerClient = blobServiceClient.getContainerClient(opts.name);
|
||||
const blockBlobClient = containerClient.getBlockBlobClient(opts.key);
|
||||
const response = await blockBlobClient.download(0);
|
||||
return response.readableStreamBody;
|
||||
}
|
||||
|
||||
function testGoogleStorage(logger, opts) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const serviceKey = JSON.parse(opts.service_key);
|
||||
const storage = new Storage({
|
||||
projectId: serviceKey.project_id,
|
||||
credentials: {
|
||||
client_email: serviceKey.client_email,
|
||||
private_key: serviceKey.private_key
|
||||
},
|
||||
});
|
||||
|
||||
const blob = storage.bucket(opts.name).file('jambonz-sample.text');
|
||||
|
||||
fs.createReadStream(`${__dirname}/jambonz-sample.text`)
|
||||
.pipe(blob.createWriteStream())
|
||||
.on('error', (err) => reject(err))
|
||||
.on('finish', () => resolve());
|
||||
});
|
||||
}
|
||||
|
||||
async function getGoogleStorageObject(logger, opts) {
|
||||
const serviceKey = JSON.parse(opts.service_key);
|
||||
const storage = new Storage({
|
||||
projectId: serviceKey.project_id,
|
||||
credentials: {
|
||||
client_email: serviceKey.client_email,
|
||||
private_key: serviceKey.private_key
|
||||
},
|
||||
});
|
||||
|
||||
const bucket = storage.bucket(opts.name);
|
||||
const file = bucket.file(opts.key);
|
||||
|
||||
return file.createReadStream();
|
||||
}
|
||||
|
||||
async function testAwsS3(logger, opts) {
|
||||
const s3 = new S3Client({
|
||||
@@ -38,5 +93,9 @@ async function getS3Object(logger, opts) {
|
||||
|
||||
module.exports = {
|
||||
testAwsS3,
|
||||
getS3Object
|
||||
getS3Object,
|
||||
testGoogleStorage,
|
||||
getGoogleStorageObject,
|
||||
testAzureStorage,
|
||||
getAzureStorageObject
|
||||
};
|
||||
|
||||
4340
package-lock.json
generated
4340
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
16
package.json
16
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jambonz-api-server",
|
||||
"version": "0.8.3",
|
||||
"version": "0.8.4",
|
||||
"description": "",
|
||||
"main": "app.js",
|
||||
"scripts": {
|
||||
@@ -19,15 +19,15 @@
|
||||
"url": "https://github.com/jambonz/jambonz-api-server.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-transcribe": "^3.348.0",
|
||||
"@aws-sdk/client-s3": "^3.348.0",
|
||||
"@aws-sdk/client-transcribe": "^3.363.0",
|
||||
"@aws-sdk/client-s3": "^3.363.0",
|
||||
"@deepgram/sdk": "^1.21.0",
|
||||
"@google-cloud/speech": "^5.2.0",
|
||||
"@jambonz/db-helpers": "^0.9.0",
|
||||
"@jambonz/realtimedb-helpers": "^0.8.6",
|
||||
"@jambonz/speech-utils": "^0.0.15",
|
||||
"@jambonz/time-series": "^0.2.7",
|
||||
"@jambonz/verb-specifications": "^0.0.24",
|
||||
"@jambonz/time-series": "^0.2.8",
|
||||
"@jambonz/verb-specifications": "^0.0.29",
|
||||
"@jambonz/lamejs": "^1.2.2",
|
||||
"@soniox/soniox-node": "^1.1.1",
|
||||
"argon2": "^0.30.3",
|
||||
@@ -40,7 +40,7 @@
|
||||
"helmet": "^5.1.0",
|
||||
"ibm-watson": "^7.1.2",
|
||||
"jsonwebtoken": "^9.0.0",
|
||||
"mailgun.js": "^3.7.3",
|
||||
"mailgun.js": "^9.1.2",
|
||||
"microsoft-cognitiveservices-speech-sdk": "^1.24.1",
|
||||
"mysql2": "^2.3.3",
|
||||
"nocache": "3.0.4",
|
||||
@@ -53,7 +53,9 @@
|
||||
"uuid": "^8.3.2",
|
||||
"yamljs": "^0.3.0",
|
||||
"ws": "^8.12.1",
|
||||
"wav": "^1.0.2"
|
||||
"wav": "^1.0.2",
|
||||
"@google-cloud/storage": "^6.12.0",
|
||||
"@azure/storage-blob": "^12.15.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^8.39.0",
|
||||
|
||||
@@ -53,7 +53,7 @@ test('application tests', async(t) => {
|
||||
]'
|
||||
}
|
||||
});
|
||||
t.ok(result.statusCode === 400, 'Cant create application with invalid app_josn');
|
||||
t.ok(result.statusCode === 400, 'Cant create application with invalid app_json');
|
||||
|
||||
/* add an application */
|
||||
result = await request.post('/Applications', {
|
||||
@@ -81,7 +81,15 @@ test('application tests', async(t) => {
|
||||
"seekOffset": 8000,\
|
||||
"actionHook": "/play/action"\
|
||||
}\
|
||||
]'
|
||||
]',
|
||||
use_for_fallback_speech: 1,
|
||||
fallback_speech_synthesis_vendor: 'google',
|
||||
fallback_speech_synthesis_language: 'en-US',
|
||||
fallback_speech_synthesis_voice: 'man',
|
||||
fallback_speech_synthesis_label: 'label1',
|
||||
fallback_speech_recognizer_vendor: 'google',
|
||||
fallback_speech_recognizer_language: 'en-US',
|
||||
fallback_speech_recognizer_label: 'label1'
|
||||
}
|
||||
});
|
||||
t.ok(result.statusCode === 201, 'successfully created application');
|
||||
@@ -102,6 +110,14 @@ test('application tests', async(t) => {
|
||||
});
|
||||
t.ok(result.name === 'daveh' , 'successfully retrieved application by sid');
|
||||
t.ok(result.messaging_hook.url === 'http://example.com/sms' , 'successfully retrieved messaging_hook from application');
|
||||
t.ok(result.use_for_fallback_speech === 1, 'successfully create use_for_fallback_speech');
|
||||
t.ok(result.fallback_speech_synthesis_vendor === 'google', 'successfully create fallback_speech_synthesis_vendor');
|
||||
t.ok(result.fallback_speech_synthesis_language === 'en-US', 'successfully create fallback_speech_synthesis_language');
|
||||
t.ok(result.fallback_speech_synthesis_voice === 'man', 'successfully create fallback_speech_synthesis_voice');
|
||||
t.ok(result.fallback_speech_synthesis_label === 'label1', 'successfully create fallback_speech_synthesis_label');
|
||||
t.ok(result.fallback_speech_recognizer_vendor === 'google', 'successfully create fallback_speech_recognizer_vendor');
|
||||
t.ok(result.fallback_speech_recognizer_language === 'en-US', 'successfully create fallback_speech_recognizer_language');
|
||||
t.ok(result.fallback_speech_recognizer_label === 'label1', 'successfully create fallback_speech_recognizer_label');
|
||||
let app_json = JSON.parse(result.app_json);
|
||||
t.ok(app_json[0].verb === 'play', 'successfully retrieved app_json from application')
|
||||
|
||||
@@ -126,7 +142,15 @@ test('application tests', async(t) => {
|
||||
}\
|
||||
}\
|
||||
]',
|
||||
record_all_calls: true
|
||||
record_all_calls: true,
|
||||
use_for_fallback_speech: 0,
|
||||
fallback_speech_synthesis_vendor: 'microsoft',
|
||||
fallback_speech_synthesis_language: 'en-US',
|
||||
fallback_speech_synthesis_voice: 'woman',
|
||||
fallback_speech_synthesis_label: 'label2',
|
||||
fallback_speech_recognizer_vendor: 'microsoft',
|
||||
fallback_speech_recognizer_language: 'en-US',
|
||||
fallback_speech_recognizer_label: 'label2'
|
||||
}
|
||||
});
|
||||
t.ok(result.statusCode === 204, 'successfully updated application');
|
||||
@@ -140,6 +164,14 @@ test('application tests', async(t) => {
|
||||
app_json = JSON.parse(result.app_json);
|
||||
t.ok(app_json[0].verb === 'hangup', 'successfully updated app_json from application')
|
||||
t.ok(result.record_all_calls === 1, 'successfully updated record_all_calls from application')
|
||||
t.ok(result.use_for_fallback_speech === 0, 'successfully update use_for_fallback_speech');
|
||||
t.ok(result.fallback_speech_synthesis_vendor === 'microsoft', 'successfully update fallback_speech_synthesis_vendor');
|
||||
t.ok(result.fallback_speech_synthesis_language === 'en-US', 'successfully update fallback_speech_synthesis_language');
|
||||
t.ok(result.fallback_speech_synthesis_voice === 'woman', 'successfully update fallback_speech_synthesis_voice');
|
||||
t.ok(result.fallback_speech_synthesis_label === 'label2', 'successfully update fallback_speech_synthesis_label');
|
||||
t.ok(result.fallback_speech_recognizer_vendor === 'microsoft', 'successfully update fallback_speech_recognizer_vendor');
|
||||
t.ok(result.fallback_speech_recognizer_language === 'en-US', 'successfully update fallback_speech_recognizer_language');
|
||||
t.ok(result.fallback_speech_recognizer_label === 'label2', 'successfully update fallback_speech_recognizer_label');
|
||||
|
||||
/* remove applications app_json*/
|
||||
result = await request.put(`/Applications/${sid}`, {
|
||||
|
||||
@@ -31,8 +31,8 @@ test('Create Call Success With Synthesizer in Payload', async (t) => {
|
||||
auth: authUser,
|
||||
json: true,
|
||||
body: {
|
||||
call_hook: "https://public-apps.jambonz.us/hello-world",
|
||||
call_status_hook: "https://public-apps.jambonz.us/call-status",
|
||||
call_hook: "https://public-apps.jambonz.cloud/hello-world",
|
||||
call_status_hook: "https://public-apps.jambonz.cloud/call-status",
|
||||
from: "15083778299",
|
||||
to: {
|
||||
type: "phone",
|
||||
@@ -73,8 +73,8 @@ test('Create Call Success Without Synthesizer in Payload', async (t) => {
|
||||
auth: authUser,
|
||||
json: true,
|
||||
body: {
|
||||
call_hook: "https://public-apps.jambonz.us/hello-world",
|
||||
call_status_hook: "https://public-apps.jambonz.us/call-status",
|
||||
call_hook: "https://public-apps.jambonz.cloud/hello-world",
|
||||
call_status_hook: "https://public-apps.jambonz.cloud/call-status",
|
||||
from: "15083778299",
|
||||
to: {
|
||||
type: "phone",
|
||||
|
||||
@@ -7,7 +7,7 @@ const test = async() => {
|
||||
headers: {
|
||||
Authorization: `Bearer ${process.env.GH_CODE}`,
|
||||
Accept: 'application/json',
|
||||
'User-Agent': 'jambonz.us'
|
||||
'User-Agent': 'jambonz.cloud'
|
||||
}
|
||||
}, (err, response, body) => {
|
||||
if (err) console.log(error);
|
||||
|
||||
@@ -75,32 +75,29 @@ test('recent calls tests', async(t) => {
|
||||
auth: authUser,
|
||||
json: true,
|
||||
});
|
||||
console.log(JSON.stringify(result));
|
||||
t.ok(result.data.length === 5, 'retrieved 5 recent calls by account');
|
||||
|
||||
result = await request.get(`/Accounts/${account_sid}/RecentCalls?page=1&count=25&from=16`, {
|
||||
auth: authUser,
|
||||
json: true,
|
||||
});
|
||||
t.ok(result.data.length === 0, 'retrieved 5 recent calls by account and from');
|
||||
|
||||
result = await request.get(`/Accounts/${account_sid}/RecentCalls?page=1&count=25&from=15`, {
|
||||
result = await request.get(`/Accounts/${account_sid}/RecentCalls?page=1&count=25&filter=1508`, {
|
||||
auth: authUser,
|
||||
json: true,
|
||||
});
|
||||
console.log(JSON.stringify(result));
|
||||
t.ok(result.data.length === 5, 'retrieved 5 recent calls by account and from');
|
||||
|
||||
result = await request.get(`/Accounts/${account_sid}/RecentCalls?page=1&count=25&to=19`, {
|
||||
result = await request.get(`/Accounts/${account_sid}/RecentCalls?page=1&count=25&filter=15080`, {
|
||||
auth: authUser,
|
||||
json: true,
|
||||
});
|
||||
t.ok(result.data.length === 0, 'retrieved 5 recent calls by account and to');
|
||||
console.log(JSON.stringify(result));
|
||||
t.ok(result.data.length === 0, 'retrieved 0 recent calls by account and non-matching from');
|
||||
|
||||
result = await request.get(`/Accounts/${account_sid}/RecentCalls?page=1&count=25&to=18`, {
|
||||
result = await request.get(`/Accounts/${account_sid}/RecentCalls?page=1&count=25&filter=1888`, {
|
||||
auth: authUser,
|
||||
json: true,
|
||||
});
|
||||
console.log(JSON.stringify(result));
|
||||
t.ok(result.data.length === 5, 'retrieved 5 recent calls by account and to');
|
||||
//console.log({data: result.data}, 'Account recent calls');
|
||||
|
||||
/* query last 7 days by service provider */
|
||||
result = await request.get(`/ServiceProviders/${service_provider_sid}/RecentCalls?page=1&count=25`, {
|
||||
@@ -109,29 +106,18 @@ test('recent calls tests', async(t) => {
|
||||
});
|
||||
t.ok(result.data.length === 5, 'retrieved 5 recent calls by service provider');
|
||||
|
||||
result = await request.get(`/ServiceProviders/${service_provider_sid}/RecentCalls?page=1&count=25&from=16`, {
|
||||
auth: authAdmin,
|
||||
json: true,
|
||||
});
|
||||
t.ok(result.data.length === 0, 'retrieved 5 recent calls by service provider and from');
|
||||
|
||||
result = await request.get(`/ServiceProviders/${service_provider_sid}/RecentCalls?page=1&count=25&from=15`, {
|
||||
result = await request.get(`/ServiceProviders/${service_provider_sid}/RecentCalls?page=1&count=25&filter=1508`, {
|
||||
auth: authAdmin,
|
||||
json: true,
|
||||
});
|
||||
t.ok(result.data.length === 5, 'retrieved 5 recent calls by service provider and from');
|
||||
|
||||
result = await request.get(`/ServiceProviders/${service_provider_sid}/RecentCalls?page=1&count=25&to=19`, {
|
||||
auth: authAdmin,
|
||||
json: true,
|
||||
});
|
||||
t.ok(result.data.length === 0, 'retrieved 5 recent calls by service provider and to');
|
||||
|
||||
result = await request.get(`/ServiceProviders/${service_provider_sid}/RecentCalls?page=1&count=25&to=18`, {
|
||||
result = await request.get(`/ServiceProviders/${service_provider_sid}/RecentCalls?page=1&count=25&filter=1888`, {
|
||||
auth: authAdmin,
|
||||
json: true,
|
||||
});
|
||||
t.ok(result.data.length === 5, 'retrieved 5 recent calls by service provider and to');
|
||||
|
||||
//console.log({data: result.data}, 'SP recent calls');
|
||||
|
||||
/* pull sip traces and pcap from homer */
|
||||
|
||||
45
test/sbcs.js
45
test/sbcs.js
@@ -17,6 +17,37 @@ test('sbc_addresses tests', async(t) => {
|
||||
let result;
|
||||
const service_provider_sid = await createServiceProvider(request);
|
||||
|
||||
/* add service_provider user */
|
||||
const sp_name = 'sbc_service_provider';
|
||||
const sp_password = 'password';
|
||||
result = await request.post(`/Users`, {
|
||||
resolveWithFullResponse: true,
|
||||
json: true,
|
||||
auth: authAdmin,
|
||||
body: {
|
||||
name: sp_name,
|
||||
email: 'sbc_sp@jambonz.com',
|
||||
is_active: true,
|
||||
force_change: false,
|
||||
initial_password: sp_password,
|
||||
service_provider_sid,
|
||||
}
|
||||
});
|
||||
t.ok(result.statusCode === 201 && result.body.user_sid, 'SBC service_provider scope user created');
|
||||
const sbc_sp_user_sid = result.body.user_sid;
|
||||
|
||||
result = await request.post('/login', {
|
||||
resolveWithFullResponse: true,
|
||||
json: true,
|
||||
body: {
|
||||
username: sp_name,
|
||||
password: sp_password,
|
||||
}
|
||||
});
|
||||
t.ok(result.statusCode === 200 && result.body.token, 'successfully logged in as sbc user');
|
||||
const authSbcSp = {bearer: result.body.token};
|
||||
|
||||
|
||||
/* add a service provider sbc */
|
||||
result = await request.post('/Sbcs', {
|
||||
resolveWithFullResponse: true,
|
||||
@@ -38,6 +69,20 @@ test('sbc_addresses tests', async(t) => {
|
||||
//console.log(result.body)
|
||||
t.ok(result.body.length === 1 && result.body[0].ipv4 === '192.168.1.4', 'successfully retrieved service provider sbc');
|
||||
|
||||
result = await request.get('/Sbcs', {
|
||||
resolveWithFullResponse: true,
|
||||
auth: authSbcSp,
|
||||
json: true
|
||||
});
|
||||
//console.log(result.body)
|
||||
t.ok(result.body.length === 1 && result.body[0].ipv4 === '192.168.1.4', 'successfully retrieved service provider sbc');
|
||||
|
||||
await request.delete(`/Users/${sbc_sp_user_sid}`, {
|
||||
resolveWithFullResponse: true,
|
||||
json: true,
|
||||
auth: authAdmin,
|
||||
});
|
||||
|
||||
await deleteObjectBySid(request, '/Sbcs', sid);
|
||||
await deleteObjectBySid(request, '/ServiceProviders', service_provider_sid);
|
||||
|
||||
|
||||
@@ -84,6 +84,7 @@ test('speech credentials tests', async(t) => {
|
||||
json: true,
|
||||
body: {
|
||||
vendor: 'google',
|
||||
label: 'label1',
|
||||
service_key: jsonKey,
|
||||
use_for_tts: true,
|
||||
use_for_stt: true
|
||||
@@ -111,6 +112,7 @@ test('speech credentials tests', async(t) => {
|
||||
json: true,
|
||||
});
|
||||
t.ok(result.vendor === 'google' , 'successfully retrieved speech credential by sid');
|
||||
t.ok(result.label === 'label1' , 'label is successfully created');
|
||||
|
||||
/* query all credentials */
|
||||
result = await request.get(`/Accounts/${account_sid}/SpeechCredentials`, {
|
||||
|
||||
@@ -192,7 +192,7 @@ test('webapp tests', async(t) => {
|
||||
t.ok(result.statusCode === 200 && result.body.available === true, 'indicates when email is available');
|
||||
|
||||
/* check if a subdomain is available */
|
||||
result = await request.get('/Availability?type=subdomain&value=mycompany.sip.jambonz.us', {
|
||||
result = await request.get('/Availability?type=subdomain&value=mycompany.sip.jambonz.cloud', {
|
||||
resolveWithFullResponse: true,
|
||||
auth: authUser,
|
||||
json: true,
|
||||
|
||||
Reference in New Issue
Block a user