From 0e056ad2968fa61a6050fc14089d349e5a35f5d0 Mon Sep 17 00:00:00 2001 From: Hoan Luu Huu <110280845+xquanluu@users.noreply.github.com> Date: Tue, 28 Nov 2023 20:44:45 +0700 Subject: [PATCH 1/3] support getting registered user details (#265) * support getting registered user details * add swager * fix to use new registrar api * assert sip_realm should be available for registered sip user query * update mw registra version --- lib/routes/api/accounts.js | 40 ++++++++++++++++++++++++++++++++++++++ lib/swagger/swagger.yaml | 21 ++++++++++++++++++++ package-lock.json | 14 ++++++------- package.json | 2 +- test/clients.js | 18 +++++++++++++++++ 5 files changed, 87 insertions(+), 8 deletions(-) diff --git a/lib/routes/api/accounts.js b/lib/routes/api/accounts.js index ea0151f..fbfa040 100644 --- a/lib/routes/api/accounts.js +++ b/lib/routes/api/accounts.js @@ -155,6 +155,9 @@ router.get('/:sid/RegisteredSipUsers', async(req, res) => { if (!result || result.length === 0) { throw new DbErrorBadRequest(`account not found for sid ${account_sid}`); } + if (!result[0].sip_realm) { + throw new DbErrorBadRequest('account does not have sip_realm configuration'); + } const users = await registrar.getRegisteredUsersForRealm(result[0].sip_realm); res.status(200).json(users.map((u) => `${u}@${result[0].sip_realm}`)); } catch (err) { @@ -162,6 +165,43 @@ router.get('/:sid/RegisteredSipUsers', async(req, res) => { } }); +router.post('/:sid/RegisteredSipUsers', async(req, res) => { + const {logger, registrar} = req.app.locals; + const users = req.body; + try { + const account_sid = parseAccountSid(req); + await validateRequest(req, account_sid); + const result = await Account.retrieve(account_sid); + if (!result || result.length === 0) { + throw new DbErrorBadRequest(`account not found for sid ${account_sid}`); + } + if (!result[0].sip_realm) { + throw new DbErrorBadRequest('account does not have sip_realm configuration'); + } + if (!users || !Array.isArray(users) || users.length === 0) { + return res.status(200).json(await registrar.getRegisteredUsersDetailsForRealm(result[0].sip_realm)); + } + const ret = []; + for (const u of users) { + const user = await registrar.query(`${u}@${result[0].sip_realm}`) || { + name: u, + contact: null, + expiryTime: 0, + protocol: null, + registered_status: 'inactive' + }; + delete user.sbcAddress; + ret.push({ + name: u, + ...user + }); + } + res.status(200).json(ret); + } catch (err) { + sysError(logger, res, err); + } +}); + router.get('/:sid/RegisteredSipUsers/:client', async(req, res) => { const {logger, registrar, lookupClientByAccountAndUsername} = req.app.locals; const client = req.params.client; diff --git a/lib/swagger/swagger.yaml b/lib/swagger/swagger.yaml index 6595998..2b1d422 100644 --- a/lib/swagger/swagger.yaml +++ b/lib/swagger/swagger.yaml @@ -4192,6 +4192,27 @@ paths: type: array items: type: string + post: + tags: + - Accounts + summary: retrieve online sip users for an account by list of sip username + operationId: listRegisteredSipUsers + requestBody: + content: + application/json: + schema: + type: array + items: + type: string + responses: + 200: + description: retrieve online sip users for an account + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/RegisteredClient' /Accounts/{AccountSid}/RegisteredSipUsers/{Client}: parameters: - name: Client diff --git a/package-lock.json b/package-lock.json index 75c9704..a0e3384 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ "@google-cloud/storage": "^6.12.0", "@jambonz/db-helpers": "^0.9.1", "@jambonz/lamejs": "^1.2.2", - "@jambonz/mw-registrar": "^0.2.5", + "@jambonz/mw-registrar": "^0.2.7", "@jambonz/realtimedb-helpers": "^0.8.7", "@jambonz/speech-utils": "^0.0.26", "@jambonz/time-series": "^0.2.8", @@ -1951,9 +1951,9 @@ } }, "node_modules/@jambonz/mw-registrar": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/@jambonz/mw-registrar/-/mw-registrar-0.2.5.tgz", - "integrity": "sha512-+aBI2xpR6Ir140Hi7/ED+z5Hl7NgCalyVzTLNlgUVzTvsMLtyZZh6n9IQipKnuKvhh4nPM4aJ+JuFhi1UH9zEA==", + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@jambonz/mw-registrar/-/mw-registrar-0.2.7.tgz", + "integrity": "sha512-ckvIimjR2NpTcX1L5ryyXH1ynHhHl4wkGkt4wiip6Ngc+gb5Nf9eQF+zHShvahHi863gOdxtV7K7sAWQQHELGQ==", "dependencies": { "debug": "^4.3.4" } @@ -11413,9 +11413,9 @@ } }, "@jambonz/mw-registrar": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/@jambonz/mw-registrar/-/mw-registrar-0.2.5.tgz", - "integrity": "sha512-+aBI2xpR6Ir140Hi7/ED+z5Hl7NgCalyVzTLNlgUVzTvsMLtyZZh6n9IQipKnuKvhh4nPM4aJ+JuFhi1UH9zEA==", + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@jambonz/mw-registrar/-/mw-registrar-0.2.7.tgz", + "integrity": "sha512-ckvIimjR2NpTcX1L5ryyXH1ynHhHl4wkGkt4wiip6Ngc+gb5Nf9eQF+zHShvahHi863gOdxtV7K7sAWQQHELGQ==", "requires": { "debug": "^4.3.4" } diff --git a/package.json b/package.json index 68ac6c5..94d03e8 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "@google-cloud/storage": "^6.12.0", "@jambonz/db-helpers": "^0.9.1", "@jambonz/lamejs": "^1.2.2", - "@jambonz/mw-registrar": "^0.2.5", + "@jambonz/mw-registrar": "^0.2.7", "@jambonz/realtimedb-helpers": "^0.8.7", "@jambonz/speech-utils": "^0.0.26", "@jambonz/time-series": "^0.2.8", diff --git a/test/clients.js b/test/clients.js index e4ccd92..7494c9b 100644 --- a/test/clients.js +++ b/test/clients.js @@ -85,6 +85,24 @@ test('client test', async(t) => { t.ok(result.length === 1 && result[0] === 'dhorton@drachtio.org', 'successfully queried all registered clients'); + result = await request.post(`/Accounts/${account_sid}/RegisteredSipUsers`, { + resolveWithFullResponse: true, + auth: authAdmin, + json: true, + body: ['dhorton'] + }); + t.ok(result.body.length === 1 && result.body[0].name === 'dhorton', + 'successfully queried all registered clients'); + + result = await request.post(`/Accounts/${account_sid}/RegisteredSipUsers`, { + resolveWithFullResponse: true, + auth: authAdmin, + json: true, + body: [] + }); + t.ok(result.body.length === 1 && result.body[0].name === 'dhorton', + 'successfully queried all registered clients'); + /* query all entity */ result = await request.get('/Clients', { auth: authAdmin, From e2d6086f9f257f9bc6fcf2eef96f248daa6e8561 Mon Sep 17 00:00:00 2001 From: Hoan Luu Huu <110280845+xquanluu@users.noreply.github.com> Date: Fri, 1 Dec 2023 02:01:50 +0700 Subject: [PATCH 2/3] elevenlabs new model and options (#267) --- lib/routes/api/speech-credentials.js | 11 +++++++---- lib/utils/speech-utils.js | 1 + 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/routes/api/speech-credentials.js b/lib/routes/api/speech-credentials.js index 4ca3069..cf365a2 100644 --- a/lib/routes/api/speech-credentials.js +++ b/lib/routes/api/speech-credentials.js @@ -132,7 +132,8 @@ const encryptCredential = (obj) => { custom_tts_url, auth_token = '', cobalt_server_uri, - model_id + model_id, + options } = obj; switch (vendor) { @@ -210,7 +211,7 @@ const encryptCredential = (obj) => { case 'elevenlabs': assert(api_key, 'invalid elevenLabs speech credential: api_key is required'); assert(model_id, 'invalid elevenLabs speech credential: model_id is required'); - const elevenlabsData = JSON.stringify({api_key, model_id}); + const elevenlabsData = JSON.stringify({api_key, model_id, options}); return encrypt(elevenlabsData); case 'assemblyai': @@ -411,7 +412,8 @@ router.put('/:sid', async(req, res) => { custom_stt_url, custom_tts_url, cobalt_server_uri, - model_id + model_id, + options } = req.body; const newCred = { @@ -433,7 +435,8 @@ router.put('/:sid', async(req, res) => { custom_stt_url, custom_tts_url, cobalt_server_uri, - model_id + model_id, + options }; logger.info({o, newCred}, 'updating speech credential with this new credential'); obj.credential = encryptCredential(newCred); diff --git a/lib/utils/speech-utils.js b/lib/utils/speech-utils.js index 2f667a3..47981fc 100644 --- a/lib/utils/speech-utils.js +++ b/lib/utils/speech-utils.js @@ -395,6 +395,7 @@ function decryptCredential(obj, credential, logger, isObscureKey = true) { const o = JSON.parse(decrypt(credential)); obj.api_key = isObscureKey ? obscureKey(o.api_key) : o.api_key; obj.model_id = o.model_id; + obj.options = o.options; } else if (obj.vendor.startsWith('custom:')) { const o = JSON.parse(decrypt(credential)); obj.auth_token = isObscureKey ? obscureKey(o.auth_token) : o.auth_token; From 4ec34faa296230909153f5a4f1ae5921b10c733b Mon Sep 17 00:00:00 2001 From: Hoan Luu Huu <110280845+xquanluu@users.noreply.github.com> Date: Sat, 2 Dec 2023 23:31:07 +0700 Subject: [PATCH 3/3] support inband dtmf (#261) * support inband dtmf * support inband dtmf * wip --- lib/routes/api/accounts.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/routes/api/accounts.js b/lib/routes/api/accounts.js index fbfa040..d84b29c 100644 --- a/lib/routes/api/accounts.js +++ b/lib/routes/api/accounts.js @@ -265,7 +265,8 @@ function validateUpdateCall(opts) { 'mute_status', 'sip_request', 'record', - 'tag' + 'tag', + 'dtmf' ] .reduce((acc, prop) => (opts[prop] ? ++acc : acc), 0); @@ -307,6 +308,9 @@ function validateUpdateCall(opts) { if (opts.record && !opts.record.action) { throw new DbErrorBadRequest('record requires action property'); } + if (opts.dtmf && !opts.dtmf.digit) { + throw new DbErrorBadRequest('invalid dtmf'); + } if ('startCallRecording' === opts.record?.action && !opts.record.siprecServerURL) { throw new DbErrorBadRequest('record requires siprecServerURL property when starting recording'); }