@ethora/sdk-backend 26.1.1 → 26.2.2
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 +349 -111
- package/dist/repositories/EthoraSDKService.d.ts +30 -18
- package/dist/repositories/EthoraSDKService.d.ts.map +1 -1
- package/dist/repositories/EthoraSDKService.js +159 -84
- package/dist/repositories/EthoraSDKService.js.map +1 -1
- package/dist/scripts/test-logs.d.ts +2 -0
- package/dist/scripts/test-logs.d.ts.map +1 -0
- package/dist/scripts/test-logs.js +93 -0
- package/dist/scripts/test-logs.js.map +1 -0
- package/dist/types/index.d.ts +14 -7
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -79,7 +79,7 @@ npm install dotenv
|
|
|
79
79
|
In your main application file (e.g., `app.js`, `server.js`, or `index.ts`):
|
|
80
80
|
|
|
81
81
|
```typescript
|
|
82
|
-
import dotenv from
|
|
82
|
+
import dotenv from 'dotenv';
|
|
83
83
|
|
|
84
84
|
// Load environment variables
|
|
85
85
|
dotenv.config();
|
|
@@ -90,7 +90,7 @@ dotenv.config();
|
|
|
90
90
|
### Step 1: Import the SDK
|
|
91
91
|
|
|
92
92
|
```typescript
|
|
93
|
-
import { getEthoraSDKService } from
|
|
93
|
+
import { getEthoraSDKService } from '@ethora/sdk-backend';
|
|
94
94
|
```
|
|
95
95
|
|
|
96
96
|
### Step 2: Initialize the Service
|
|
@@ -101,7 +101,7 @@ You can initialize the service in several ways:
|
|
|
101
101
|
|
|
102
102
|
```typescript
|
|
103
103
|
// services/chatService.ts
|
|
104
|
-
import { getEthoraSDKService } from
|
|
104
|
+
import { getEthoraSDKService } from '@ethora/sdk-backend';
|
|
105
105
|
|
|
106
106
|
// Get the singleton instance
|
|
107
107
|
const chatService = getEthoraSDKService();
|
|
@@ -113,7 +113,7 @@ export default chatService;
|
|
|
113
113
|
|
|
114
114
|
```typescript
|
|
115
115
|
// In your route handler or service
|
|
116
|
-
import { getEthoraSDKService } from
|
|
116
|
+
import { getEthoraSDKService } from '@ethora/sdk-backend';
|
|
117
117
|
|
|
118
118
|
const chatService = getEthoraSDKService();
|
|
119
119
|
```
|
|
@@ -122,8 +122,8 @@ const chatService = getEthoraSDKService();
|
|
|
122
122
|
|
|
123
123
|
```typescript
|
|
124
124
|
// chat.service.ts
|
|
125
|
-
import { Injectable } from
|
|
126
|
-
import { getEthoraSDKService } from
|
|
125
|
+
import { Injectable } from '@nestjs/common';
|
|
126
|
+
import { getEthoraSDKService } from '@ethora/sdk-backend';
|
|
127
127
|
|
|
128
128
|
@Injectable()
|
|
129
129
|
export class ChatService {
|
|
@@ -139,16 +139,16 @@ export class ChatService {
|
|
|
139
139
|
|
|
140
140
|
```typescript
|
|
141
141
|
// routes/chat.ts
|
|
142
|
-
import express, { Request, Response } from
|
|
143
|
-
import { getEthoraSDKService } from
|
|
144
|
-
import axios from
|
|
142
|
+
import express, { Request, Response } from 'express';
|
|
143
|
+
import { getEthoraSDKService } from '@ethora/sdk-backend';
|
|
144
|
+
import axios from 'axios';
|
|
145
145
|
|
|
146
146
|
const router = express.Router();
|
|
147
147
|
const chatService = getEthoraSDKService();
|
|
148
148
|
|
|
149
149
|
// Create a chat room for a workspace
|
|
150
150
|
router.post(
|
|
151
|
-
|
|
151
|
+
'/workspaces/:workspaceId/chat',
|
|
152
152
|
async (req: Request, res: Response) => {
|
|
153
153
|
try {
|
|
154
154
|
const { workspaceId } = req.params;
|
|
@@ -157,7 +157,7 @@ router.post(
|
|
|
157
157
|
const response = await chatService.createChatRoom(workspaceId, {
|
|
158
158
|
title: roomData.title || `Chat Room ${workspaceId}`,
|
|
159
159
|
uuid: workspaceId,
|
|
160
|
-
type: roomData.type ||
|
|
160
|
+
type: roomData.type || 'group',
|
|
161
161
|
...roomData,
|
|
162
162
|
});
|
|
163
163
|
|
|
@@ -165,18 +165,18 @@ router.post(
|
|
|
165
165
|
} catch (error) {
|
|
166
166
|
if (axios.isAxiosError(error)) {
|
|
167
167
|
res.status(error.response?.status || 500).json({
|
|
168
|
-
error:
|
|
168
|
+
error: 'Failed to create chat room',
|
|
169
169
|
details: error.response?.data,
|
|
170
170
|
});
|
|
171
171
|
} else {
|
|
172
|
-
res.status(500).json({ error:
|
|
172
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
173
173
|
}
|
|
174
174
|
}
|
|
175
|
-
}
|
|
175
|
+
},
|
|
176
176
|
);
|
|
177
177
|
|
|
178
178
|
// Create a user
|
|
179
|
-
router.post(
|
|
179
|
+
router.post('/users/:userId', async (req: Request, res: Response) => {
|
|
180
180
|
try {
|
|
181
181
|
const { userId } = req.params;
|
|
182
182
|
const userData = req.body;
|
|
@@ -186,50 +186,72 @@ router.post("/users/:userId", async (req: Request, res: Response) => {
|
|
|
186
186
|
} catch (error) {
|
|
187
187
|
if (axios.isAxiosError(error)) {
|
|
188
188
|
res.status(error.response?.status || 500).json({
|
|
189
|
-
error:
|
|
189
|
+
error: 'Failed to create user',
|
|
190
190
|
details: error.response?.data,
|
|
191
191
|
});
|
|
192
192
|
} else {
|
|
193
|
-
res.status(500).json({ error:
|
|
193
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
194
194
|
}
|
|
195
195
|
}
|
|
196
196
|
});
|
|
197
197
|
|
|
198
198
|
// Grant user access to chat room
|
|
199
199
|
router.post(
|
|
200
|
-
|
|
200
|
+
'/workspaces/:workspaceId/chat/users/:userId',
|
|
201
201
|
async (req: Request, res: Response) => {
|
|
202
202
|
try {
|
|
203
203
|
const { workspaceId, userId } = req.params;
|
|
204
204
|
|
|
205
205
|
await chatService.grantUserAccessToChatRoom(workspaceId, userId);
|
|
206
|
-
res.json({ success: true, message:
|
|
206
|
+
res.json({ success: true, message: 'Access granted' });
|
|
207
207
|
} catch (error) {
|
|
208
208
|
if (axios.isAxiosError(error)) {
|
|
209
209
|
res.status(error.response?.status || 500).json({
|
|
210
|
-
error:
|
|
210
|
+
error: 'Failed to grant access',
|
|
211
211
|
details: error.response?.data,
|
|
212
212
|
});
|
|
213
213
|
} else {
|
|
214
|
-
res.status(500).json({ error:
|
|
214
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
215
215
|
}
|
|
216
216
|
}
|
|
217
|
-
}
|
|
217
|
+
},
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
// Remove user access from chat room
|
|
221
|
+
router.delete(
|
|
222
|
+
'/workspaces/:workspaceId/chat/users/:userId',
|
|
223
|
+
async (req: Request, res: Response) => {
|
|
224
|
+
try {
|
|
225
|
+
const { workspaceId, userId } = req.params;
|
|
226
|
+
|
|
227
|
+
await chatService.removeUserAccessFromChatRoom(workspaceId, userId);
|
|
228
|
+
res.json({ success: true, message: 'Access removed' });
|
|
229
|
+
} catch (error) {
|
|
230
|
+
if (axios.isAxiosError(error)) {
|
|
231
|
+
res.status(error.response?.status || 500).json({
|
|
232
|
+
error: 'Failed to remove access',
|
|
233
|
+
details: error.response?.data,
|
|
234
|
+
});
|
|
235
|
+
} else {
|
|
236
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
},
|
|
218
240
|
);
|
|
219
241
|
|
|
220
242
|
// Generate client JWT token
|
|
221
|
-
router.get(
|
|
243
|
+
router.get('/users/:userId/chat-token', (req: Request, res: Response) => {
|
|
222
244
|
try {
|
|
223
245
|
const { userId } = req.params;
|
|
224
246
|
const token = chatService.createChatUserJwtToken(userId);
|
|
225
247
|
res.json({ token });
|
|
226
248
|
} catch (error) {
|
|
227
|
-
res.status(500).json({ error:
|
|
249
|
+
res.status(500).json({ error: 'Failed to generate token' });
|
|
228
250
|
}
|
|
229
251
|
});
|
|
230
252
|
|
|
231
253
|
// Get users
|
|
232
|
-
router.get(
|
|
254
|
+
router.get('/users', async (req: Request, res: Response) => {
|
|
233
255
|
try {
|
|
234
256
|
const { chatName, xmppUsername } = req.query;
|
|
235
257
|
const params: any = {};
|
|
@@ -237,32 +259,32 @@ router.get("/users", async (req: Request, res: Response) => {
|
|
|
237
259
|
if (xmppUsername) params.xmppUsername = String(xmppUsername);
|
|
238
260
|
|
|
239
261
|
const response = await chatService.getUsers(
|
|
240
|
-
Object.keys(params).length > 0 ? params : undefined
|
|
262
|
+
Object.keys(params).length > 0 ? params : undefined,
|
|
241
263
|
);
|
|
242
264
|
res.json({ success: true, data: response });
|
|
243
265
|
} catch (error) {
|
|
244
266
|
if (axios.isAxiosError(error)) {
|
|
245
267
|
res.status(error.response?.status || 500).json({
|
|
246
|
-
error:
|
|
268
|
+
error: 'Failed to get users',
|
|
247
269
|
details: error.response?.data,
|
|
248
270
|
});
|
|
249
271
|
} else {
|
|
250
|
-
res.status(500).json({ error:
|
|
272
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
251
273
|
}
|
|
252
274
|
}
|
|
253
275
|
});
|
|
254
276
|
|
|
255
277
|
// Update users (batch)
|
|
256
|
-
router.patch(
|
|
278
|
+
router.patch('/users', async (req: Request, res: Response) => {
|
|
257
279
|
try {
|
|
258
280
|
const { users } = req.body;
|
|
259
281
|
if (!Array.isArray(users) || users.length === 0) {
|
|
260
|
-
return res.status(400).json({ error:
|
|
282
|
+
return res.status(400).json({ error: 'users must be a non-empty array' });
|
|
261
283
|
}
|
|
262
284
|
if (users.length > 100) {
|
|
263
285
|
return res
|
|
264
286
|
.status(400)
|
|
265
|
-
.json({ error:
|
|
287
|
+
.json({ error: 'Maximum 100 users allowed per request' });
|
|
266
288
|
}
|
|
267
289
|
|
|
268
290
|
const response = await chatService.updateUsers(users);
|
|
@@ -270,11 +292,11 @@ router.patch("/users", async (req: Request, res: Response) => {
|
|
|
270
292
|
} catch (error) {
|
|
271
293
|
if (axios.isAxiosError(error)) {
|
|
272
294
|
res.status(error.response?.status || 500).json({
|
|
273
|
-
error:
|
|
295
|
+
error: 'Failed to update users',
|
|
274
296
|
details: error.response?.data,
|
|
275
297
|
});
|
|
276
298
|
} else {
|
|
277
|
-
res.status(500).json({ error:
|
|
299
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
278
300
|
}
|
|
279
301
|
}
|
|
280
302
|
});
|
|
@@ -286,9 +308,9 @@ export default router;
|
|
|
286
308
|
|
|
287
309
|
```typescript
|
|
288
310
|
// chat/chat.service.ts
|
|
289
|
-
import { Injectable, HttpException, HttpStatus } from
|
|
290
|
-
import { getEthoraSDKService } from
|
|
291
|
-
import axios from
|
|
311
|
+
import { Injectable, HttpException, HttpStatus } from '@nestjs/common';
|
|
312
|
+
import { getEthoraSDKService } from '@ethora/sdk-backend';
|
|
313
|
+
import axios from 'axios';
|
|
292
314
|
|
|
293
315
|
@Injectable()
|
|
294
316
|
export class ChatService {
|
|
@@ -301,10 +323,10 @@ export class ChatService {
|
|
|
301
323
|
if (axios.isAxiosError(error)) {
|
|
302
324
|
throw new HttpException(
|
|
303
325
|
{
|
|
304
|
-
message:
|
|
326
|
+
message: 'Failed to create chat room',
|
|
305
327
|
details: error.response?.data,
|
|
306
328
|
},
|
|
307
|
-
error.response?.status || HttpStatus.INTERNAL_SERVER_ERROR
|
|
329
|
+
error.response?.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
|
308
330
|
);
|
|
309
331
|
}
|
|
310
332
|
throw error;
|
|
@@ -318,10 +340,10 @@ export class ChatService {
|
|
|
318
340
|
if (axios.isAxiosError(error)) {
|
|
319
341
|
throw new HttpException(
|
|
320
342
|
{
|
|
321
|
-
message:
|
|
343
|
+
message: 'Failed to create user',
|
|
322
344
|
details: error.response?.data,
|
|
323
345
|
},
|
|
324
|
-
error.response?.status || HttpStatus.INTERNAL_SERVER_ERROR
|
|
346
|
+
error.response?.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
|
325
347
|
);
|
|
326
348
|
}
|
|
327
349
|
throw error;
|
|
@@ -334,28 +356,28 @@ export class ChatService {
|
|
|
334
356
|
}
|
|
335
357
|
|
|
336
358
|
// chat/chat.controller.ts
|
|
337
|
-
import { Controller, Post, Get, Param, Body } from
|
|
338
|
-
import { ChatService } from
|
|
359
|
+
import { Controller, Post, Get, Param, Body } from '@nestjs/common';
|
|
360
|
+
import { ChatService } from './chat.service';
|
|
339
361
|
|
|
340
|
-
@Controller(
|
|
362
|
+
@Controller('chat')
|
|
341
363
|
export class ChatController {
|
|
342
364
|
constructor(private readonly chatService: ChatService) {}
|
|
343
365
|
|
|
344
|
-
@Post(
|
|
366
|
+
@Post('workspaces/:workspaceId/rooms')
|
|
345
367
|
async createChatRoom(
|
|
346
|
-
@Param(
|
|
347
|
-
@Body() roomData: any
|
|
368
|
+
@Param('workspaceId') workspaceId: string,
|
|
369
|
+
@Body() roomData: any,
|
|
348
370
|
) {
|
|
349
371
|
return this.chatService.createChatRoom(workspaceId, roomData);
|
|
350
372
|
}
|
|
351
373
|
|
|
352
|
-
@Post(
|
|
353
|
-
async createUser(@Param(
|
|
374
|
+
@Post('users/:userId')
|
|
375
|
+
async createUser(@Param('userId') userId: string, @Body() userData: any) {
|
|
354
376
|
return this.chatService.createUser(userId, userData);
|
|
355
377
|
}
|
|
356
378
|
|
|
357
|
-
@Get(
|
|
358
|
-
getClientToken(@Param(
|
|
379
|
+
@Get('users/:userId/token')
|
|
380
|
+
getClientToken(@Param('userId') userId: string) {
|
|
359
381
|
return { token: this.chatService.generateClientToken(userId) };
|
|
360
382
|
}
|
|
361
383
|
}
|
|
@@ -365,15 +387,15 @@ export class ChatController {
|
|
|
365
387
|
|
|
366
388
|
```typescript
|
|
367
389
|
// routes/chat.ts
|
|
368
|
-
import { FastifyInstance, FastifyRequest, FastifyReply } from
|
|
369
|
-
import { getEthoraSDKService } from
|
|
390
|
+
import { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
|
|
391
|
+
import { getEthoraSDKService } from '@ethora/sdk-backend';
|
|
370
392
|
|
|
371
393
|
const chatService = getEthoraSDKService();
|
|
372
394
|
|
|
373
395
|
export async function chatRoutes(fastify: FastifyInstance) {
|
|
374
396
|
// Create chat room
|
|
375
397
|
fastify.post(
|
|
376
|
-
|
|
398
|
+
'/workspaces/:workspaceId/chat',
|
|
377
399
|
async (request: FastifyRequest, reply: FastifyReply) => {
|
|
378
400
|
const { workspaceId } = request.params as { workspaceId: string };
|
|
379
401
|
const roomData = request.body as any;
|
|
@@ -381,23 +403,23 @@ export async function chatRoutes(fastify: FastifyInstance) {
|
|
|
381
403
|
try {
|
|
382
404
|
const response = await chatService.createChatRoom(
|
|
383
405
|
workspaceId,
|
|
384
|
-
roomData
|
|
406
|
+
roomData,
|
|
385
407
|
);
|
|
386
408
|
return { success: true, data: response };
|
|
387
409
|
} catch (error) {
|
|
388
|
-
reply.code(500).send({ error:
|
|
410
|
+
reply.code(500).send({ error: 'Failed to create chat room' });
|
|
389
411
|
}
|
|
390
|
-
}
|
|
412
|
+
},
|
|
391
413
|
);
|
|
392
414
|
|
|
393
415
|
// Generate client token
|
|
394
416
|
fastify.get(
|
|
395
|
-
|
|
417
|
+
'/users/:userId/chat-token',
|
|
396
418
|
async (request: FastifyRequest, reply: FastifyReply) => {
|
|
397
419
|
const { userId } = request.params as { userId: string };
|
|
398
420
|
const token = chatService.createChatUserJwtToken(userId);
|
|
399
421
|
return { token };
|
|
400
|
-
}
|
|
422
|
+
},
|
|
401
423
|
);
|
|
402
424
|
}
|
|
403
425
|
```
|
|
@@ -412,7 +434,7 @@ When creating a new workspace, set up the chat room and initial users:
|
|
|
412
434
|
async function setupWorkspaceChat(
|
|
413
435
|
workspaceId: string,
|
|
414
436
|
userIds: string[],
|
|
415
|
-
adminUserId: string
|
|
437
|
+
adminUserId: string,
|
|
416
438
|
) {
|
|
417
439
|
const chatService = getEthoraSDKService();
|
|
418
440
|
|
|
@@ -421,15 +443,15 @@ async function setupWorkspaceChat(
|
|
|
421
443
|
await chatService.createChatRoom(workspaceId, {
|
|
422
444
|
title: `Workspace ${workspaceId}`,
|
|
423
445
|
uuid: workspaceId,
|
|
424
|
-
type:
|
|
446
|
+
type: 'group',
|
|
425
447
|
});
|
|
426
448
|
|
|
427
449
|
// 2. Create users (if they don't exist)
|
|
428
450
|
for (const userId of userIds) {
|
|
429
451
|
try {
|
|
430
452
|
await chatService.createUser(userId, {
|
|
431
|
-
firstName:
|
|
432
|
-
lastName:
|
|
453
|
+
firstName: 'User',
|
|
454
|
+
lastName: 'Name',
|
|
433
455
|
});
|
|
434
456
|
} catch (error) {
|
|
435
457
|
// User might already exist, continue
|
|
@@ -444,12 +466,12 @@ async function setupWorkspaceChat(
|
|
|
444
466
|
try {
|
|
445
467
|
await chatService.grantChatbotAccessToChatRoom(workspaceId);
|
|
446
468
|
} catch (error) {
|
|
447
|
-
console.warn(
|
|
469
|
+
console.warn('Chatbot access not configured or failed');
|
|
448
470
|
}
|
|
449
471
|
|
|
450
472
|
return { success: true };
|
|
451
473
|
} catch (error) {
|
|
452
|
-
console.error(
|
|
474
|
+
console.error('Failed to setup workspace chat:', error);
|
|
453
475
|
throw error;
|
|
454
476
|
}
|
|
455
477
|
}
|
|
@@ -462,7 +484,7 @@ When a new user joins your platform:
|
|
|
462
484
|
```typescript
|
|
463
485
|
async function onboardNewUser(
|
|
464
486
|
userId: string,
|
|
465
|
-
userData: { firstName: string; lastName: string; email: string }
|
|
487
|
+
userData: { firstName: string; lastName: string; email: string },
|
|
466
488
|
) {
|
|
467
489
|
const chatService = getEthoraSDKService();
|
|
468
490
|
|
|
@@ -483,7 +505,7 @@ async function onboardNewUser(
|
|
|
483
505
|
chatToken: clientToken,
|
|
484
506
|
};
|
|
485
507
|
} catch (error) {
|
|
486
|
-
console.error(
|
|
508
|
+
console.error('Failed to onboard user:', error);
|
|
487
509
|
throw error;
|
|
488
510
|
}
|
|
489
511
|
}
|
|
@@ -510,13 +532,50 @@ async function addUserToWorkspace(workspaceId: string, userId: string) {
|
|
|
510
532
|
|
|
511
533
|
return { success: true };
|
|
512
534
|
} catch (error) {
|
|
513
|
-
console.error(
|
|
535
|
+
console.error('Failed to add user to workspace:', error);
|
|
536
|
+
throw error;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
### Use Case 4: Removing User from Workspace
|
|
542
|
+
|
|
543
|
+
When removing a user from a workspace:
|
|
544
|
+
|
|
545
|
+
```typescript
|
|
546
|
+
async function removeUserFromWorkspace(workspaceId: string, userId: string) {
|
|
547
|
+
const chatService = getEthoraSDKService();
|
|
548
|
+
|
|
549
|
+
try {
|
|
550
|
+
// Remove access from workspace chat room
|
|
551
|
+
await chatService.removeUserAccessFromChatRoom(workspaceId, userId);
|
|
552
|
+
|
|
553
|
+
return { success: true };
|
|
554
|
+
} catch (error) {
|
|
555
|
+
console.error('Failed to remove user from workspace:', error);
|
|
556
|
+
throw error;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// Remove multiple users at once
|
|
561
|
+
async function removeMultipleUsersFromWorkspace(
|
|
562
|
+
workspaceId: string,
|
|
563
|
+
userIds: string[],
|
|
564
|
+
) {
|
|
565
|
+
const chatService = getEthoraSDKService();
|
|
566
|
+
|
|
567
|
+
try {
|
|
568
|
+
await chatService.removeUserAccessFromChatRoom(workspaceId, userIds);
|
|
569
|
+
return { success: true };
|
|
570
|
+
} catch (error) {
|
|
571
|
+
console.error('Failed to remove users from workspace:', error);
|
|
514
572
|
throw error;
|
|
515
573
|
}
|
|
516
574
|
}
|
|
517
575
|
```
|
|
518
576
|
|
|
519
|
-
### Use Case
|
|
577
|
+
### Use Case 5: Cleanup on Workspace Deletion
|
|
578
|
+
|
|
520
579
|
|
|
521
580
|
When deleting a workspace:
|
|
522
581
|
|
|
@@ -533,19 +592,19 @@ async function cleanupWorkspaceChat(workspaceId: string, userIds: string[]) {
|
|
|
533
592
|
try {
|
|
534
593
|
await chatService.deleteUsers(userIds);
|
|
535
594
|
} catch (error) {
|
|
536
|
-
console.warn(
|
|
595
|
+
console.warn('Some users might not exist:', error);
|
|
537
596
|
}
|
|
538
597
|
}
|
|
539
598
|
|
|
540
599
|
return { success: true };
|
|
541
600
|
} catch (error) {
|
|
542
|
-
console.error(
|
|
601
|
+
console.error('Failed to cleanup workspace chat:', error);
|
|
543
602
|
throw error;
|
|
544
603
|
}
|
|
545
604
|
}
|
|
546
605
|
```
|
|
547
606
|
|
|
548
|
-
### Use Case
|
|
607
|
+
### Use Case 6: Getting Users
|
|
549
608
|
|
|
550
609
|
Retrieve users from the chat service:
|
|
551
610
|
|
|
@@ -560,23 +619,23 @@ async function getUsersExample() {
|
|
|
560
619
|
|
|
561
620
|
// Get users by chat name (group chat)
|
|
562
621
|
const groupChatUsers = await chatService.getUsers({
|
|
563
|
-
chatName:
|
|
622
|
+
chatName: 'appId_workspaceId',
|
|
564
623
|
});
|
|
565
624
|
|
|
566
625
|
// Get users by chat name (1-on-1 chat)
|
|
567
626
|
const oneOnOneUsers = await chatService.getUsers({
|
|
568
|
-
chatName:
|
|
627
|
+
chatName: 'userA-userB',
|
|
569
628
|
});
|
|
570
629
|
|
|
571
630
|
return { allUsers, groupChatUsers, oneOnOneUsers };
|
|
572
631
|
} catch (error) {
|
|
573
|
-
console.error(
|
|
632
|
+
console.error('Failed to get users:', error);
|
|
574
633
|
throw error;
|
|
575
634
|
}
|
|
576
635
|
}
|
|
577
636
|
```
|
|
578
637
|
|
|
579
|
-
### Use Case
|
|
638
|
+
### Use Case 7: Updating Users (Batch)
|
|
580
639
|
|
|
581
640
|
Update multiple users at once:
|
|
582
641
|
|
|
@@ -588,48 +647,224 @@ async function updateUsersExample() {
|
|
|
588
647
|
// Update multiple users (1-100 users per request)
|
|
589
648
|
const response = await chatService.updateUsers([
|
|
590
649
|
{
|
|
591
|
-
xmppUsername:
|
|
592
|
-
firstName:
|
|
593
|
-
lastName:
|
|
594
|
-
username:
|
|
595
|
-
profileImage:
|
|
650
|
+
xmppUsername: 'appId_user1',
|
|
651
|
+
firstName: 'John',
|
|
652
|
+
lastName: 'Doe',
|
|
653
|
+
username: 'johndoe',
|
|
654
|
+
profileImage: 'https://example.com/avatar1.jpg',
|
|
596
655
|
},
|
|
597
656
|
{
|
|
598
|
-
xmppUsername:
|
|
599
|
-
firstName:
|
|
600
|
-
lastName:
|
|
601
|
-
username:
|
|
657
|
+
xmppUsername: 'appId_user2',
|
|
658
|
+
firstName: 'Jane',
|
|
659
|
+
lastName: 'Smith',
|
|
660
|
+
username: 'janesmith',
|
|
602
661
|
},
|
|
603
662
|
]);
|
|
604
663
|
|
|
605
664
|
// Check results
|
|
606
665
|
response.results?.forEach((result: any) => {
|
|
607
|
-
if (result.status ===
|
|
666
|
+
if (result.status === 'updated') {
|
|
608
667
|
console.log(`User ${result.xmppUsername} updated successfully`);
|
|
609
|
-
} else if (result.status ===
|
|
668
|
+
} else if (result.status === 'not-found') {
|
|
610
669
|
console.warn(`User ${result.xmppUsername} not found`);
|
|
611
|
-
} else if (result.status ===
|
|
670
|
+
} else if (result.status === 'skipped') {
|
|
612
671
|
console.log(`User ${result.xmppUsername} update skipped`);
|
|
613
672
|
}
|
|
614
673
|
});
|
|
615
674
|
|
|
616
675
|
return response;
|
|
617
676
|
} catch (error) {
|
|
618
|
-
console.error(
|
|
677
|
+
console.error('Failed to update users:', error);
|
|
619
678
|
throw error;
|
|
620
679
|
}
|
|
621
680
|
}
|
|
622
681
|
```
|
|
623
682
|
|
|
683
|
+
## API Reference
|
|
684
|
+
|
|
685
|
+
### Core Methods
|
|
686
|
+
|
|
687
|
+
#### `createUser(userId: UUID, userData?: Record<string, unknown>): Promise<ApiResponse>`
|
|
688
|
+
|
|
689
|
+
Creates a user in the chat service using the `/v2/users/batch` endpoint.
|
|
690
|
+
|
|
691
|
+
**Parameters:**
|
|
692
|
+
- `userId` (UUID): The unique identifier of the user
|
|
693
|
+
- `userData` (optional): Additional user data
|
|
694
|
+
- `firstName` (string): User's first name
|
|
695
|
+
- `lastName` (string): User's last name (minimum 2 characters)
|
|
696
|
+
- `email` (string): User's email address
|
|
697
|
+
- `password` (string): User's password
|
|
698
|
+
- `displayName` (string): Display name (will be split into firstName/lastName if needed)
|
|
699
|
+
|
|
700
|
+
**Returns:** Promise resolving to the API response
|
|
701
|
+
|
|
702
|
+
**Note:** The API requires `lastName` to be at least 2 characters. If not provided or too short, defaults to "User".
|
|
703
|
+
|
|
704
|
+
---
|
|
705
|
+
|
|
706
|
+
#### `createChatRoom(chatId: UUID, roomData?: Record<string, unknown>): Promise<ApiResponse>`
|
|
707
|
+
|
|
708
|
+
Creates a chat room using the `/v2/chats` endpoint.
|
|
709
|
+
|
|
710
|
+
**Parameters:**
|
|
711
|
+
- `chatId` (UUID): The unique identifier of the chat/workspace
|
|
712
|
+
- `roomData` (optional): Room configuration
|
|
713
|
+
- `title` (string): Chat room title
|
|
714
|
+
- `uuid` (string): Room UUID (defaults to chatId)
|
|
715
|
+
- `type` (string): Room type (defaults to "group")
|
|
716
|
+
|
|
717
|
+
**Returns:** Promise resolving to the API response
|
|
718
|
+
|
|
719
|
+
---
|
|
720
|
+
|
|
721
|
+
#### `grantUserAccessToChatRoom(chatId: UUID, userId: UUID | UUID[]): Promise<ApiResponse>`
|
|
722
|
+
|
|
723
|
+
Grants user(s) access to a chat room using the `/v2/chats/users-access` endpoint.
|
|
724
|
+
|
|
725
|
+
**Parameters:**
|
|
726
|
+
- `chatId` (UUID): The unique identifier of the chat/workspace
|
|
727
|
+
- `userId` (UUID | UUID[]): Single user ID or array of user IDs
|
|
728
|
+
|
|
729
|
+
**Returns:** Promise resolving to the API response
|
|
730
|
+
|
|
731
|
+
**Note:** User IDs are automatically prefixed with `{appId}_` if they don't already have the prefix.
|
|
732
|
+
|
|
733
|
+
---
|
|
734
|
+
|
|
735
|
+
#### `removeUserAccessFromChatRoom(chatId: UUID, userId: UUID | UUID[]): Promise<ApiResponse>`
|
|
736
|
+
|
|
737
|
+
Removes user(s) access from a chat room using the `/v2/chats/users-access` DELETE endpoint.
|
|
738
|
+
|
|
739
|
+
**Parameters:**
|
|
740
|
+
- `chatId` (UUID): The unique identifier of the chat/workspace
|
|
741
|
+
- `userId` (UUID | UUID[]): Single user ID or array of user IDs to remove
|
|
742
|
+
|
|
743
|
+
**Returns:** Promise resolving to the API response
|
|
744
|
+
|
|
745
|
+
**Note:** User IDs are automatically prefixed with `{appId}_` if they don't already have the prefix.
|
|
746
|
+
|
|
747
|
+
---
|
|
748
|
+
|
|
749
|
+
#### `grantChatbotAccessToChatRoom(chatId: UUID): Promise<ApiResponse>`
|
|
750
|
+
|
|
751
|
+
Grants chatbot access to a chat room.
|
|
752
|
+
|
|
753
|
+
**Parameters:**
|
|
754
|
+
- `chatId` (UUID): The unique identifier of the chat/workspace
|
|
755
|
+
|
|
756
|
+
**Returns:** Promise resolving to the API response
|
|
757
|
+
|
|
758
|
+
**Requires:** `ETHORA_CHAT_BOT_JID` environment variable to be set
|
|
759
|
+
|
|
760
|
+
---
|
|
761
|
+
|
|
762
|
+
#### `getUsers(params?: GetUsersQueryParams): Promise<ApiResponse>`
|
|
763
|
+
|
|
764
|
+
Retrieves users from the chat service using the `/v2/chats/users` endpoint.
|
|
765
|
+
|
|
766
|
+
**Parameters:**
|
|
767
|
+
- `params` (GetUsersQueryParams, optional): Query parameters
|
|
768
|
+
- `chatName` (string): Filter by chat name
|
|
769
|
+
- Group chats: `appId_chatId` format
|
|
770
|
+
- 1-on-1 chats: `xmppUsernameA-xmppUsernameB` format
|
|
771
|
+
- `xmppUsername` (string): Filter by specific XMPP username
|
|
772
|
+
|
|
773
|
+
**Query Modes:**
|
|
774
|
+
- No parameters: Returns all users of the app
|
|
775
|
+
- With `chatName`: Returns all users of the specified chat
|
|
776
|
+
- With `xmppUsername`: Returns a specific user
|
|
777
|
+
|
|
778
|
+
**Returns:** Promise resolving to the API response with users array
|
|
779
|
+
|
|
780
|
+
---
|
|
781
|
+
|
|
782
|
+
#### `updateUsers(users: UpdateUserData[]): Promise<ApiResponse>`
|
|
783
|
+
|
|
784
|
+
Updates multiple users at once using the `/v2/chats/users` PATCH endpoint.
|
|
785
|
+
|
|
786
|
+
**Parameters:**
|
|
787
|
+
- `users` (UpdateUserData[]): Array of user data to update (1-100 users)
|
|
788
|
+
- `xmppUsername` (string, required): XMPP username to identify the user
|
|
789
|
+
- `firstName` (string, optional): First name
|
|
790
|
+
- `lastName` (string, optional): Last name
|
|
791
|
+
- `username` (string, optional): Username
|
|
792
|
+
- `profileImage` (string, optional): Profile image URL
|
|
793
|
+
|
|
794
|
+
**Returns:** Promise resolving to the API response with results array
|
|
795
|
+
|
|
796
|
+
**Response Status Values:**
|
|
797
|
+
- `updated`: User was successfully updated (includes updated user data)
|
|
798
|
+
- `not-found`: User was not found
|
|
799
|
+
- `skipped`: User update was skipped
|
|
800
|
+
|
|
801
|
+
**Limits:** 1-100 users per request
|
|
802
|
+
|
|
803
|
+
---
|
|
804
|
+
|
|
805
|
+
#### `deleteUsers(userIds: UUID[]): Promise<ApiResponse>`
|
|
806
|
+
|
|
807
|
+
Deletes users from the chat service using the `/v1/users/batch` endpoint.
|
|
808
|
+
|
|
809
|
+
**Parameters:**
|
|
810
|
+
- `userIds` (UUID[]): Array of user IDs to delete
|
|
811
|
+
|
|
812
|
+
**Returns:** Promise resolving to the API response
|
|
813
|
+
|
|
814
|
+
**Note:** Gracefully handles non-existent users (422 status with "not found").
|
|
815
|
+
|
|
816
|
+
---
|
|
817
|
+
|
|
818
|
+
#### `deleteChatRoom(chatId: UUID): Promise<ApiResponse>`
|
|
819
|
+
|
|
820
|
+
Deletes a chat room using the `/v1/chats` endpoint.
|
|
821
|
+
|
|
822
|
+
**Parameters:**
|
|
823
|
+
- `chatId` (UUID): The unique identifier of the chat/workspace
|
|
824
|
+
|
|
825
|
+
**Returns:** Promise resolving to the API response
|
|
826
|
+
|
|
827
|
+
**Note:** Gracefully handles non-existent rooms (422 status with "not found").
|
|
828
|
+
|
|
829
|
+
---
|
|
830
|
+
|
|
831
|
+
### Helper Methods
|
|
832
|
+
|
|
833
|
+
#### `createChatName(chatId: UUID, full?: boolean): string`
|
|
834
|
+
|
|
835
|
+
Generates a chat room JID from a chat ID.
|
|
836
|
+
|
|
837
|
+
**Parameters:**
|
|
838
|
+
- `chatId` (UUID): The unique identifier of the chat
|
|
839
|
+
- `full` (boolean, optional): Whether to include the full JID domain (default: true)
|
|
840
|
+
|
|
841
|
+
**Returns:** The JID string
|
|
842
|
+
- Full: `{appId}_{chatId}@conference.xmpp.ethoradev.com`
|
|
843
|
+
- Short: `{appId}_{chatId}`
|
|
844
|
+
|
|
845
|
+
---
|
|
846
|
+
|
|
847
|
+
#### `createChatUserJwtToken(userId: UUID): string`
|
|
848
|
+
|
|
849
|
+
Creates a client-side JWT token for user authentication.
|
|
850
|
+
|
|
851
|
+
**Parameters:**
|
|
852
|
+
- `userId` (UUID): The unique identifier of the user
|
|
853
|
+
|
|
854
|
+
**Returns:** The encoded JWT token for client-side authentication
|
|
855
|
+
|
|
856
|
+
---
|
|
857
|
+
|
|
624
858
|
## Error Handling
|
|
625
859
|
|
|
860
|
+
|
|
626
861
|
### Handling API Errors
|
|
627
862
|
|
|
628
863
|
The SDK uses Axios for HTTP requests, so errors are AxiosError instances:
|
|
629
864
|
|
|
630
865
|
```typescript
|
|
631
|
-
import axios from
|
|
632
|
-
import { getEthoraSDKService } from
|
|
866
|
+
import axios from 'axios';
|
|
867
|
+
import { getEthoraSDKService } from '@ethora/sdk-backend';
|
|
633
868
|
|
|
634
869
|
const chatService = getEthoraSDKService();
|
|
635
870
|
|
|
@@ -644,20 +879,20 @@ async function createChatRoomSafely(workspaceId: string) {
|
|
|
644
879
|
// Handle specific error cases
|
|
645
880
|
if (status === 422) {
|
|
646
881
|
// Validation error
|
|
647
|
-
console.error(
|
|
882
|
+
console.error('Validation error:', errorData);
|
|
648
883
|
} else if (status === 401) {
|
|
649
884
|
// Authentication error
|
|
650
|
-
console.error(
|
|
885
|
+
console.error('Authentication failed - check your credentials');
|
|
651
886
|
} else if (status === 404) {
|
|
652
887
|
// Resource not found
|
|
653
|
-
console.error(
|
|
888
|
+
console.error('Resource not found');
|
|
654
889
|
} else {
|
|
655
890
|
// Other HTTP errors
|
|
656
891
|
console.error(`HTTP error ${status}:`, errorData);
|
|
657
892
|
}
|
|
658
893
|
} else {
|
|
659
894
|
// Non-HTTP errors
|
|
660
|
-
console.error(
|
|
895
|
+
console.error('Unexpected error:', error);
|
|
661
896
|
}
|
|
662
897
|
throw error;
|
|
663
898
|
}
|
|
@@ -678,17 +913,17 @@ async function ensureChatRoomExists(workspaceId: string) {
|
|
|
678
913
|
if (axios.isAxiosError(error)) {
|
|
679
914
|
const errorData = error.response?.data;
|
|
680
915
|
const errorMessage =
|
|
681
|
-
typeof errorData ===
|
|
682
|
-
? (errorData as { error?: string }).error ||
|
|
683
|
-
: String(errorData ||
|
|
916
|
+
typeof errorData === 'object' && errorData !== null
|
|
917
|
+
? (errorData as { error?: string }).error || ''
|
|
918
|
+
: String(errorData || '');
|
|
684
919
|
|
|
685
920
|
// If room already exists, that's okay
|
|
686
921
|
if (
|
|
687
922
|
error.response?.status === 422 &&
|
|
688
|
-
(errorMessage.includes(
|
|
689
|
-
errorMessage.includes(
|
|
923
|
+
(errorMessage.includes('already exist') ||
|
|
924
|
+
errorMessage.includes('already exists'))
|
|
690
925
|
) {
|
|
691
|
-
console.log(
|
|
926
|
+
console.log('Chat room already exists, continuing...');
|
|
692
927
|
return; // Success - room exists
|
|
693
928
|
}
|
|
694
929
|
}
|
|
@@ -719,8 +954,8 @@ Create a service wrapper in your application:
|
|
|
719
954
|
|
|
720
955
|
```typescript
|
|
721
956
|
// services/chatService.ts
|
|
722
|
-
import { getEthoraSDKService } from
|
|
723
|
-
import type { ChatRepository } from
|
|
957
|
+
import { getEthoraSDKService } from '@ethora/sdk-backend';
|
|
958
|
+
import type { ChatRepository } from '@ethora/sdk-backend';
|
|
724
959
|
|
|
725
960
|
class ChatServiceWrapper {
|
|
726
961
|
private service: ChatRepository;
|
|
@@ -752,16 +987,16 @@ Validate environment variables on application startup:
|
|
|
752
987
|
// config/validateEnv.ts
|
|
753
988
|
function validateEthoraConfig() {
|
|
754
989
|
const required = [
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
990
|
+
'ETHORA_CHAT_API_URL',
|
|
991
|
+
'ETHORA_CHAT_APP_ID',
|
|
992
|
+
'ETHORA_CHAT_APP_SECRET',
|
|
758
993
|
];
|
|
759
994
|
|
|
760
995
|
const missing = required.filter((key) => !process.env[key]);
|
|
761
996
|
|
|
762
997
|
if (missing.length > 0) {
|
|
763
998
|
throw new Error(
|
|
764
|
-
`Missing required Ethora environment variables: ${missing.join(
|
|
999
|
+
`Missing required Ethora environment variables: ${missing.join(', ')}`,
|
|
765
1000
|
);
|
|
766
1001
|
}
|
|
767
1002
|
}
|
|
@@ -775,8 +1010,8 @@ validateEthoraConfig();
|
|
|
775
1010
|
Integrate with your existing logging system:
|
|
776
1011
|
|
|
777
1012
|
```typescript
|
|
778
|
-
import { getEthoraSDKService } from
|
|
779
|
-
import { logger } from
|
|
1013
|
+
import { getEthoraSDKService } from '@ethora/sdk-backend';
|
|
1014
|
+
import { logger } from './utils/logger'; // Your logger
|
|
780
1015
|
|
|
781
1016
|
const chatService = getEthoraSDKService();
|
|
782
1017
|
|
|
@@ -798,7 +1033,7 @@ async function createChatRoomWithLogging(workspaceId: string) {
|
|
|
798
1033
|
Use TypeScript types from the SDK:
|
|
799
1034
|
|
|
800
1035
|
```typescript
|
|
801
|
-
import type { UUID, ApiResponse } from
|
|
1036
|
+
import type { UUID, ApiResponse } from '@ethora/sdk-backend';
|
|
802
1037
|
|
|
803
1038
|
async function createUserTyped(
|
|
804
1039
|
userId: UUID,
|
|
@@ -806,7 +1041,7 @@ async function createUserTyped(
|
|
|
806
1041
|
firstName: string;
|
|
807
1042
|
lastName: string;
|
|
808
1043
|
email: string;
|
|
809
|
-
}
|
|
1044
|
+
},
|
|
810
1045
|
): Promise<ApiResponse> {
|
|
811
1046
|
const chatService = getEthoraSDKService();
|
|
812
1047
|
return await chatService.createUser(userId, userData);
|
|
@@ -839,7 +1074,7 @@ try {
|
|
|
839
1074
|
} catch (error) {
|
|
840
1075
|
if (axios.isAxiosError(error) && error.response?.status === 422) {
|
|
841
1076
|
// User already exists, continue
|
|
842
|
-
console.log(
|
|
1077
|
+
console.log('User already exists');
|
|
843
1078
|
} else {
|
|
844
1079
|
throw error;
|
|
845
1080
|
}
|
|
@@ -887,3 +1122,6 @@ Apache 2.0
|
|
|
887
1122
|
## Support
|
|
888
1123
|
|
|
889
1124
|
For issues and questions, please open an issue on the GitHub repository.
|
|
1125
|
+
|
|
1126
|
+
To run tests with logs run from root:
|
|
1127
|
+
TEST_LOG_FILE=logs/chat-repo.log npm test
|