@unboundcx/sdk 2.8.6 → 2.8.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -1
- package/base.js +43 -12
- package/index.js +21 -3
- package/package.json +5 -2
- package/proto/transcription.proto +207 -0
- package/services/ai/SttStream.js +311 -0
- package/services/ai/playbooks.js +958 -0
- package/services/ai.js +773 -52
- package/services/engagementMetrics.js +6 -2
- package/services/objects.js +12 -3
- package/services/phoneNumbers.js +88 -3
- package/services/sipEndpoints.js +105 -33
- package/services/storage.js +176 -6
- package/services/taskRouter/MetricsService.js +111 -0
- package/services/taskRouter/TaskRouterService.js +12 -0
- package/services/taskRouter/TaskService.js +838 -0
- package/services/taskRouter/WorkerService.js +394 -0
- package/services/taskRouter.js +6 -0
- package/services/video.js +145 -5
- package/services/voice.js +124 -67
- package/services/workflows.js +34 -7
|
@@ -139,7 +139,11 @@ export class EngagementMetricsService {
|
|
|
139
139
|
* @param {string[]} [options.userIds] - Array of user IDs to filter by
|
|
140
140
|
* @returns {Promise<Object>} All metrics
|
|
141
141
|
*/
|
|
142
|
-
async getDashboardMetrics({
|
|
142
|
+
async getDashboardMetrics({
|
|
143
|
+
queueIds = [],
|
|
144
|
+
statuses = [],
|
|
145
|
+
userIds = [],
|
|
146
|
+
} = {}) {
|
|
143
147
|
return this.getMetrics({
|
|
144
148
|
queueIds,
|
|
145
149
|
statuses,
|
|
@@ -150,4 +154,4 @@ export class EngagementMetricsService {
|
|
|
150
154
|
includeAgentPerformance: true,
|
|
151
155
|
});
|
|
152
156
|
}
|
|
153
|
-
}
|
|
157
|
+
}
|
package/services/objects.js
CHANGED
|
@@ -45,9 +45,13 @@ export class ObjectsService {
|
|
|
45
45
|
}
|
|
46
46
|
} else if (typeof arg1 === 'object') {
|
|
47
47
|
id = arg1.id;
|
|
48
|
-
query
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
if (arg1.query) {
|
|
49
|
+
query = { ...arg1.query };
|
|
50
|
+
} else {
|
|
51
|
+
query = { ...arg1 };
|
|
52
|
+
if (query.id) {
|
|
53
|
+
delete query.id;
|
|
54
|
+
}
|
|
51
55
|
}
|
|
52
56
|
}
|
|
53
57
|
|
|
@@ -63,7 +67,9 @@ export class ObjectsService {
|
|
|
63
67
|
);
|
|
64
68
|
|
|
65
69
|
const params = { query };
|
|
70
|
+
|
|
66
71
|
const result = await this.sdk._fetch(`/object/${id}`, 'GET', params);
|
|
72
|
+
// console.log(`sdk.objects.byId :: params :: `, params, result);
|
|
67
73
|
return result;
|
|
68
74
|
}
|
|
69
75
|
|
|
@@ -97,6 +103,7 @@ export class ObjectsService {
|
|
|
97
103
|
previousId = null,
|
|
98
104
|
orderByDirection = 'DESC',
|
|
99
105
|
expandDetails = false,
|
|
106
|
+
meta = {},
|
|
100
107
|
} = args[0];
|
|
101
108
|
|
|
102
109
|
this.sdk.validateParams(
|
|
@@ -109,6 +116,7 @@ export class ObjectsService {
|
|
|
109
116
|
previousId,
|
|
110
117
|
orderByDirection,
|
|
111
118
|
expandDetails,
|
|
119
|
+
meta,
|
|
112
120
|
},
|
|
113
121
|
{
|
|
114
122
|
object: { type: 'string', required: true },
|
|
@@ -119,6 +127,7 @@ export class ObjectsService {
|
|
|
119
127
|
previousId: { type: 'string', required: false },
|
|
120
128
|
orderByDirection: { type: 'string', required: false },
|
|
121
129
|
expandDetails: { type: 'boolean', required: false },
|
|
130
|
+
meta: { type: 'object', required: false },
|
|
122
131
|
},
|
|
123
132
|
);
|
|
124
133
|
|
package/services/phoneNumbers.js
CHANGED
|
@@ -110,10 +110,21 @@ export class PhoneNumbersService {
|
|
|
110
110
|
voiceAppMetaData,
|
|
111
111
|
voiceRecordTypeId,
|
|
112
112
|
messagingRecordTypeId,
|
|
113
|
+
recordCalls,
|
|
113
114
|
},
|
|
114
115
|
) {
|
|
115
116
|
this.sdk.validateParams(
|
|
116
|
-
{
|
|
117
|
+
{
|
|
118
|
+
id,
|
|
119
|
+
messagingWebHookUrl,
|
|
120
|
+
voiceWebHookUrl,
|
|
121
|
+
voiceAppExternalUrl,
|
|
122
|
+
voiceAppExternalMethod,
|
|
123
|
+
voiceApp,
|
|
124
|
+
voiceRecordTypeId,
|
|
125
|
+
messagingRecordTypeId,
|
|
126
|
+
recordCalls,
|
|
127
|
+
},
|
|
117
128
|
{
|
|
118
129
|
id: { type: 'string', required: true },
|
|
119
130
|
name: { type: 'string', required: false },
|
|
@@ -122,9 +133,9 @@ export class PhoneNumbersService {
|
|
|
122
133
|
voiceAppExternalUrl: { type: 'string', required: false },
|
|
123
134
|
voiceAppExternalMethod: { type: 'string', required: false },
|
|
124
135
|
voiceApp: { type: 'string', required: false },
|
|
125
|
-
voiceAppMetaData: { type: 'object', required: false },
|
|
126
136
|
voiceRecordTypeId: { type: 'string', required: false },
|
|
127
137
|
messagingRecordTypeId: { type: 'string', required: false },
|
|
138
|
+
recordCalls: { type: 'boolean', required: false },
|
|
128
139
|
},
|
|
129
140
|
);
|
|
130
141
|
|
|
@@ -142,6 +153,7 @@ export class PhoneNumbersService {
|
|
|
142
153
|
if (voiceRecordTypeId) updateData.voiceRecordTypeId = voiceRecordTypeId;
|
|
143
154
|
if (messagingRecordTypeId)
|
|
144
155
|
updateData.messagingRecordTypeId = messagingRecordTypeId;
|
|
156
|
+
if (recordCalls !== undefined) updateData.recordCalls = recordCalls;
|
|
145
157
|
|
|
146
158
|
const params = {
|
|
147
159
|
body: updateData,
|
|
@@ -187,10 +199,83 @@ export class PhoneNumbersService {
|
|
|
187
199
|
return result;
|
|
188
200
|
}
|
|
189
201
|
|
|
190
|
-
|
|
202
|
+
/**
|
|
203
|
+
* Get routing options for phone numbers or extensions
|
|
204
|
+
*
|
|
205
|
+
* Supports multiple query modes:
|
|
206
|
+
* 1. Get list of valid app types with metadata (appType: 'list')
|
|
207
|
+
* 2. Get all application types (default)
|
|
208
|
+
* 3. Get details for a specific application type
|
|
209
|
+
* 4. Get versions for a specific workflow
|
|
210
|
+
*
|
|
211
|
+
* @param {Object} [options] - Query options
|
|
212
|
+
* @param {string} [options.mode] - Context mode: 'phoneNumbers' (default) or 'extensions'
|
|
213
|
+
* @param {string} [options.type] - Filter by routing type: 'voice' or 'messaging'
|
|
214
|
+
* @param {string} [options.appType] - 'list' for metadata, or specific app type: 'workflows', 'extensions', 'voiceApps', 'users'
|
|
215
|
+
* @param {string} [options.workflowId] - Get versions for a specific workflow ID
|
|
216
|
+
* @param {string} [options.search] - Search/filter by name
|
|
217
|
+
* @returns {Promise<Object>} Routing options based on query parameters
|
|
218
|
+
* @example
|
|
219
|
+
* // Get metadata about available app types
|
|
220
|
+
* const metadata = await sdk.phoneNumbers.getRoutingOptions({ appType: 'list' });
|
|
221
|
+
* // Returns: { types: [{ key: 'voiceApps', label: 'Voice Applications', description: '...', ... }] }
|
|
222
|
+
*
|
|
223
|
+
* // Get all application types for phone numbers (default)
|
|
224
|
+
* const all = await sdk.phoneNumbers.getRoutingOptions();
|
|
225
|
+
* // Returns: { voiceApps: [...], workflows: [...], extensions: [...], webhooks: [...] }
|
|
226
|
+
*
|
|
227
|
+
* // Get routing options for extensions
|
|
228
|
+
* const extensionOptions = await sdk.phoneNumbers.getRoutingOptions({ mode: 'extensions' });
|
|
229
|
+
* // Returns: { voiceApps: [...], workflows: [...], users: [...] }
|
|
230
|
+
*
|
|
231
|
+
* // Get only voice routing options
|
|
232
|
+
* const voice = await sdk.phoneNumbers.getRoutingOptions({ type: 'voice' });
|
|
233
|
+
*
|
|
234
|
+
* // Get all workflows with their versions
|
|
235
|
+
* const workflows = await sdk.phoneNumbers.getRoutingOptions({ appType: 'workflows' });
|
|
236
|
+
*
|
|
237
|
+
* // Get all users (for extension routing)
|
|
238
|
+
* const users = await sdk.phoneNumbers.getRoutingOptions({ mode: 'extensions', appType: 'users' });
|
|
239
|
+
*
|
|
240
|
+
* // Get versions for a specific workflow
|
|
241
|
+
* const versions = await sdk.phoneNumbers.getRoutingOptions({ workflowId: 'wf_123' });
|
|
242
|
+
*
|
|
243
|
+
* // Search workflows by name
|
|
244
|
+
* const filtered = await sdk.phoneNumbers.getRoutingOptions({ appType: 'workflows', search: 'customer' });
|
|
245
|
+
*/
|
|
246
|
+
async getRoutingOptions({ mode, type, appType, workflowId, search } = {}) {
|
|
247
|
+
const validationSchema = {};
|
|
248
|
+
const args = arguments[0] || {};
|
|
249
|
+
|
|
250
|
+
if ('mode' in args)
|
|
251
|
+
validationSchema.mode = { type: 'string', required: false };
|
|
252
|
+
if ('type' in args)
|
|
253
|
+
validationSchema.type = { type: 'string', required: false };
|
|
254
|
+
if ('appType' in args)
|
|
255
|
+
validationSchema.appType = { type: 'string', required: false };
|
|
256
|
+
if ('workflowId' in args)
|
|
257
|
+
validationSchema.workflowId = { type: 'string', required: false };
|
|
258
|
+
if ('search' in args)
|
|
259
|
+
validationSchema.search = { type: 'string', required: false };
|
|
260
|
+
|
|
261
|
+
if (Object.keys(validationSchema).length > 0) {
|
|
262
|
+
this.sdk.validateParams(args, validationSchema);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const params = {
|
|
266
|
+
query: {
|
|
267
|
+
mode,
|
|
268
|
+
type,
|
|
269
|
+
appType,
|
|
270
|
+
workflowId,
|
|
271
|
+
search,
|
|
272
|
+
},
|
|
273
|
+
};
|
|
274
|
+
|
|
191
275
|
const result = await this.sdk._fetch(
|
|
192
276
|
'/phoneNumbers/routing-options',
|
|
193
277
|
'GET',
|
|
278
|
+
params,
|
|
194
279
|
);
|
|
195
280
|
return result;
|
|
196
281
|
}
|
package/services/sipEndpoints.js
CHANGED
|
@@ -3,23 +3,45 @@ export class SipEndpointsService {
|
|
|
3
3
|
this.sdk = sdk;
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Create a new SIP endpoint
|
|
8
|
+
* @param {Object} options - The endpoint configuration
|
|
9
|
+
* @param {string} options.recordTypeId - Record type ID for permissions
|
|
10
|
+
* @param {string} options.userId - User ID to associate with this endpoint
|
|
11
|
+
* @param {string} options.type - Endpoint type: 'webRtc' or 'ipPhone'
|
|
12
|
+
* @param {string} options.name - Endpoint name
|
|
13
|
+
* @param {string} options.macAddress - MAC address for ipPhone type
|
|
14
|
+
* @param {Object} options - Additional provisioning fields (timezone, vlanId, etc.)
|
|
15
|
+
* @returns {Promise<Object>} Created endpoint
|
|
16
|
+
*/
|
|
17
|
+
async create(options) {
|
|
18
|
+
const {
|
|
19
|
+
recordTypeId,
|
|
20
|
+
userId,
|
|
21
|
+
type,
|
|
22
|
+
name,
|
|
23
|
+
macAddress,
|
|
24
|
+
useSecureCalling,
|
|
25
|
+
...provisioningFields
|
|
26
|
+
} = options;
|
|
27
|
+
|
|
7
28
|
this.sdk.validateParams(
|
|
8
|
-
{
|
|
29
|
+
{ type, useSecureCalling },
|
|
9
30
|
{
|
|
10
|
-
recordTypeId: { type: 'string', required: false },
|
|
11
31
|
type: { type: 'string', required: true },
|
|
12
|
-
|
|
13
|
-
name: { type: 'string', required: false },
|
|
32
|
+
useSecureCalling: { type: 'boolean', required: false },
|
|
14
33
|
},
|
|
15
34
|
);
|
|
16
35
|
|
|
17
36
|
const params = {
|
|
18
37
|
body: {
|
|
19
38
|
recordTypeId,
|
|
20
|
-
type,
|
|
21
39
|
userId,
|
|
40
|
+
type,
|
|
41
|
+
useSecureCalling,
|
|
22
42
|
name,
|
|
43
|
+
macAddress,
|
|
44
|
+
...provisioningFields, // Include all provisioning fields
|
|
23
45
|
},
|
|
24
46
|
};
|
|
25
47
|
|
|
@@ -27,23 +49,53 @@ export class SipEndpointsService {
|
|
|
27
49
|
return result;
|
|
28
50
|
}
|
|
29
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Get the authenticated user's WebRTC endpoint details
|
|
54
|
+
* @returns {Promise<Object>} WebRTC endpoint configuration
|
|
55
|
+
*/
|
|
30
56
|
async getWebRtcDetails() {
|
|
31
|
-
const
|
|
32
|
-
const result = await this.sdk._fetch('/sipEndpoints', 'GET', params, true);
|
|
57
|
+
const result = await this.sdk._fetch('/sipEndpoints/webrtc', 'GET');
|
|
33
58
|
return result;
|
|
34
59
|
}
|
|
35
60
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Update a SIP endpoint
|
|
63
|
+
* @param {string} endpointId - The endpoint ID
|
|
64
|
+
* @param {Object} options - The fields to update
|
|
65
|
+
* @param {string} options.name - Endpoint name
|
|
66
|
+
* @param {string} options.userId - User ID to associate with this endpoint
|
|
67
|
+
* * @param {string} options.useSecureCalling - provision with TLS / secure calling
|
|
68
|
+
* @param {string} options.macAddress - MAC address
|
|
69
|
+
* @param {boolean} options.useIceAccelerator - Enable/disable ICE accelerator
|
|
70
|
+
* @param {Object} options - Additional provisioning fields (timezone, vlanId, etc.)
|
|
71
|
+
* @returns {Promise<Object>} Updated endpoint
|
|
72
|
+
*/
|
|
73
|
+
async update(endpointId, options) {
|
|
74
|
+
this.sdk.validateParams(
|
|
75
|
+
{ endpointId },
|
|
76
|
+
{
|
|
77
|
+
endpointId: { type: 'string', required: true },
|
|
78
|
+
},
|
|
79
|
+
);
|
|
40
80
|
|
|
41
|
-
|
|
42
|
-
|
|
81
|
+
const params = {
|
|
82
|
+
body: { ...options }, // Pass all options through
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const result = await this.sdk._fetch(
|
|
86
|
+
`/sipEndpoints/${endpointId}`,
|
|
87
|
+
'PUT',
|
|
88
|
+
params,
|
|
89
|
+
);
|
|
43
90
|
return result;
|
|
44
91
|
}
|
|
45
92
|
|
|
46
|
-
|
|
93
|
+
/**
|
|
94
|
+
* Delete a SIP endpoint
|
|
95
|
+
* @param {string} endpointId - The endpoint ID
|
|
96
|
+
* @returns {Promise<Object>} Deletion result
|
|
97
|
+
*/
|
|
98
|
+
async delete(endpointId) {
|
|
47
99
|
this.sdk.validateParams(
|
|
48
100
|
{ endpointId },
|
|
49
101
|
{
|
|
@@ -51,39 +103,59 @@ export class SipEndpointsService {
|
|
|
51
103
|
},
|
|
52
104
|
);
|
|
53
105
|
|
|
54
|
-
const result = await this.sdk._fetch(
|
|
106
|
+
const result = await this.sdk._fetch(
|
|
107
|
+
`/sipEndpoints/${endpointId}`,
|
|
108
|
+
'DELETE',
|
|
109
|
+
);
|
|
55
110
|
return result;
|
|
56
111
|
}
|
|
57
112
|
|
|
58
|
-
|
|
113
|
+
/**
|
|
114
|
+
* Reboot a SIP endpoint (forces re-registration)
|
|
115
|
+
* @param {string} endpointId - The endpoint ID
|
|
116
|
+
* @returns {Promise<Object>} Reboot result
|
|
117
|
+
*/
|
|
118
|
+
async reboot(endpointId) {
|
|
59
119
|
this.sdk.validateParams(
|
|
60
120
|
{ endpointId },
|
|
61
121
|
{
|
|
62
122
|
endpointId: { type: 'string', required: true },
|
|
63
|
-
displayName: { type: 'string', required: false },
|
|
64
|
-
description: { type: 'string', required: false },
|
|
65
|
-
enabled: { type: 'boolean', required: false },
|
|
66
123
|
},
|
|
67
124
|
);
|
|
68
125
|
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
126
|
+
const result = await this.sdk._fetch(
|
|
127
|
+
`/sipEndpoints/${endpointId}/reboot`,
|
|
128
|
+
'POST',
|
|
129
|
+
);
|
|
130
|
+
return result;
|
|
131
|
+
}
|
|
73
132
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
133
|
+
/**
|
|
134
|
+
* Change the access secret for a SIP endpoint
|
|
135
|
+
* @param {string} endpointId - The endpoint ID
|
|
136
|
+
* @returns {Promise<Object>} New endpoint credentials
|
|
137
|
+
*/
|
|
138
|
+
async changeAccessSecret(endpointId) {
|
|
139
|
+
this.sdk.validateParams(
|
|
140
|
+
{ endpointId },
|
|
141
|
+
{
|
|
142
|
+
endpointId: { type: 'string', required: true },
|
|
143
|
+
},
|
|
144
|
+
);
|
|
77
145
|
|
|
78
146
|
const result = await this.sdk._fetch(
|
|
79
|
-
`/sipEndpoints/${endpointId}`,
|
|
80
|
-
'
|
|
81
|
-
params,
|
|
147
|
+
`/sipEndpoints/${endpointId}/secret`,
|
|
148
|
+
'POST',
|
|
82
149
|
);
|
|
83
150
|
return result;
|
|
84
151
|
}
|
|
85
152
|
|
|
86
|
-
|
|
153
|
+
/**
|
|
154
|
+
* Change the provisioning secret for a SIP endpoint
|
|
155
|
+
* @param {string} endpointId - The endpoint ID
|
|
156
|
+
* @returns {Promise<Object>} New endpoint credentials
|
|
157
|
+
*/
|
|
158
|
+
async changeProvisioningSecret(endpointId) {
|
|
87
159
|
this.sdk.validateParams(
|
|
88
160
|
{ endpointId },
|
|
89
161
|
{
|
|
@@ -92,8 +164,8 @@ export class SipEndpointsService {
|
|
|
92
164
|
);
|
|
93
165
|
|
|
94
166
|
const result = await this.sdk._fetch(
|
|
95
|
-
`/sipEndpoints/${endpointId}`,
|
|
96
|
-
'
|
|
167
|
+
`/sipEndpoints/${endpointId}/secret/provisioning`,
|
|
168
|
+
'POST',
|
|
97
169
|
);
|
|
98
170
|
return result;
|
|
99
171
|
}
|
package/services/storage.js
CHANGED
|
@@ -213,8 +213,25 @@ export class StorageService {
|
|
|
213
213
|
formFields,
|
|
214
214
|
endpoint = '/storage/upload',
|
|
215
215
|
method = 'POST',
|
|
216
|
+
onProgress = null,
|
|
217
|
+
skipClamscan = false,
|
|
216
218
|
) {
|
|
217
219
|
const isNode = typeof window === 'undefined';
|
|
220
|
+
|
|
221
|
+
// In browser with progress callback: Use XMLHttpRequest
|
|
222
|
+
if (!isNode && onProgress && typeof onProgress === 'function') {
|
|
223
|
+
return this._performUploadWithProgress(
|
|
224
|
+
file,
|
|
225
|
+
fileName,
|
|
226
|
+
formFields,
|
|
227
|
+
endpoint,
|
|
228
|
+
method,
|
|
229
|
+
onProgress,
|
|
230
|
+
skipClamscan,
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Default behavior: Use fetch via sdk._fetch
|
|
218
235
|
let formData, headers;
|
|
219
236
|
|
|
220
237
|
if (isNode) {
|
|
@@ -227,6 +244,15 @@ export class StorageService {
|
|
|
227
244
|
headers = result.headers;
|
|
228
245
|
}
|
|
229
246
|
|
|
247
|
+
if (process?.env?.AUTH_V3_TOKEN_TYPE_OVERRIDE) {
|
|
248
|
+
headers['x-token-type-override'] =
|
|
249
|
+
process.env.AUTH_V3_TOKEN_TYPE_OVERRIDE;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (skipClamscan && process?.env?.CLAMSCAN_OVERRIDE_KEY) {
|
|
253
|
+
headers['x-clamscan-override-key'] = process.env.CLAMSCAN_OVERRIDE_KEY;
|
|
254
|
+
}
|
|
255
|
+
|
|
230
256
|
const params = {
|
|
231
257
|
body: formData,
|
|
232
258
|
headers,
|
|
@@ -235,6 +261,123 @@ export class StorageService {
|
|
|
235
261
|
return await this.sdk._fetch(endpoint, method, params, true);
|
|
236
262
|
}
|
|
237
263
|
|
|
264
|
+
// Upload with progress tracking using XMLHttpRequest
|
|
265
|
+
async _performUploadWithProgress(
|
|
266
|
+
file,
|
|
267
|
+
fileName,
|
|
268
|
+
formFields,
|
|
269
|
+
endpoint,
|
|
270
|
+
method,
|
|
271
|
+
onProgress,
|
|
272
|
+
skipClamscan = false,
|
|
273
|
+
) {
|
|
274
|
+
const { formData } = this._createBrowserFormData(
|
|
275
|
+
file,
|
|
276
|
+
fileName,
|
|
277
|
+
formFields,
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
return new Promise((resolve, reject) => {
|
|
281
|
+
const xhr = new XMLHttpRequest();
|
|
282
|
+
|
|
283
|
+
const startTime = Date.now();
|
|
284
|
+
|
|
285
|
+
// Progress tracking
|
|
286
|
+
xhr.upload.onprogress = (event) => {
|
|
287
|
+
if (event.lengthComputable) {
|
|
288
|
+
const percentComplete = (event.loaded / event.total) * 100;
|
|
289
|
+
const elapsed = (Date.now() - startTime) / 1000; // seconds
|
|
290
|
+
const speed = event.loaded / elapsed; // bytes per second
|
|
291
|
+
|
|
292
|
+
onProgress({
|
|
293
|
+
loaded: event.loaded,
|
|
294
|
+
total: event.total,
|
|
295
|
+
percentage: percentComplete,
|
|
296
|
+
speed: speed, // bytes/sec
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
xhr.onload = () => {
|
|
302
|
+
if (xhr.status >= 200 && xhr.status < 300) {
|
|
303
|
+
try {
|
|
304
|
+
const response = JSON.parse(xhr.responseText);
|
|
305
|
+
resolve(response);
|
|
306
|
+
} catch (e) {
|
|
307
|
+
reject(new Error('Invalid JSON response'));
|
|
308
|
+
}
|
|
309
|
+
} else {
|
|
310
|
+
try {
|
|
311
|
+
const errorResponse = JSON.parse(xhr.responseText);
|
|
312
|
+
reject(errorResponse);
|
|
313
|
+
} catch (e) {
|
|
314
|
+
reject(new Error(`Upload failed with status ${xhr.status}`));
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
xhr.onerror = () => reject(new Error('Network error during upload'));
|
|
320
|
+
xhr.onabort = () => reject(new Error('Upload aborted'));
|
|
321
|
+
|
|
322
|
+
// Build URL with auth headers
|
|
323
|
+
const url = `${this.sdk.fullUrl}${endpoint}`;
|
|
324
|
+
xhr.open(method, url, true);
|
|
325
|
+
|
|
326
|
+
// IMPORTANT: Include credentials (cookies) for authentication
|
|
327
|
+
xhr.withCredentials = true;
|
|
328
|
+
|
|
329
|
+
// Add auth headers
|
|
330
|
+
if (this.sdk.token) {
|
|
331
|
+
xhr.setRequestHeader('Authorization', `Bearer ${this.sdk.token}`);
|
|
332
|
+
}
|
|
333
|
+
if (this.sdk.fwRequestId) {
|
|
334
|
+
xhr.setRequestHeader('x-request-id-fw', this.sdk.fwRequestId);
|
|
335
|
+
}
|
|
336
|
+
if (this.sdk.callId) {
|
|
337
|
+
xhr.setRequestHeader('x-call-id', this.sdk.callId);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Add environment variable override headers
|
|
341
|
+
if (process?.env?.AUTH_V3_TOKEN_TYPE_OVERRIDE) {
|
|
342
|
+
xhr.setRequestHeader(
|
|
343
|
+
'x-token-type-override',
|
|
344
|
+
process.env.AUTH_V3_TOKEN_TYPE_OVERRIDE,
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
if (skipClamscan && process?.env?.CLAMSCAN_OVERRIDE_KEY) {
|
|
348
|
+
xhr.setRequestHeader(
|
|
349
|
+
'x-clamscan-override-key',
|
|
350
|
+
process.env.CLAMSCAN_OVERRIDE_KEY,
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
xhr.send(formData);
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
/*
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
Response:
|
|
361
|
+
{
|
|
362
|
+
"uploaded": [
|
|
363
|
+
{
|
|
364
|
+
"id": "017d0120251229hvdxjod4486582468133095",
|
|
365
|
+
"fileName": "sip-messages-20251223T195443.txt",
|
|
366
|
+
"fileSize": 18979,
|
|
367
|
+
"url": "https://masterc.api.dev-d01.app1svc.com/storage/017d0120251229hvdxjod4486582468133095.txt",
|
|
368
|
+
"mimeType": "text/plain",
|
|
369
|
+
"s3Regions": [
|
|
370
|
+
"d01",
|
|
371
|
+
"d03"
|
|
372
|
+
],
|
|
373
|
+
"isPublic": false
|
|
374
|
+
}
|
|
375
|
+
],
|
|
376
|
+
"viruses": [],
|
|
377
|
+
"errors": []
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
*/
|
|
238
381
|
async upload({
|
|
239
382
|
classification = 'generic',
|
|
240
383
|
folder,
|
|
@@ -246,6 +389,8 @@ export class StorageService {
|
|
|
246
389
|
relatedId,
|
|
247
390
|
createAccessKey = false,
|
|
248
391
|
accessKeyExpiresIn,
|
|
392
|
+
onProgress,
|
|
393
|
+
_options,
|
|
249
394
|
}) {
|
|
250
395
|
this.sdk.validateParams(
|
|
251
396
|
{
|
|
@@ -270,7 +415,7 @@ export class StorageService {
|
|
|
270
415
|
expireAfter: { type: 'string', required: false },
|
|
271
416
|
relatedId: { type: 'string', required: false },
|
|
272
417
|
createAccessKey: { type: 'boolean', required: false },
|
|
273
|
-
accessKeyExpiresIn: { type: '
|
|
418
|
+
accessKeyExpiresIn: { type: 'number', required: false },
|
|
274
419
|
},
|
|
275
420
|
);
|
|
276
421
|
|
|
@@ -288,11 +433,20 @@ export class StorageService {
|
|
|
288
433
|
if (accessKeyExpiresIn)
|
|
289
434
|
formFields.push(['accessKeyExpiresIn', accessKeyExpiresIn]);
|
|
290
435
|
|
|
291
|
-
return this._performUpload(
|
|
436
|
+
return this._performUpload(
|
|
437
|
+
file,
|
|
438
|
+
fileName,
|
|
439
|
+
formFields,
|
|
440
|
+
'/storage/upload',
|
|
441
|
+
'POST',
|
|
442
|
+
onProgress,
|
|
443
|
+
_options?.skipScan,
|
|
444
|
+
);
|
|
292
445
|
}
|
|
293
446
|
|
|
294
447
|
async uploadFiles(files, options = {}) {
|
|
295
|
-
const { classification, expireAfter, isPublic, metadata } =
|
|
448
|
+
const { classification, expireAfter, isPublic, metadata, _options } =
|
|
449
|
+
options;
|
|
296
450
|
|
|
297
451
|
// Validate files parameter
|
|
298
452
|
if (!files) {
|
|
@@ -392,6 +546,10 @@ export class StorageService {
|
|
|
392
546
|
if (metadata) formData.append('metadata', JSON.stringify(metadata));
|
|
393
547
|
}
|
|
394
548
|
|
|
549
|
+
if (_options?.skipScan && process?.env?.CLAMSCAN_OVERRIDE_KEY) {
|
|
550
|
+
headers['x-clamscan-override-key'] = process.env.CLAMSCAN_OVERRIDE_KEY;
|
|
551
|
+
}
|
|
552
|
+
|
|
395
553
|
const params = {
|
|
396
554
|
body: formData,
|
|
397
555
|
headers,
|
|
@@ -467,13 +625,19 @@ export class StorageService {
|
|
|
467
625
|
return result;
|
|
468
626
|
}
|
|
469
627
|
|
|
470
|
-
async uploadProfileImage({
|
|
628
|
+
async uploadProfileImage({
|
|
629
|
+
file,
|
|
630
|
+
classification = 'user_images',
|
|
631
|
+
fileName,
|
|
632
|
+
userId,
|
|
633
|
+
}) {
|
|
471
634
|
this.sdk.validateParams(
|
|
472
|
-
{ file, classification },
|
|
635
|
+
{ file, classification, userId },
|
|
473
636
|
{
|
|
474
637
|
file: { type: 'object', required: true },
|
|
475
638
|
classification: { type: 'string', required: true },
|
|
476
639
|
fileName: { type: 'string', required: false },
|
|
640
|
+
userId: { type: 'string', required: false },
|
|
477
641
|
},
|
|
478
642
|
);
|
|
479
643
|
|
|
@@ -488,6 +652,7 @@ export class StorageService {
|
|
|
488
652
|
// Build form fields exactly like the regular upload but only include classification
|
|
489
653
|
const formFields = [];
|
|
490
654
|
formFields.push(['classification', classification]);
|
|
655
|
+
formFields.push(['userId', userId]);
|
|
491
656
|
|
|
492
657
|
// Use the correct profile image endpoint with proper FormData
|
|
493
658
|
return this._performUpload(
|
|
@@ -535,12 +700,14 @@ export class StorageService {
|
|
|
535
700
|
}
|
|
536
701
|
|
|
537
702
|
async listFiles(options = {}) {
|
|
538
|
-
const { classification, limit, offset, orderBy, orderDirection } =
|
|
703
|
+
const { classification, folder, limit, offset, orderBy, orderDirection } =
|
|
704
|
+
options;
|
|
539
705
|
|
|
540
706
|
// Validate optional parameters
|
|
541
707
|
const validationSchema = {};
|
|
542
708
|
if ('classification' in options)
|
|
543
709
|
validationSchema.classification = { type: 'string' };
|
|
710
|
+
if ('folder' in options) validationSchema.folder = { type: 'string' };
|
|
544
711
|
if ('limit' in options) validationSchema.limit = { type: 'number' };
|
|
545
712
|
if ('offset' in options) validationSchema.offset = { type: 'number' };
|
|
546
713
|
if ('orderBy' in options) validationSchema.orderBy = { type: 'string' };
|
|
@@ -749,6 +916,7 @@ export class StorageService {
|
|
|
749
916
|
country,
|
|
750
917
|
expireAfter,
|
|
751
918
|
relatedId,
|
|
919
|
+
_options,
|
|
752
920
|
},
|
|
753
921
|
) {
|
|
754
922
|
this.sdk.validateParams(
|
|
@@ -784,6 +952,8 @@ export class StorageService {
|
|
|
784
952
|
formFields,
|
|
785
953
|
`/storage/${storageId}`,
|
|
786
954
|
'PUT',
|
|
955
|
+
null,
|
|
956
|
+
_options?.skipScan,
|
|
787
957
|
);
|
|
788
958
|
} else {
|
|
789
959
|
// If only updating metadata, use JSON request
|