import _ from 'lodash' import { getJsonBody } from './utils' import { getList, get, getAsBlob, patchAdd, patchReplace, patchRemove, patchReplaceFull, patchAddFull, httpApi, apiUploadCsv } from './common' import { assignNumbers } from './user' const minValue = 3 const generateSymbols = '!@#$%^*()_+~`|}{[]:;?><,./-=' const generateNumbers = '0123456789' const generateLowercase = 'abcdefghijklmnopqrstuvwxyz' const generateUppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' const generateLength = 12 export function getPreferences (id) { return new Promise((resolve, reject) => { httpApi.get('api/subscriberpreferences/' + id).then((result) => { resolve(getJsonBody(result.data)) }).catch((err) => { reject(err) }) }) } export async function getPreferencesDefs (id) { const result = await httpApi.get('api/subscriberpreferencedefs/') return getJsonBody(result.data) } export async function setPreference (id, field, value) { if (value === undefined || value === null || value === '' || value === false || (Array.isArray(value) && !value.length)) { await removePreference(id, field) } else { try { await replacePreference(id, field, value) } catch (err) { const errCode = err.status + '' if (errCode === '422') { // eslint-disable-next-line no-useless-catch try { await addPreference(id, field, value) } catch (innerErr) { throw innerErr } } else { throw err } } } } export async function setPreferencePhonebook (id, field, value) { if (value === undefined || value === null || value === '' || (Array.isArray(value) && !value.length)) { await removePreferencePhonebook(id, field) } else { try { await replacePreferencePhonebook(id, field, value) } catch (err) { if (err) { throw err } } } } export async function setPreferencePhonebookCustomer (id, field, value) { if (value === undefined || value === null || value === '' || (Array.isArray(value) && !value.length)) { await removePreferencePhonebookCustomer(id, field) } else { try { await replacePreferencePhonebookCustomer(id, field, value) } catch (err) { if (err) { throw err } } } } export function getNcosLevels (options) { return new Promise((resolve, reject) => { options = options || {} options = _.merge(options, { path: 'api/ncoslevels/', root: '_embedded.ngcp:ncoslevels', all: true }) getList(options).then((list) => { resolve(list) }).catch((err) => { reject(err) }) }) } export async function getNcosSet () { try { let streams = [] const res = await get({ path: 'api/v2/ncos/sets/' }) if (res?.data?.total_count > 0) { streams = getJsonBody(res.data)._embedded['ngcp:ncos/sets'] } return streams } catch (err) { return [] } } export async function removePreference (id, field) { return await patchRemove({ path: 'api/subscriberpreferences/' + id, fieldPath: field }) } export async function removePreferencePhonebook (id, field) { return await patchRemove({ path: 'api/subscriberphonebookentries/' + id, fieldPath: field }) } export async function removePreferencePhonebookCustomer (id, field) { return await patchRemove({ path: 'api/customerphonebookentries/' + id, fieldPath: field }) } export function addPreference (id, field, value) { return new Promise((resolve, reject) => { patchAdd({ path: 'api/subscriberpreferences/' + id, fieldPath: field, value: value }).then(() => { resolve() }).catch((err) => { reject(err) }) }) } export function addPreferenceFull (id, field, value) { return new Promise((resolve, reject) => { patchAddFull({ path: 'api/subscriberpreferences/' + id, fieldPath: field, value: value }).then((preferences) => { resolve(preferences) }).catch((err) => { reject(err) }) }) } export function replacePreference (id, field, value) { return new Promise((resolve, reject) => { patchReplace({ path: 'api/subscriberpreferences/' + id, fieldPath: field, value: value }).then(() => { resolve() }).catch((err) => { reject(err) }) }) } export function replacePreferencePhonebook (id, field, value) { return new Promise((resolve, reject) => { patchReplace({ path: 'api/subscriberphonebookentries/' + id, fieldPath: field, value: value }).then(() => { resolve() }).catch((err) => { reject(err) }) }) } export function replacePreferencePhonebookCustomer (id, field, value) { return new Promise((resolve, reject) => { patchReplace({ path: 'api/customerphonebookentries/' + id, fieldPath: field, value: value }).then(() => { resolve() }).catch((err) => { reject(err) }) }) } export function prependItemToArrayPreference (id, field, value) { return new Promise((resolve, reject) => { Promise.resolve().then(() => { return getPreferences(id) }).then((result) => { const prefs = _.cloneDeep(result) delete prefs._links prefs[field] = _.get(prefs, field, []) prefs[field] = [value].concat(prefs[field]) return httpApi.put('api/subscriberpreferences/' + id, prefs) }).then(() => { resolve() }).catch((err) => { reject(err) }) }) } export function appendItemToArrayPreference (id, field, value) { return new Promise((resolve, reject) => { Promise.resolve().then(() => { return getPreferences(id) }).then((result) => { const prefs = _.cloneDeep(result) delete prefs._links prefs[field] = _.get(prefs, field, []) prefs[field].push(value) return httpApi.put('api/subscriberpreferences/' + id, prefs) }).then(() => { resolve() }).catch((err) => { reject(err) }) }) } export function editItemInArrayPreference (id, field, itemIndex, value) { return new Promise((resolve, reject) => { Promise.resolve().then(() => { return getPreferences(id) }).then((result) => { const prefs = _.cloneDeep(result) delete prefs._links if (_.isArray(prefs[field]) && itemIndex < prefs[field].length) { prefs[field][itemIndex] = value return httpApi.put('api/subscriberpreferences/' + id, prefs) } else { return Promise.reject(new Error('Array index does not exists')) } }).then(() => { resolve() }).catch((err) => { reject(err) }) }) } export function removeItemFromArrayPreference (id, field, itemIndex) { return new Promise((resolve, reject) => { Promise.resolve().then(() => { return getPreferences(id) }).then((result) => { const prefs = _.cloneDeep(result) delete prefs._links prefs[field] = _.get(prefs, field, []) _.remove(prefs[field], (value, index) => { return index === itemIndex }) return httpApi.put('api/subscriberpreferences/' + id, prefs) }).then(() => { resolve() }).catch((err) => { reject(err) }) }) } export function setBlockInMode (id, value) { return setPreference(id, 'block_in_mode', value) } export function enableBlockIn (id) { return setBlockInMode(id, true) } export function disableBlockIn (id) { return setBlockInMode(id, false) } export function addToBlockInList (id, number) { return prependItemToArrayPreference(id, 'block_in_list', number) } export function editBlockInList (id, index, number) { return editItemInArrayPreference(id, 'block_in_list', index, number) } export function removeFromBlockInList (id, index) { return removeItemFromArrayPreference(id, 'block_in_list', index) } export function setBlockOutMode (id, value) { return setPreference(id, 'block_out_mode', value) } export function enableBlockOut (id) { return setBlockOutMode(id, true) } export function disableBlockOut (id) { return setBlockOutMode(id, false) } export function addToBlockOutList (id, number) { return prependItemToArrayPreference(id, 'block_out_list', number) } export function editBlockOutList (id, index, number) { return editItemInArrayPreference(id, 'block_out_list', index, number) } export function removeFromBlockOutList (id, index) { return removeItemFromArrayPreference(id, 'block_out_list', index) } export function setPrivacy (id, value) { return setPreference(id, 'clir', value) } export function enablePrivacy (id) { return setPrivacy(id, true) } export function disablePrivacy (id) { return setPrivacy(id, false) } export function createSubscriber (subscriber, config ) { const params = {} if (config.forceCli) { params.create_primary_acli = false } return new Promise((resolve, reject) => { httpApi.post('api/subscribers/', subscriber, { params } ).then((res) => { resolve(_.last(res.headers.location.split('/'))) }).catch((err) => { if (err.response.status >= 400) { reject(new Error(err.response.data.message)) } else { reject(err) } }) }) } export function deleteSubscriber (id) { return new Promise((resolve, reject) => { httpApi.delete('api/subscribers/' + id).then(() => { resolve() }).catch((err) => { if (err.response.status >= 400) { reject(new Error(err.response.data.message)) } else { reject(err) } }) }) } export function setField (id, field, value) { return new Promise((resolve, reject) => { httpApi.patch('api/subscribers/' + id, [{ op: 'replace', path: '/' + field, value: value }], { headers: { 'Content-Type': 'application/json-patch+json', Prefer: 'return=minimal' } }).then((result) => { resolve(result) }).catch((err) => { if (err.response.status >= 400) { reject(new Error(err.response.data.message)) } else { reject(err) } }) }) } export function setDisplayName (id, displayName) { return setField(id, 'display_name', displayName) } export function setWebUsername (id, webUsername) { return setField(id, 'webusername', webUsername) } export function setPbxExtension (id, pbxExtension) { return setField(id, 'pbx_extension', pbxExtension) } export function setPbxWebPassword (id, pbxWebPassword) { return setField(id, 'webpassword', pbxWebPassword) } export function setPbxSIPPassword (id, pbxWebPassword) { return setField(id, 'password', pbxWebPassword) } export function setPbxHuntPolicy (id, pbxHuntPolicy) { return setField(id, 'pbx_hunt_policy', pbxHuntPolicy) } export function setPbxHuntTimeout (id, pbxHuntTimeout) { return setField(id, 'pbx_hunt_timeout', pbxHuntTimeout) } export function setPbxHuntCancelMode (id, pbxHuntCancelMode) { return setField(id, 'pbx_hunt_cancel_mode', pbxHuntCancelMode) } export function setPbxGroupMemberIds (id, ids) { return setField(id, 'pbx_groupmember_ids', ids) } export function setPbxGroupIds (id, ids) { return setField(id, 'pbx_group_ids', ids) } export function setPreferenceIntraPbx (id, value) { return setPreference(id, 'clir_intrapbx', value) } export function setPreferenceMusicOnHold (id, value) { return setPreference(id, 'music_on_hold', value) } export function setPreferenceAnnouncementCfu (id, value) { return setPreference(id, 'play_announce_before_cf', value) } export function setPreferenceAnnouncementCallSetup (id, value) { return setPreference(id, 'play_announce_before_call_setup', value) } export function setPreferenceAnnouncementToCallee (id, value) { return setPreference(id, 'play_announce_to_callee', value) } export function setPreferenceIgnoreCfWhenHunting (id, value) { return setPreference(id, 'ignore_cf_when_hunting', value) } export function setPreferenceCstaClient (id, value) { return setPreference(id, 'csta_client', value) } export function setPreferenceCstaController (id, value) { return setPreference(id, 'csta_controller', value) } export function setPreferenceCli (id, value) { return setPreference(id, 'cli', value) } export function getSubscribers (options) { return new Promise((resolve, reject) => { options = options || {} options = _.merge(options, { path: 'api/subscribers/', root: '_embedded.ngcp:subscribers' }) getList(options).then((list) => { resolve(list) }).catch((err) => { reject(err) }) }) } export function getFullSubscribers (options) { return new Promise((resolve, reject) => { let subscribers = { items: [] } Promise.resolve().then(() => { return getSubscribers(options) }).then(($subscribers) => { subscribers = $subscribers const promises = [] subscribers.items.forEach((subscriber) => { promises.push(getSubscriberPreference(subscriber.id)) }) return Promise.all(promises) }).then((preferences) => { resolve({ subscribers: subscribers, preferences: preferences }) }).catch((err) => { reject(err) }) }) } export function getSubscriber (id) { return new Promise((resolve, reject) => { get({ path: 'api/subscribers/' + id }).then((subscriber) => { resolve(subscriber) }).catch((err) => { reject(err) }) }) } export function getSubscriberPreference (id) { return new Promise((resolve, reject) => { get({ path: 'api/subscriberpreferences/' + id }).then((subscriberPreference) => { resolve(subscriberPreference) }).catch((err) => { reject(err) }) }) } export function setBlockAnonymous (id, value) { return setPreference(id, 'block_in_clir', value) } export function blockAnonymous (id) { return setBlockAnonymous(id, true) } export function allowAnonymous (id) { return setBlockAnonymous(id, false) } export function getSubscribersByCallQueueEnabled () { return new Promise((resolve, reject) => { let prefsByCallQueueEnabled = [] Promise.resolve().then(() => { return getList({ path: 'api/subscriberpreferences/', root: '_embedded.ngcp:subscriberpreferences', all: true }) }).then((prefs) => { const subscriberPromises = [] prefsByCallQueueEnabled = prefs.items.filter((pref) => { return pref.cloud_pbx_callqueue === true }) prefsByCallQueueEnabled.forEach((pref) => { subscriberPromises.push(getSubscriber(pref.subscriber_id)) }) return Promise.all(subscriberPromises) }).then((subscribers) => { subscribers.forEach((subscriber, index) => { subscriber.prefs = prefsByCallQueueEnabled[index] }) resolve(subscribers) }).catch((err) => { reject(err) }) }) } export function addNewCallQueueConfig (id, config) { return httpApi.put('api/subscriberpreferences/' + id, config) } export function editCallQueuePreference (id, config) { return new Promise((resolve, reject) => { const $prefs = Object.assign(config, { cloud_pbx_callqueue: true }) Promise.resolve().then(() => { return getPreferences(id) }).then((result) => { const prefs = Object.assign(result, $prefs) delete prefs._links return httpApi.put('api/subscriberpreferences/' + id, prefs) }).then(() => { resolve() }).catch((err) => { reject(err) }) }) } export function setQueueLength (id, queueLength) { return editCallQueuePreference(id, { max_queue_length: queueLength }) } export function setWrapUpTime (id, wrapUpTime) { return editCallQueuePreference(id, { queue_wrap_up_time: wrapUpTime }) } export function removeCallQueueConfig (subscriberId) { const param = { cloud_pbx_callqueue: false } return httpApi.put('api/subscriberpreferences/' + subscriberId, param) } export function getAllPreferences (options) { return new Promise((resolve, reject) => { options = options || {} options = _.merge(options, { path: 'api/subscriberpreferences/', root: '_embedded.ngcp:subscriberpreferences', all: true }) getList(options).then((list) => { resolve(list) }).catch((err) => { reject(err) }) }) } export function getSubscriberAndPreferences (id) { return new Promise((resolve, reject) => { Promise.all([ getSubscriber(id), getPreferences(id) ]).then((result) => { resolve({ subscriber: result[0], preferences: result[1] }) }).catch((err) => { reject(err) }) }) } export function setSubscriberNumbers (options) { return new Promise((resolve, reject) => { Promise.resolve().then(() => { return Promise.all([ assignNumbers(options.assignedNumbers, options.subscriberId), assignNumbers(options.unassignedNumbers, options.pilotId) ]) }).then(() => { if (options.assignedNumbers.length > 0) { return Promise.resolve({ subscriber: null, preferences: null }) } else { return getSubscriberAndPreferences(options.subscriberId) } }).then((result) => { resolve(result) }).catch((err) => { reject(err) }) }) } export function changePassword (subscriber, newPassword) { return new Promise((resolve, reject) => { patchReplaceFull({ path: 'api/subscribers/' + subscriber, fieldPath: 'webpassword', value: newPassword }).then((subscriber) => { resolve(subscriber) }).catch((err) => { reject(err) }) }) } export async function changeSIPPassword (subscriber, newPassword) { return patchReplaceFull({ path: 'api/subscribers/' + subscriber, fieldPath: 'password', value: newPassword }) } export async function resetPassword ({ username, domain = '' }) { const payLoad = { domain, type: 'subscriber', username } return await httpApi.post('api/passwordreset/', payLoad) } export async function recoverPassword (data) { const payLoad = { new_password: data.password, token: data.token } return await httpApi.post('api/passwordrecovery/', payLoad) } export async function getBrandingLogo (subscriberId) { const url = 'api/resellerbrandinglogos/?subscriber_id=' + subscriberId try { const res = await httpApi.get(url, { responseType: 'blob' }) return URL.createObjectURL(res.data) } catch (err) { return null } } export async function getRecordings (options) { const data = { recordings: [], total_count: 0 } const res = await httpApi.get('api/callrecordings/', { params: options }) if (res.data.total_count > 0) { const recordings = getJsonBody(res.data)._embedded['ngcp:callrecordings'] data.recordings = recordings.map(recording => { return { id: recording.id, time: recording.start_time, caller: recording.caller, callee: recording.callee, files: [] } }) data.total_count = res.data.total_count } return data } export async function getRecordingStreams (recId) { let streams = [] const res = await httpApi.get('api/callrecordingstreams/', { params: { recording_id: recId } }) if (res.data.total_count > 0) { streams = getJsonBody(res.data)._embedded['ngcp:callrecordingstreams'] } return streams } export async function downloadRecordingStream (fileId) { const res = await httpApi.get('api/callrecordingfiles/' + fileId, { responseType: 'blob' }) return res.data } export async function getSubscriberRegistrations (options) { let all = false if (options.rows === 0) { delete options.rows delete options.page all = true } if (!options.order_by) { delete options.order_by delete options.order_by_direction } const list = await getList({ resource: 'subscriberregistrations', all, params: options }) return list } export async function getSubscriberPhonebook (options) { let all = false if (options.rows === 0) { delete options.rows delete options.page all = true } if (!options.order_by) { delete options.order_by delete options.order_by_direction } const list = await getList({ resource: 'subscriberphonebookentries', all, params: options }) return list } export async function getCustomerPhonebook (options) { let all = false if (options.rows === 0) { delete options.rows delete options.page all = true } if (!options.order_by) { delete options.order_by delete options.order_by_direction } const list = await getList({ resource: 'customerphonebookentries', all, params: options }) return list } export async function createPhonebook (data) { const payLoad = { name: data.name, number: data.number, shared: data.shared } return await httpApi.post('api/subscriberphonebookentries/', payLoad) } export async function createCustomerPhonebook (data) { const payLoad = { name: data.name, number: data.number } return await httpApi.post('api/customerphonebookentries/', payLoad) } export async function uploadCsv (context, formData) { const config = { headers: { 'Content-Type': 'text/csv' } } const purgeExistingValue = formData?.purge_existing ? '1' : '0' await apiUploadCsv({ path: 'api/customerphonebookentries' + '/?purge_existing=' + purgeExistingValue + '&customer_id=' + formData.customer_id, data: formData.file, config }) } export function setValueShared (id, value) { return setPreferencePhonebook(id, 'shared', value) } export function setValueName (id, value) { return setPreferencePhonebook(id, 'name', value) } export function setValueNameCustomer (id, value) { return setPreferencePhonebookCustomer(id, 'name', value) } export function setValueNumber (id, value) { return setPreferencePhonebook(id, 'number', value) } export function setValueNumberCustomer (id, value) { return setPreferencePhonebookCustomer(id, 'number', value) } export async function getRecordingStream (fileId) { return await getAsBlob({ path: 'api/callrecordingfiles/' + fileId }) } export async function getSubscriberProfile (id) { const profile = await get({ path: `api/subscriberprofiles/${id}` }) return profile } export function getCustomerPreference (id) { return new Promise((resolve, reject) => { get({ path: 'api/customerpreferences/' + id }).then((customerPreferences) => { resolve(customerPreferences) }).catch((err) => { reject(err) }) }) } export function setCustomerPreference (customerId, customerValue, fieldName) { return new Promise((resolve, reject) => { Promise.resolve().then(() => { if (customerValue === undefined || customerValue === null || customerValue === '' || (Array.isArray(customerValue) && !customerValue.length)) { return patchRemove({ path: 'api/customerpreferences/' + customerId, fieldPath: fieldName }).then((customer) => { resolve(customer.data) }) } return patchReplaceFull({ path: 'api/customerpreferences/' + customerId, fieldPath: fieldName, value: customerValue }) }).then((customer) => { resolve(customer) }).catch((err) => { const errCode = err.status + '' if (errCode === '422') { return patchAdd({ path: 'api/customerpreferences/' + customerId, fieldPath: fieldName, value: customerValue }) } }).then((customer) => { resolve(customer.data) }).catch((err) => { reject(err) }) }) } export async function generateGeneralPassword () { const getRandomValues = (buf) => crypto.getRandomValues(buf) const getRandomInt = (max) => { const randomBuffer = new Uint32Array(1) getRandomValues(randomBuffer) return Math.floor(randomBuffer[0] / (0xFFFFFFFF + 1) * max) } const getRandomChar = (charset) => charset[getRandomInt(charset.length)] const charGroups = [ generateNumbers, generateLowercase, generateUppercase, generateSymbols ] let password = charGroups.flatMap(group => Array.from({ length: minValue }, () => getRandomChar(group)) ).join('') while (password.length < generateLength) { const allChars = charGroups.join('') password += getRandomChar(allChars) } password = password.split('').sort(() => getRandomInt(2) - 0.5).join('') return password }