replace bent by native node fetch (#401)

* replace bent by native node fetch

* wip

* wip

* wip
This commit is contained in:
Hoan Luu Huu
2025-04-24 17:50:15 +07:00
committed by GitHub
parent b05b32d73e
commit ffda2398f4
41 changed files with 678 additions and 667 deletions
+30 -27
View File
@@ -1,6 +1,5 @@
if (!process.env.JAMBONES_HOSTING) return;
const bent = require('bent');
const crypto = require('crypto');
const assert = require('assert');
const domains = new Map();
@@ -26,17 +25,20 @@ const createAuthHeaders = () => {
const getDnsDomainId = async(logger, name) => {
checkAsserts();
const headers = createAuthHeaders();
const get = bent(process.env.DME_BASE_URL, 'GET', 'json', headers);
try {
const result = await get('/dns/managed');
debug(result, 'getDnsDomainId: all domains');
if (Array.isArray(result.data)) {
const domain = result.data.find((o) => o.name === name);
if (domain) return domain.id;
debug(`getDnsDomainId: failed to find domain ${name}`);
}
} catch (err) {
logger.error({err}, 'Error retrieving domains');
const response = await fetch(`${process.env.DME_BASE_URL}/dns/managed`, {
method: 'GET',
headers
});
if (!response.ok) {
logger.error({response}, 'Error retrieving domains');
return;
}
const result = await response.json();
debug(result, 'getDnsDomainId: all domains');
if (Array.isArray(result.data)) {
const domain = result.data.find((o) => o.name === name);
if (domain) return domain.id;
debug(`getDnsDomainId: failed to find domain ${name}`);
}
};
@@ -80,21 +82,20 @@ const createDnsRecords = async(logger, domain, name, value, ttl = 3600) => {
];
const headers = createAuthHeaders();
const records = [...a_records, ...srv_records];
const post = bent(process.env.DME_BASE_URL, 'POST', 201, 400, headers);
logger.debug({records}, 'Attemting to create dns records');
const res = await post(`/dns/managed/${domainId}/records/createMulti`,
[...a_records, ...srv_records]);
if (201 === res.statusCode) {
const str = await res.text();
return JSON.parse(str);
const response = await fetch(`${process.env.DME_BASE_URL}/dns/managed/${domainId}/records/createMulti`, {
method: 'POST',
headers,
body: JSON.stringify(records)
});
if (!response.ok) {
logger.error({response}, 'Error creating records');
return;
}
let body;
try {
body = await res.json();
} catch (err) {
const result = await response.json();
logger.debug({result}, 'createDnsRecords: created records');
if (201 === response.status) {
return result;
}
logger.error({headers: res.headers, body}, `Error creating records, status ${res.statusCode}`);
} catch (err) {
logger.error({err}, 'Error retrieving domains');
}
@@ -103,7 +104,6 @@ const createDnsRecords = async(logger, domain, name, value, ttl = 3600) => {
const deleteDnsRecords = async(logger, domain, recIds) => {
checkAsserts();
const headers = createAuthHeaders();
const del = bent(process.env.DME_BASE_URL, 'DELETE', 200, headers);
try {
if (!domains.has(domain)) {
const domainId = await getDnsDomainId(logger, domain);
@@ -112,7 +112,10 @@ const deleteDnsRecords = async(logger, domain, recIds) => {
}
const domainId = domains.get(domain);
const url = `/dns/managed/${domainId}/records?${recIds.map((r) => `ids=${r}`).join('&')}`;
await del(url);
await fetch(`${process.env.DME_BASE_URL}${url}`, {
method: 'DELETE',
headers
});
return true;
} catch (err) {
console.error(err);
+14 -14
View File
@@ -1,7 +1,6 @@
const formData = require('form-data');
const Mailgun = require('mailgun.js');
const mailgun = new Mailgun(formData);
const bent = require('bent');
const validateEmail = (email) => {
// eslint-disable-next-line max-len
const re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
@@ -19,8 +18,9 @@ const emailSimpleText = async(logger, to, subject, text) => {
};
const sendEmailByCustomVendor = async(logger, from, to, subject, text) => {
try {
const post = bent('POST', {
const response = await fetch(process.env.CUSTOM_EMAIL_VENDOR_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...((process.env.CUSTOM_EMAIL_VENDOR_USERNAME && process.env.CUSTOM_EMAIL_VENDOR_PASSWORD) &&
({
@@ -28,22 +28,22 @@ const sendEmailByCustomVendor = async(logger, from, to, subject, text) => {
`${process.env.CUSTOM_EMAIL_VENDOR_USERNAME}:${process.env.CUSTOM_EMAIL_VENDOR_PASSWORD}`
).toString('base64')}`
}))
});
const res = await post(process.env.CUSTOM_EMAIL_VENDOR_URL, {
},
body: JSON.stringify({
from,
to,
subject,
text
});
logger.debug({
res
}, 'sent email to custom vendor.');
} catch (err) {
logger.info({
err
}, 'Error sending email From Custom email vendor');
})
});
if (!response.ok) {
logger.error({response}, 'Error sending email to custom vendor');
return;
}
const res = await response.json();
logger.debug({
res
}, 'sent email to custom vendor.');
};
const sendEmailByMailgun = async(logger, from, to, subject, text) => {
+87 -58
View File
@@ -1,15 +1,11 @@
const debug = require('debug')('jambonz:api-server');
const bent = require('bent');
const basicAuth = (apiKey) => {
const header = `Bearer ${apiKey}`;
return {Authorization: header};
};
const postJSON = bent(process.env.HOMER_BASE_URL || 'http://127.0.0.1', 'POST', 'json', 200, 201);
const postPcap = bent(process.env.HOMER_BASE_URL || 'http://127.0.0.1', 'POST', 200, {
'Content-Type': 'application/json',
'Accept': 'application/json, text/plain, */*',
});
const SEVEN_DAYS_IN_MS = (1000 * 3600 * 24 * 7);
const HOMER_BASE_URL = process.env.HOMER_BASE_URL || 'http://127.0.0.1';
const getHomerApiKey = async(logger) => {
if (!process.env.HOMER_BASE_URL || !process.env.HOMER_USERNAME || !process.env.HOMER_PASSWORD) {
@@ -17,11 +13,21 @@ const getHomerApiKey = async(logger) => {
}
try {
const obj = await postJSON('/api/v3/auth', {
username: process.env.HOMER_USERNAME,
password: process.env.HOMER_PASSWORD
const response = await fetch(`${HOMER_BASE_URL}/api/v3/auth`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: process.env.HOMER_USERNAME,
password: process.env.HOMER_PASSWORD
})
});
debug(obj);
if (!response.ok) {
logger.error({response}, 'Error retrieving apikey');
return;
}
const obj = await response.json();
logger.debug({obj}, `getHomerApiKey for user ${process.env.HOMER_USERNAME}`);
return obj.token;
} catch (err) {
@@ -36,28 +42,40 @@ const getHomerSipTrace = async(logger, apiKey, callId) => {
}
try {
const now = Date.now();
const obj = await postJSON('/api/v3/call/transaction', {
param: {
transaction: {
call: true,
registration: true,
rest: false
},
orlogic: true,
search: {
'1_call': {
callid: [callId]
},
'1_registration': {
callid: [callId]
}
},
const response = await fetch(`${HOMER_BASE_URL}/api/v3/call/transaction`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...basicAuth(apiKey)
},
timestamp: {
from: now - SEVEN_DAYS_IN_MS,
to: now
}
}, basicAuth(apiKey));
body: JSON.stringify({
param: {
transaction: {
call: true,
registration: true,
rest: false
},
orlogic: true,
search: {
'1_call': {
callid: [callId]
},
'1_registration': {
callid: [callId]
}
},
},
timestamp: {
from: now - SEVEN_DAYS_IN_MS,
to: now
}
})
});
if (!response.ok) {
logger.error({response}, 'Error retrieving messages');
return;
}
const obj = await response.json();
return obj;
} catch (err) {
logger.info({err}, `getHomerSipTrace: Error retrieving messages for callid ${callId}`);
@@ -70,34 +88,45 @@ const getHomerPcap = async(logger, apiKey, callIds, method) => {
}
try {
const now = Date.now();
const stream = await postPcap('/api/v3/export/call/messages/pcap', {
param: {
transaction: {
call: method === 'invite',
registration: method === 'register',
rest: false
},
orlogic: true,
search: {
...(method === 'invite' && {
'1_call': {
callid: callIds
}
})
,
...(method === 'register' && {
'1_registration': {
callid: callIds
}
})
},
const response = await fetch(`${HOMER_BASE_URL}/api/v3/export/call/messages/pcap`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...basicAuth(apiKey)
},
timestamp: {
from: now - SEVEN_DAYS_IN_MS,
to: now
}
}, basicAuth(apiKey));
return stream;
body: JSON.stringify({
param: {
transaction: {
call: method === 'invite',
registration: method === 'register',
rest: false
},
orlogic: true,
search: {
...(method === 'invite' && {
'1_call': {
callid: callIds
}
})
,
...(method === 'register' && {
'1_registration': {
callid: callIds
}
})
},
},
timestamp: {
from: now - SEVEN_DAYS_IN_MS,
to: now
}
})
});
if (!response.ok) {
logger.error({response}, 'Error retrieving messages');
return;
}
return response.body;
} catch (err) {
logger.info({err}, `getHomerPcap: Error retrieving messages for callid ${callIds}`);
}
+7 -3
View File
@@ -1,5 +1,4 @@
const bent = require('bent');
const getJSON = bent(process.env.JAEGER_BASE_URL || 'http://127.0.0.1', 'GET', 'json', 200);
const JAEGER_BASE_URL = process.env.JAEGER_BASE_URL || 'http://127.0.0.1';
const getJaegerTrace = async(logger, traceId) => {
if (!process.env.JAEGER_BASE_URL) {
@@ -7,7 +6,12 @@ const getJaegerTrace = async(logger, traceId) => {
return null;
}
try {
return await getJSON(`/api/v3/traces/${traceId}`);
const response = await fetch(`${JAEGER_BASE_URL}/api/traces/${traceId}`);
if (!response.ok) {
logger.error({response}, 'Error retrieving spans');
return;
}
return await response.json();
} catch (err) {
const url = `${process.env.JAEGER_BASE_URL}/api/traces/${traceId}`;
logger.error({err, traceId}, `getJaegerTrace: Error retrieving spans from ${url}`);
+71 -29
View File
@@ -1,7 +1,4 @@
const assert = require('assert');
const bent = require('bent');
const postJSON = bent('POST', 'json', 200);
const getJSON = bent('GET', 'json', 200);
const {emailSimpleText} = require('./email-utils');
const {DbErrorForbidden} = require('../utils/errors');
@@ -10,13 +7,26 @@ const doGithubAuth = async(logger, payload) => {
try {
/* exchange the code for an access token */
const obj = await postJSON('https://github.com/login/oauth/access_token', {
client_id: payload.oauth2_client_id,
client_secret: process.env.GITHUB_CLIENT_SECRET,
code: payload.oauth2_code,
state: payload.oauth2_state,
redirect_uri: payload.oauth2_redirect_uri
const response = await fetch('https://github.com/login/oauth/access_token', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json'
},
body: JSON.stringify({
client_id: payload.oauth2_client_id,
client_secret: process.env.GITHUB_CLIENT_SECRET,
code: payload.oauth2_code,
state: payload.oauth2_state,
redirect_uri: payload.oauth2_redirect_uri
})
});
if (!response.ok) {
logger.error({response}, 'Error retrieving access_token from github');
throw new DbErrorForbidden(await response.text());
}
const obj = await response.json();
if (!obj.access_token) {
logger.error({obj}, 'Error retrieving access_token from github');
if (obj.error === 'bad_verification_code') throw new Error('bad verification code');
@@ -25,17 +35,31 @@ const doGithubAuth = async(logger, payload) => {
logger.debug({obj}, 'got response from github for access_token');
/* use the access token to get basic public info as well as primary email */
const userDetails = await getJSON('https://api.github.com/user', null, {
Authorization: `Bearer ${obj.access_token}`,
Accept: 'application/json',
'User-Agent': 'jambonz 1.0'
const userResponse = await fetch('https://api.github.com/user', {
headers: {
Authorization: `Bearer ${obj.access_token}`,
Accept: 'application/json',
'User-Agent': 'jambonz 1.0'
}
});
if (!userResponse.ok) {
logger.error({userResponse}, 'Error retrieving user details from github');
throw new DbErrorForbidden(await userResponse.text());
}
const userDetails = await userResponse.json();
const emails = await getJSON('https://api.github.com/user/emails', null, {
Authorization: `Bearer ${obj.access_token}`,
Accept: 'application/json',
'User-Agent': 'jambonz 1.0'
const emailsResponse = await fetch('https://api.github.com/user/emails', {
headers: {
Authorization: `Bearer ${obj.access_token}`,
Accept: 'application/json',
'User-Agent': 'jambonz 1.0'
}
});
if (!emailsResponse.ok) {
logger.error({emailsResponse}, 'Error retrieving emails from github');
throw new DbErrorForbidden(await emailsResponse.text());
}
const emails = await emailsResponse.json();
const primary = emails.find((e) => e.primary);
if (primary) Object.assign(userDetails, {
email: primary.email,
@@ -55,14 +79,26 @@ const doGoogleAuth = async(logger, payload) => {
try {
/* exchange the code for an access token */
const obj = await postJSON('https://oauth2.googleapis.com/token', {
client_id: payload.oauth2_client_id,
client_secret: process.env.GOOGLE_OAUTH_CLIENT_SECRET,
code: payload.oauth2_code,
state: payload.oauth2_state,
redirect_uri: payload.oauth2_redirect_uri,
grant_type: 'authorization_code'
const response = await fetch('https://oauth2.googleapis.com/token', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json'
},
body: JSON.stringify({
client_id: payload.oauth2_client_id,
client_secret: process.env.GOOGLE_OAUTH_CLIENT_SECRET,
code: payload.oauth2_code,
state: payload.oauth2_state,
redirect_uri: payload.oauth2_redirect_uri,
grant_type: 'authorization_code'
})
});
if (!response.ok) {
logger.error({response}, 'Error retrieving access_token from google');
throw new DbErrorForbidden(await response.text());
}
const obj = await response.json();
if (!obj.access_token) {
logger.error({obj}, 'Error retrieving access_token from github');
if (obj.error === 'bad_verification_code') throw new Error('bad verification code');
@@ -71,12 +107,18 @@ const doGoogleAuth = async(logger, payload) => {
logger.debug({obj}, 'got response from google for access_token');
/* use the access token to get basic public info as well as primary email */
const userDetails = await getJSON('https://www.googleapis.com/oauth2/v2/userinfo', null, {
Authorization: `Bearer ${obj.access_token}`,
Accept: 'application/json',
'User-Agent': 'jambonz 1.0'
const userDetailsResponse = await fetch('https://www.googleapis.com/oauth2/v2/userinfo', {
headers: {
Authorization: `Bearer ${obj.access_token}`,
Accept: 'application/json',
'User-Agent': 'jambonz 1.0'
}
});
if (!userDetailsResponse.ok) {
logger.error({userDetailsResponse}, 'Error retrieving user details from google');
throw new DbErrorForbidden(await userDetailsResponse.text());
}
const userDetails = await userDetailsResponse.json();
logger.info({userDetails}, 'retrieved user details from google');
return userDetails;
} catch (err) {
+100 -65
View File
@@ -3,7 +3,6 @@ const { TranscribeClient, ListVocabulariesCommand } = require('@aws-sdk/client-t
const { Deepgram } = require('@deepgram/sdk');
const sdk = require('microsoft-cognitiveservices-speech-sdk');
const { SpeechClient } = require('@soniox/soniox-node');
const bent = require('bent');
const fs = require('fs');
const { AssemblyAI } = require('assemblyai');
const {decrypt, obscureKey} = require('./encrypt-decrypt');
@@ -288,44 +287,46 @@ const testMicrosoftTts = async(logger, synthAudio, credentials) => {
const testWellSaidTts = async(logger, credentials) => {
const {api_key} = credentials;
try {
const post = bent('https://api.wellsaidlabs.com', 'POST', 'buffer', {
const response = await fetch('https://api.wellsaidlabs.com/v1/tts/stream', {
method: 'POST',
headers: {
'X-Api-Key': api_key,
'Accept': 'audio/mpeg',
'Content-Type': 'application/json'
});
const mp3 = await post('/v1/tts/stream', {
},
body: JSON.stringify({
text: 'Hello, world',
speaker_id: '3'
});
return mp3;
} catch (err) {
logger.info({err}, 'testWellSaidTts returned error');
throw err;
})
});
if (!response.ok) {
throw new Error('failed to synthesize speech');
}
return response.body;
};
const testElevenlabs = async(logger, credentials) => {
const {api_key, model_id} = credentials;
try {
const post = bent('https://api.elevenlabs.io', 'POST', 'buffer', {
const response = await fetch('https://api.elevenlabs.io/v1/text-to-speech/21m00Tcm4TlvDq8ikWAM', {
method: 'POST',
headers: {
'xi-api-key': api_key,
'Accept': 'audio/mpeg',
'Content-Type': 'application/json'
});
const mp3 = await post('/v1/text-to-speech/21m00Tcm4TlvDq8ikWAM', {
},
body: JSON.stringify({
text: 'Hello',
model_id,
voice_settings: {
stability: 0.5,
similarity_boost: 0.5
}
});
return mp3;
} catch (err) {
logger.info({err}, 'synthEvenlabs returned error');
throw err;
})
});
if (!response.ok) {
throw new Error('failed to synthesize speech');
}
return response.body;
};
const testPlayHT = async(logger, synthAudio, credentials) => {
@@ -466,18 +467,18 @@ const testVerbioTts = async(logger, synthAudio, credentials) => {
};
const testVerbioStt = async(logger, getVerbioAccessToken, credentials) => {
const token = await getVerbioAccessToken(credentials);
try {
const post = bent('https://us.rest.speechcenter.verbio.com', 'POST', 'json', {
const response = await fetch('https://us.rest.speechcenter.verbio.com/api/v1/recognize?language=en-US&version=V1', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token.access_token}`,
'User-Agent': 'jambonz',
'Content-Type': 'audio/wav'
});
const json = await post('/api/v1/recognize?language=en-US&version=V1',
fs.readFileSync(`${__dirname}/../../data/test_audio.wav`));
logger.debug({json}, 'successfully speech to text from verbio');
} catch (err) {
logger.info({err}, 'testWellSaidTts returned error');
throw err;
},
body: fs.readFileSync(`${__dirname}/../../data/test_audio.wav`)
});
if (!response.ok) {
logger.error({Error: await response.text()}, 'Error transcribing speech');
throw new Error('failed to transcribe speech');
}
};
@@ -546,16 +547,17 @@ const testAssemblyStt = async(logger, credentials) => {
const testVoxistStt = async(logger, credentials) => {
const {api_key} = credentials;
try {
const get = bent('https://api-asr.voxist.com', 'GET', 'json', {
const response = await fetch('https://api-asr.voxist.com/clients', {
headers: {
'Accept': 'application/json',
'x-lvl-key': api_key
});
await get('/clients');
} catch (err) {
logger.info({err}, 'failed to get clients from Voxist');
throw err;
}
});
if (!response.ok) {
logger.error({response}, 'Error retrieving clients');
throw new Error('failed to get clients');
}
return response.json();
};
const getSpeechCredential = (credential, logger) => {
@@ -812,17 +814,18 @@ async function getLanguagesVoicesForAws(credential, getTtsVoices, logger) {
async function getLanguagesVoicesForMicrosoft(credential, getTtsVoices, logger) {
if (credential) {
try {
const get = bent('https://westus.tts.speech.microsoft.com', 'GET', 'json', {
'Ocp-Apim-Subscription-Key' : credential.api_key
});
const voices = await get('/cognitiveservices/voices/list');
const tts = parseMicrosoftLanguagesVoices(voices);
return tranform(tts, SttMicrosoftLanguagesVoices);
} catch (err) {
logger.info('Error while fetching Microsoft languages, voices, return predefined values', err);
const response = await fetch('https://westus.tts.speech.microsoft.com/cognitiveservices/voices/list', {
headers: {
'Ocp-Apim-Subscription-Key': credential.api_key
}
});
if (!response.ok) {
logger.error({response}, 'Error fetching Microsoft voices');
throw new Error('failed to list voices');
}
const voices = await response.json();
const tts = parseMicrosoftLanguagesVoices(voices);
return tranform(tts, SttMicrosoftLanguagesVoices);
}
return tranform(TtsMicrosoftLanguagesVoices, SttMicrosoftLanguagesVoices);
}
@@ -885,14 +888,27 @@ async function getLanguagesVoicesForSpeechmatics(credential) {
async function getLanguagesVoicesForElevenlabs(credential) {
if (credential) {
const get = bent('https://api.elevenlabs.io', 'GET', 'json', {
'xi-api-key' : credential.api_key
const headers = {
'xi-api-key': credential.api_key
};
const getModelPromise = fetch('https://api.elevenlabs.io/v1/models', {
headers
});
const getVoicePromise = fetch('https://api.elevenlabs.io/v1/voices', {
headers
});
const [langResp, voiceResp] = await Promise.all([getModelPromise, getVoicePromise]);
const [langResp, voiceResp] = await Promise.all([get('/v1/models'), get('/v1/voices')]);
if (!langResp.ok || !voiceResp.ok) {
throw new Error('failed to list voices');
}
const model = langResp.find((m) => m.model_id === credential.model_id);
const models = langResp.map((m) => {
const langs = await langResp.json();
const voicesR = await voiceResp.json();
const model = langs.find((m) => m.model_id === credential.model_id);
const models = langs.map((m) => {
return {
value: m.model_id,
name: m.name
@@ -908,7 +924,7 @@ async function getLanguagesVoicesForElevenlabs(credential) {
if (languages && languages.length > 0) {
// using if condition to avoid \n character in name
const voices = voiceResp ? voiceResp.voices.map((v) => {
const voices = voicesR ? voicesR.voices.map((v) => {
let name = `${v.name}${v.category !== 'premade' ? ` (${v.category.trim()})` : ''} - (`;
if (v.labels.accent) name += `${v.labels.accent}, `;
if (v.labels.description) name += `${v.labels.description}, `;
@@ -946,18 +962,28 @@ const concat = (a) => {
const fetchLayHTVoices = async(credential) => {
if (credential) {
const get = bent('https://api.play.ht', 'GET', 'json', {
'AUTHORIZATION' : credential.api_key,
const headers = {
'AUTHORIZATION': credential.api_key,
'X-USER-ID': credential.user_id,
'Accept': 'application/json'
};
const response = await fetch('https://api.play.ht/api/v2/voices', {
headers
});
const voices = await get('/api/v2/voices');
if (!response.ok) {
throw new Error('failed to list voices');
}
const voices = await response.json();
let clone_voices = [];
try {
// try if the account has permission to cloned voice
//otherwise ignore this.
clone_voices = await get('/api/v2/cloned-voices');
const clone_voices_Response = await fetch('https://api.play.ht/api/v2/cloned-voices', {
headers
});
if (clone_voices_Response.ok) {
clone_voices = await clone_voices_Response.json();
}
} catch {}
return [clone_voices, voices];
}
@@ -1032,10 +1058,15 @@ async function getLanguagesVoicesForPlayHT(credential) {
async function getLanguagesVoicesForRimelabs(credential) {
const model_id = credential ? credential.model_id : null;
const get = bent('https://users.rime.ai', 'GET', 'json', {
'Accept': 'application/json'
const response = await fetch('https://users.rime.ai//data/voices/all-v2.json', {
headers: {
'Accept': 'application/json'
}
});
const voices = await get('/data/voices/all-v2.json');
if (!response.ok) {
throw new Error('failed to list models');
}
const voices = await response.json();
const modelVoices = model_id ? voices[model_id] :
Object.keys(voices).length > 0 ? voices[Object.keys(voices)[0]] : [];
const ttsVoices = Object.entries(modelVoices).map(([key, voices]) => ({
@@ -1238,14 +1269,18 @@ function parseVerbioLanguagesVoices(data) {
const fetchCartesiaVoices = async(credential) => {
if (credential) {
const get = bent('https://api.cartesia.ai', 'GET', 'json', {
'X-API-Key' : credential.api_key,
'Cartesia-Version': '2024-06-10',
'Accept': 'application/json'
const response = await fetch('https://api.cartesia.ai/voices', {
headers: {
'X-API-Key': credential.api_key,
'Cartesia-Version': '2024-06-10',
'Accept': 'application/json'
}
});
if (!response.ok) {
throw new Error('failed to list voices');
}
const voices = await get('/voices');
return voices;
return await response.json();
}
};
+41 -5
View File
@@ -6,7 +6,6 @@ assert.ok(process.env.STRIPE_API_KEY || process.env.NODE_ENV === 'test',
assert.ok(process.env.STRIPE_BASE_URL || process.env.NODE_ENV === 'test',
'missing env STRIPE_BASE_URL for billing operations');
const bent = require('bent');
const formurlencoded = require('form-urlencoded');
const qs = require('qs');
const toBase64 = (str) => Buffer.from(str || '', 'utf8').toString('base64');
@@ -14,10 +13,47 @@ const basicAuth = () => {
const header = `Basic ${toBase64(process.env.STRIPE_API_KEY)}`;
return {Authorization: header};
};
const postForm = bent(process.env.STRIPE_BASE_URL || 'http://127.0.0.1', 'POST', 'string',
Object.assign({'Content-Type': 'application/x-www-form-urlencoded'}, basicAuth()), 200);
const getJSON = bent(process.env.STRIPE_BASE_URL || 'http://127.0.0.1', 'GET', 'json', basicAuth(), 200);
const deleteJSON = bent(process.env.STRIPE_BASE_URL || 'http://127.0.0.1', 'DELETE', 'json', basicAuth(), 200);
const STRIPE_BASE_URL = process.env.STRIPE_BASE_URL || 'http://127.0.0.1';
const getJSON = async(path) => {
const response = await fetch(`${STRIPE_BASE_URL}${path}`, {
headers: {
'Content-Type': 'application/json',
...basicAuth()
}
});
if (!response.ok) {
throw new Error(`Error retrieving ${path} from stripe: ${response.status}`);
}
return await response.json();
};
const postForm = async(path, body) => {
const response = await fetch(`${STRIPE_BASE_URL}${path}`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
...basicAuth()
},
body
});
if (!response.ok) {
throw new Error(`Error posting to ${path} from stripe: ${response.status}`);
}
return await response.text();
};
const deleteJSON = async(path) => {
const response = await fetch(`${STRIPE_BASE_URL}${path}`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
...basicAuth()
}
});
if (!response.ok) {
throw new Error(`Error deleting ${path} from stripe: ${response.status}`);
}
return await response.json();
};
//const debug = require('debug')('jambonz:api-server');
const listProducts = async(logger) => await getJSON('/products?active=true');