@ethora/sdk-backend 25.12.15
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/LICENSE +201 -0
- package/README.md +753 -0
- package/dist/config/secrets.d.ts +52 -0
- package/dist/config/secrets.d.ts.map +1 -0
- package/dist/config/secrets.js +63 -0
- package/dist/config/secrets.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/repositories/EthoraSDKService.d.ts +120 -0
- package/dist/repositories/EthoraSDKService.d.ts.map +1 -0
- package/dist/repositories/EthoraSDKService.js +389 -0
- package/dist/repositories/EthoraSDKService.js.map +1 -0
- package/dist/types/index.d.ts +102 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +6 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/jwt.d.ts +34 -0
- package/dist/utils/jwt.d.ts.map +1 -0
- package/dist/utils/jwt.js +86 -0
- package/dist/utils/jwt.js.map +1 -0
- package/dist/utils/logger.d.ts +26 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +58 -0
- package/dist/utils/logger.js.map +1 -0
- package/package.json +38 -0
package/README.md
ADDED
|
@@ -0,0 +1,753 @@
|
|
|
1
|
+
<!-- @format -->
|
|
2
|
+
|
|
3
|
+
# Adding Ethora SDK to Your Node.js Backend
|
|
4
|
+
|
|
5
|
+
This guide will walk you through integrating the Ethora SDK into your existing Node.js backend application.
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [Prerequisites](#prerequisites)
|
|
10
|
+
- [Installation](#installation)
|
|
11
|
+
- [Environment Configuration](#environment-configuration)
|
|
12
|
+
- [Basic Integration](#basic-integration)
|
|
13
|
+
- [Integration Patterns](#integration-patterns)
|
|
14
|
+
- [Common Use Cases](#common-use-cases)
|
|
15
|
+
- [Error Handling](#error-handling)
|
|
16
|
+
- [Best Practices](#best-practices)
|
|
17
|
+
- [Troubleshooting](#troubleshooting)
|
|
18
|
+
|
|
19
|
+
## Prerequisites
|
|
20
|
+
|
|
21
|
+
- Node.js 18+ or higher
|
|
22
|
+
- TypeScript 5.0+ (for TypeScript projects)
|
|
23
|
+
- An existing Node.js backend application (Express, Fastify, NestJS, etc.)
|
|
24
|
+
- Ethora API credentials:
|
|
25
|
+
- `ETHORA_CHAT_API_URL`
|
|
26
|
+
- `ETHORA_CHAT_APP_ID`
|
|
27
|
+
- `ETHORA_CHAT_APP_SECRET`
|
|
28
|
+
- `ETHORA_CHAT_BOT_JID` (optional, for chatbot features)
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
### Step 1: Install the Package
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm install @ethora/sdk-backend
|
|
36
|
+
# or
|
|
37
|
+
yarn add @ethora/sdk-backend
|
|
38
|
+
# or
|
|
39
|
+
pnpm add @ethora/sdk-backend
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Step 2: Install Type Definitions (if using TypeScript)
|
|
43
|
+
|
|
44
|
+
The package includes TypeScript definitions, so no additional `@types` package is needed.
|
|
45
|
+
|
|
46
|
+
## Environment Configuration
|
|
47
|
+
|
|
48
|
+
### Step 1: Add Environment Variables
|
|
49
|
+
|
|
50
|
+
Add the following environment variables to your `.env` file or your environment configuration:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# Required
|
|
54
|
+
ETHORA_CHAT_API_URL=https://api.ethoradev.com
|
|
55
|
+
ETHORA_CHAT_APP_ID=your_app_id_here
|
|
56
|
+
ETHORA_CHAT_APP_SECRET=your_app_secret_here
|
|
57
|
+
|
|
58
|
+
# Optional (for chatbot features)
|
|
59
|
+
ETHORA_CHAT_BOT_JID=your_bot_jid@domain.com
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Step 2: Load Environment Variables
|
|
63
|
+
|
|
64
|
+
If you're using a `.env` file, ensure you have `dotenv` installed and configured:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npm install dotenv
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
In your main application file (e.g., `app.js`, `server.js`, or `index.ts`):
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
import dotenv from "dotenv";
|
|
74
|
+
|
|
75
|
+
// Load environment variables
|
|
76
|
+
dotenv.config();
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Basic Integration
|
|
80
|
+
|
|
81
|
+
### Step 1: Import the SDK
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
import { getEthoraSDKService } from "@ethora/sdk-backend";
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Step 2: Initialize the Service
|
|
88
|
+
|
|
89
|
+
You can initialize the service in several ways:
|
|
90
|
+
|
|
91
|
+
#### Option A: Singleton Pattern (Recommended)
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
// services/chatService.ts
|
|
95
|
+
import { getEthoraSDKService } from "@ethora/sdk-backend";
|
|
96
|
+
|
|
97
|
+
// Get the singleton instance
|
|
98
|
+
const chatService = getEthoraSDKService();
|
|
99
|
+
|
|
100
|
+
export default chatService;
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
#### Option B: Direct Initialization
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
// In your route handler or service
|
|
107
|
+
import { getEthoraSDKService } from "@ethora/sdk-backend";
|
|
108
|
+
|
|
109
|
+
const chatService = getEthoraSDKService();
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
#### Option C: Dependency Injection (for frameworks like NestJS)
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
// chat.service.ts
|
|
116
|
+
import { Injectable } from "@nestjs/common";
|
|
117
|
+
import { getEthoraSDKService } from "@ethora/sdk-backend";
|
|
118
|
+
|
|
119
|
+
@Injectable()
|
|
120
|
+
export class ChatService {
|
|
121
|
+
private readonly ethoraService = getEthoraSDKService();
|
|
122
|
+
|
|
123
|
+
// Your methods here
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Integration Patterns
|
|
128
|
+
|
|
129
|
+
### Pattern 1: Express.js Integration
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
// routes/chat.ts
|
|
133
|
+
import express, { Request, Response } from "express";
|
|
134
|
+
import { getEthoraSDKService } from "@ethora/sdk-backend";
|
|
135
|
+
import axios from "axios";
|
|
136
|
+
|
|
137
|
+
const router = express.Router();
|
|
138
|
+
const chatService = getEthoraSDKService();
|
|
139
|
+
|
|
140
|
+
// Create a chat room for a workspace
|
|
141
|
+
router.post(
|
|
142
|
+
"/workspaces/:workspaceId/chat",
|
|
143
|
+
async (req: Request, res: Response) => {
|
|
144
|
+
try {
|
|
145
|
+
const { workspaceId } = req.params;
|
|
146
|
+
const roomData = req.body;
|
|
147
|
+
|
|
148
|
+
const response = await chatService.createChatRoom(workspaceId, {
|
|
149
|
+
title: roomData.title || `Chat Room ${workspaceId}`,
|
|
150
|
+
uuid: workspaceId,
|
|
151
|
+
type: roomData.type || "group",
|
|
152
|
+
...roomData,
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
res.json({ success: true, data: response });
|
|
156
|
+
} catch (error) {
|
|
157
|
+
if (axios.isAxiosError(error)) {
|
|
158
|
+
res.status(error.response?.status || 500).json({
|
|
159
|
+
error: "Failed to create chat room",
|
|
160
|
+
details: error.response?.data,
|
|
161
|
+
});
|
|
162
|
+
} else {
|
|
163
|
+
res.status(500).json({ error: "Internal server error" });
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
// Create a user
|
|
170
|
+
router.post("/users/:userId", async (req: Request, res: Response) => {
|
|
171
|
+
try {
|
|
172
|
+
const { userId } = req.params;
|
|
173
|
+
const userData = req.body;
|
|
174
|
+
|
|
175
|
+
const response = await chatService.createUser(userId, userData);
|
|
176
|
+
res.json({ success: true, data: response });
|
|
177
|
+
} catch (error) {
|
|
178
|
+
if (axios.isAxiosError(error)) {
|
|
179
|
+
res.status(error.response?.status || 500).json({
|
|
180
|
+
error: "Failed to create user",
|
|
181
|
+
details: error.response?.data,
|
|
182
|
+
});
|
|
183
|
+
} else {
|
|
184
|
+
res.status(500).json({ error: "Internal server error" });
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
// Grant user access to chat room
|
|
190
|
+
router.post(
|
|
191
|
+
"/workspaces/:workspaceId/chat/users/:userId",
|
|
192
|
+
async (req: Request, res: Response) => {
|
|
193
|
+
try {
|
|
194
|
+
const { workspaceId, userId } = req.params;
|
|
195
|
+
|
|
196
|
+
await chatService.grantUserAccessToChatRoom(workspaceId, userId);
|
|
197
|
+
res.json({ success: true, message: "Access granted" });
|
|
198
|
+
} catch (error) {
|
|
199
|
+
if (axios.isAxiosError(error)) {
|
|
200
|
+
res.status(error.response?.status || 500).json({
|
|
201
|
+
error: "Failed to grant access",
|
|
202
|
+
details: error.response?.data,
|
|
203
|
+
});
|
|
204
|
+
} else {
|
|
205
|
+
res.status(500).json({ error: "Internal server error" });
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
// Generate client JWT token
|
|
212
|
+
router.get("/users/:userId/chat-token", (req: Request, res: Response) => {
|
|
213
|
+
try {
|
|
214
|
+
const { userId } = req.params;
|
|
215
|
+
const token = chatService.createChatUserJwtToken(userId);
|
|
216
|
+
res.json({ token });
|
|
217
|
+
} catch (error) {
|
|
218
|
+
res.status(500).json({ error: "Failed to generate token" });
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
export default router;
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Pattern 2: NestJS Integration
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
// chat/chat.service.ts
|
|
229
|
+
import { Injectable, HttpException, HttpStatus } from "@nestjs/common";
|
|
230
|
+
import { getEthoraSDKService } from "@ethora/sdk-backend";
|
|
231
|
+
import axios from "axios";
|
|
232
|
+
|
|
233
|
+
@Injectable()
|
|
234
|
+
export class ChatService {
|
|
235
|
+
private readonly ethoraService = getEthoraSDKService();
|
|
236
|
+
|
|
237
|
+
async createChatRoom(workspaceId: string, roomData?: any) {
|
|
238
|
+
try {
|
|
239
|
+
return await this.ethoraService.createChatRoom(workspaceId, roomData);
|
|
240
|
+
} catch (error) {
|
|
241
|
+
if (axios.isAxiosError(error)) {
|
|
242
|
+
throw new HttpException(
|
|
243
|
+
{
|
|
244
|
+
message: "Failed to create chat room",
|
|
245
|
+
details: error.response?.data,
|
|
246
|
+
},
|
|
247
|
+
error.response?.status || HttpStatus.INTERNAL_SERVER_ERROR
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
throw error;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
async createUser(userId: string, userData?: any) {
|
|
255
|
+
try {
|
|
256
|
+
return await this.ethoraService.createUser(userId, userData);
|
|
257
|
+
} catch (error) {
|
|
258
|
+
if (axios.isAxiosError(error)) {
|
|
259
|
+
throw new HttpException(
|
|
260
|
+
{
|
|
261
|
+
message: "Failed to create user",
|
|
262
|
+
details: error.response?.data,
|
|
263
|
+
},
|
|
264
|
+
error.response?.status || HttpStatus.INTERNAL_SERVER_ERROR
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
throw error;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
generateClientToken(userId: string): string {
|
|
272
|
+
return this.ethoraService.createChatUserJwtToken(userId);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// chat/chat.controller.ts
|
|
277
|
+
import { Controller, Post, Get, Param, Body } from "@nestjs/common";
|
|
278
|
+
import { ChatService } from "./chat.service";
|
|
279
|
+
|
|
280
|
+
@Controller("chat")
|
|
281
|
+
export class ChatController {
|
|
282
|
+
constructor(private readonly chatService: ChatService) {}
|
|
283
|
+
|
|
284
|
+
@Post("workspaces/:workspaceId/rooms")
|
|
285
|
+
async createChatRoom(
|
|
286
|
+
@Param("workspaceId") workspaceId: string,
|
|
287
|
+
@Body() roomData: any
|
|
288
|
+
) {
|
|
289
|
+
return this.chatService.createChatRoom(workspaceId, roomData);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
@Post("users/:userId")
|
|
293
|
+
async createUser(@Param("userId") userId: string, @Body() userData: any) {
|
|
294
|
+
return this.chatService.createUser(userId, userData);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
@Get("users/:userId/token")
|
|
298
|
+
getClientToken(@Param("userId") userId: string) {
|
|
299
|
+
return { token: this.chatService.generateClientToken(userId) };
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### Pattern 3: Fastify Integration
|
|
305
|
+
|
|
306
|
+
```typescript
|
|
307
|
+
// routes/chat.ts
|
|
308
|
+
import { FastifyInstance, FastifyRequest, FastifyReply } from "fastify";
|
|
309
|
+
import { getEthoraSDKService } from "@ethora/sdk-backend";
|
|
310
|
+
|
|
311
|
+
const chatService = getEthoraSDKService();
|
|
312
|
+
|
|
313
|
+
export async function chatRoutes(fastify: FastifyInstance) {
|
|
314
|
+
// Create chat room
|
|
315
|
+
fastify.post(
|
|
316
|
+
"/workspaces/:workspaceId/chat",
|
|
317
|
+
async (request: FastifyRequest, reply: FastifyReply) => {
|
|
318
|
+
const { workspaceId } = request.params as { workspaceId: string };
|
|
319
|
+
const roomData = request.body as any;
|
|
320
|
+
|
|
321
|
+
try {
|
|
322
|
+
const response = await chatService.createChatRoom(
|
|
323
|
+
workspaceId,
|
|
324
|
+
roomData
|
|
325
|
+
);
|
|
326
|
+
return { success: true, data: response };
|
|
327
|
+
} catch (error) {
|
|
328
|
+
reply.code(500).send({ error: "Failed to create chat room" });
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
);
|
|
332
|
+
|
|
333
|
+
// Generate client token
|
|
334
|
+
fastify.get(
|
|
335
|
+
"/users/:userId/chat-token",
|
|
336
|
+
async (request: FastifyRequest, reply: FastifyReply) => {
|
|
337
|
+
const { userId } = request.params as { userId: string };
|
|
338
|
+
const token = chatService.createChatUserJwtToken(userId);
|
|
339
|
+
return { token };
|
|
340
|
+
}
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
## Common Use Cases
|
|
346
|
+
|
|
347
|
+
### Use Case 1: Workspace Setup Flow
|
|
348
|
+
|
|
349
|
+
When creating a new workspace, set up the chat room and initial users:
|
|
350
|
+
|
|
351
|
+
```typescript
|
|
352
|
+
async function setupWorkspaceChat(
|
|
353
|
+
workspaceId: string,
|
|
354
|
+
userIds: string[],
|
|
355
|
+
adminUserId: string
|
|
356
|
+
) {
|
|
357
|
+
const chatService = getEthoraSDKService();
|
|
358
|
+
|
|
359
|
+
try {
|
|
360
|
+
// 1. Create chat room
|
|
361
|
+
await chatService.createChatRoom(workspaceId, {
|
|
362
|
+
title: `Workspace ${workspaceId}`,
|
|
363
|
+
uuid: workspaceId,
|
|
364
|
+
type: "group",
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
// 2. Create users (if they don't exist)
|
|
368
|
+
for (const userId of userIds) {
|
|
369
|
+
try {
|
|
370
|
+
await chatService.createUser(userId, {
|
|
371
|
+
firstName: "User",
|
|
372
|
+
lastName: "Name",
|
|
373
|
+
});
|
|
374
|
+
} catch (error) {
|
|
375
|
+
// User might already exist, continue
|
|
376
|
+
console.warn(`User ${userId} might already exist`);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// 3. Grant access to all users
|
|
381
|
+
await chatService.grantUserAccessToChatRoom(workspaceId, userIds);
|
|
382
|
+
|
|
383
|
+
// 4. Grant chatbot access (if configured)
|
|
384
|
+
try {
|
|
385
|
+
await chatService.grantChatbotAccessToChatRoom(workspaceId);
|
|
386
|
+
} catch (error) {
|
|
387
|
+
console.warn("Chatbot access not configured or failed");
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return { success: true };
|
|
391
|
+
} catch (error) {
|
|
392
|
+
console.error("Failed to setup workspace chat:", error);
|
|
393
|
+
throw error;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### Use Case 2: User Onboarding
|
|
399
|
+
|
|
400
|
+
When a new user joins your platform:
|
|
401
|
+
|
|
402
|
+
```typescript
|
|
403
|
+
async function onboardNewUser(
|
|
404
|
+
userId: string,
|
|
405
|
+
userData: { firstName: string; lastName: string; email: string }
|
|
406
|
+
) {
|
|
407
|
+
const chatService = getEthoraSDKService();
|
|
408
|
+
|
|
409
|
+
try {
|
|
410
|
+
// Create user in chat service
|
|
411
|
+
await chatService.createUser(userId, {
|
|
412
|
+
firstName: userData.firstName,
|
|
413
|
+
lastName: userData.lastName,
|
|
414
|
+
email: userData.email,
|
|
415
|
+
displayName: `${userData.firstName} ${userData.lastName}`,
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
// Generate client token for frontend
|
|
419
|
+
const clientToken = chatService.createChatUserJwtToken(userId);
|
|
420
|
+
|
|
421
|
+
return {
|
|
422
|
+
success: true,
|
|
423
|
+
chatToken: clientToken,
|
|
424
|
+
};
|
|
425
|
+
} catch (error) {
|
|
426
|
+
console.error("Failed to onboard user:", error);
|
|
427
|
+
throw error;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
### Use Case 3: Adding User to Existing Workspace
|
|
433
|
+
|
|
434
|
+
When adding a user to an existing workspace:
|
|
435
|
+
|
|
436
|
+
```typescript
|
|
437
|
+
async function addUserToWorkspace(workspaceId: string, userId: string) {
|
|
438
|
+
const chatService = getEthoraSDKService();
|
|
439
|
+
|
|
440
|
+
try {
|
|
441
|
+
// Ensure user exists
|
|
442
|
+
try {
|
|
443
|
+
await chatService.createUser(userId);
|
|
444
|
+
} catch (error) {
|
|
445
|
+
// User might already exist, continue
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// Grant access to workspace chat room
|
|
449
|
+
await chatService.grantUserAccessToChatRoom(workspaceId, userId);
|
|
450
|
+
|
|
451
|
+
return { success: true };
|
|
452
|
+
} catch (error) {
|
|
453
|
+
console.error("Failed to add user to workspace:", error);
|
|
454
|
+
throw error;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
### Use Case 4: Cleanup on Workspace Deletion
|
|
460
|
+
|
|
461
|
+
When deleting a workspace:
|
|
462
|
+
|
|
463
|
+
```typescript
|
|
464
|
+
async function cleanupWorkspaceChat(workspaceId: string, userIds: string[]) {
|
|
465
|
+
const chatService = getEthoraSDKService();
|
|
466
|
+
|
|
467
|
+
try {
|
|
468
|
+
// Delete chat room (handles non-existent gracefully)
|
|
469
|
+
await chatService.deleteChatRoom(workspaceId);
|
|
470
|
+
|
|
471
|
+
// Optionally delete users (if they're no longer needed)
|
|
472
|
+
if (userIds.length > 0) {
|
|
473
|
+
try {
|
|
474
|
+
await chatService.deleteUsers(userIds);
|
|
475
|
+
} catch (error) {
|
|
476
|
+
console.warn("Some users might not exist:", error);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
return { success: true };
|
|
481
|
+
} catch (error) {
|
|
482
|
+
console.error("Failed to cleanup workspace chat:", error);
|
|
483
|
+
throw error;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
## Error Handling
|
|
489
|
+
|
|
490
|
+
### Handling API Errors
|
|
491
|
+
|
|
492
|
+
The SDK uses Axios for HTTP requests, so errors are AxiosError instances:
|
|
493
|
+
|
|
494
|
+
```typescript
|
|
495
|
+
import axios from "axios";
|
|
496
|
+
import { getEthoraSDKService } from "@ethora/sdk-backend";
|
|
497
|
+
|
|
498
|
+
const chatService = getEthoraSDKService();
|
|
499
|
+
|
|
500
|
+
async function createChatRoomSafely(workspaceId: string) {
|
|
501
|
+
try {
|
|
502
|
+
return await chatService.createChatRoom(workspaceId);
|
|
503
|
+
} catch (error) {
|
|
504
|
+
if (axios.isAxiosError(error)) {
|
|
505
|
+
const status = error.response?.status;
|
|
506
|
+
const errorData = error.response?.data;
|
|
507
|
+
|
|
508
|
+
// Handle specific error cases
|
|
509
|
+
if (status === 422) {
|
|
510
|
+
// Validation error
|
|
511
|
+
console.error("Validation error:", errorData);
|
|
512
|
+
} else if (status === 401) {
|
|
513
|
+
// Authentication error
|
|
514
|
+
console.error("Authentication failed - check your credentials");
|
|
515
|
+
} else if (status === 404) {
|
|
516
|
+
// Resource not found
|
|
517
|
+
console.error("Resource not found");
|
|
518
|
+
} else {
|
|
519
|
+
// Other HTTP errors
|
|
520
|
+
console.error(`HTTP error ${status}:`, errorData);
|
|
521
|
+
}
|
|
522
|
+
} else {
|
|
523
|
+
// Non-HTTP errors
|
|
524
|
+
console.error("Unexpected error:", error);
|
|
525
|
+
}
|
|
526
|
+
throw error;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
### Graceful Error Handling for Idempotent Operations
|
|
532
|
+
|
|
533
|
+
Some operations are idempotent and can be safely retried:
|
|
534
|
+
|
|
535
|
+
```typescript
|
|
536
|
+
async function ensureChatRoomExists(workspaceId: string) {
|
|
537
|
+
const chatService = getEthoraSDKService();
|
|
538
|
+
|
|
539
|
+
try {
|
|
540
|
+
await chatService.createChatRoom(workspaceId);
|
|
541
|
+
} catch (error) {
|
|
542
|
+
if (axios.isAxiosError(error)) {
|
|
543
|
+
const errorData = error.response?.data;
|
|
544
|
+
const errorMessage =
|
|
545
|
+
typeof errorData === "object" && errorData !== null
|
|
546
|
+
? (errorData as { error?: string }).error || ""
|
|
547
|
+
: String(errorData || "");
|
|
548
|
+
|
|
549
|
+
// If room already exists, that's okay
|
|
550
|
+
if (
|
|
551
|
+
error.response?.status === 422 &&
|
|
552
|
+
(errorMessage.includes("already exist") ||
|
|
553
|
+
errorMessage.includes("already exists"))
|
|
554
|
+
) {
|
|
555
|
+
console.log("Chat room already exists, continuing...");
|
|
556
|
+
return; // Success - room exists
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
// Re-throw if it's a different error
|
|
560
|
+
throw error;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
## Best Practices
|
|
566
|
+
|
|
567
|
+
### 1. Use Singleton Pattern
|
|
568
|
+
|
|
569
|
+
The SDK provides a singleton instance. Reuse it rather than creating multiple instances:
|
|
570
|
+
|
|
571
|
+
```typescript
|
|
572
|
+
// Good
|
|
573
|
+
const chatService = getEthoraSDKService();
|
|
574
|
+
|
|
575
|
+
// Avoid
|
|
576
|
+
const chatService1 = getEthoraSDKService();
|
|
577
|
+
const chatService2 = getEthoraSDKService(); // Unnecessary
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
### 2. Centralize Chat Service
|
|
581
|
+
|
|
582
|
+
Create a service wrapper in your application:
|
|
583
|
+
|
|
584
|
+
```typescript
|
|
585
|
+
// services/chatService.ts
|
|
586
|
+
import { getEthoraSDKService } from "@ethora/sdk-backend";
|
|
587
|
+
import type { ChatRepository } from "@ethora/sdk-backend";
|
|
588
|
+
|
|
589
|
+
class ChatServiceWrapper {
|
|
590
|
+
private service: ChatRepository;
|
|
591
|
+
|
|
592
|
+
constructor() {
|
|
593
|
+
this.service = getEthoraSDKService();
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
async setupWorkspace(workspaceId: string, userIds: string[]) {
|
|
597
|
+
// Your custom logic here
|
|
598
|
+
await this.service.createChatRoom(workspaceId);
|
|
599
|
+
// ... more setup logic
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// Expose other methods as needed
|
|
603
|
+
getService() {
|
|
604
|
+
return this.service;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
export default new ChatServiceWrapper();
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
### 3. Environment Variable Validation
|
|
612
|
+
|
|
613
|
+
Validate environment variables on application startup:
|
|
614
|
+
|
|
615
|
+
```typescript
|
|
616
|
+
// config/validateEnv.ts
|
|
617
|
+
function validateEthoraConfig() {
|
|
618
|
+
const required = [
|
|
619
|
+
"ETHORA_CHAT_API_URL",
|
|
620
|
+
"ETHORA_CHAT_APP_ID",
|
|
621
|
+
"ETHORA_CHAT_APP_SECRET",
|
|
622
|
+
];
|
|
623
|
+
|
|
624
|
+
const missing = required.filter((key) => !process.env[key]);
|
|
625
|
+
|
|
626
|
+
if (missing.length > 0) {
|
|
627
|
+
throw new Error(
|
|
628
|
+
`Missing required Ethora environment variables: ${missing.join(", ")}`
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// Call on startup
|
|
634
|
+
validateEthoraConfig();
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
### 4. Logging Integration
|
|
638
|
+
|
|
639
|
+
Integrate with your existing logging system:
|
|
640
|
+
|
|
641
|
+
```typescript
|
|
642
|
+
import { getEthoraSDKService } from "@ethora/sdk-backend";
|
|
643
|
+
import { logger } from "./utils/logger"; // Your logger
|
|
644
|
+
|
|
645
|
+
const chatService = getEthoraSDKService();
|
|
646
|
+
|
|
647
|
+
async function createChatRoomWithLogging(workspaceId: string) {
|
|
648
|
+
logger.info(`Creating chat room for workspace: ${workspaceId}`);
|
|
649
|
+
try {
|
|
650
|
+
const result = await chatService.createChatRoom(workspaceId);
|
|
651
|
+
logger.info(`Chat room created successfully: ${workspaceId}`);
|
|
652
|
+
return result;
|
|
653
|
+
} catch (error) {
|
|
654
|
+
logger.error(`Failed to create chat room: ${workspaceId}`, error);
|
|
655
|
+
throw error;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
### 5. Type Safety
|
|
661
|
+
|
|
662
|
+
Use TypeScript types from the SDK:
|
|
663
|
+
|
|
664
|
+
```typescript
|
|
665
|
+
import type { UUID, ApiResponse } from "@ethora/sdk-backend";
|
|
666
|
+
|
|
667
|
+
async function createUserTyped(
|
|
668
|
+
userId: UUID,
|
|
669
|
+
userData: {
|
|
670
|
+
firstName: string;
|
|
671
|
+
lastName: string;
|
|
672
|
+
email: string;
|
|
673
|
+
}
|
|
674
|
+
): Promise<ApiResponse> {
|
|
675
|
+
const chatService = getEthoraSDKService();
|
|
676
|
+
return await chatService.createUser(userId, userData);
|
|
677
|
+
}
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
## Troubleshooting
|
|
681
|
+
|
|
682
|
+
### Issue: "Missing required environment variables"
|
|
683
|
+
|
|
684
|
+
**Solution:** Ensure all required environment variables are set:
|
|
685
|
+
|
|
686
|
+
```bash
|
|
687
|
+
ETHORA_CHAT_API_URL=https://api.ethoradev.com
|
|
688
|
+
ETHORA_CHAT_APP_ID=your_app_id
|
|
689
|
+
ETHORA_CHAT_APP_SECRET=your_app_secret
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
### Issue: "Authentication failed" (401 errors)
|
|
693
|
+
|
|
694
|
+
**Solution:** Verify your `ETHORA_CHAT_APP_SECRET` is correct and matches your app ID.
|
|
695
|
+
|
|
696
|
+
### Issue: "User already exists" errors
|
|
697
|
+
|
|
698
|
+
**Solution:** Handle idempotent operations gracefully:
|
|
699
|
+
|
|
700
|
+
```typescript
|
|
701
|
+
try {
|
|
702
|
+
await chatService.createUser(userId);
|
|
703
|
+
} catch (error) {
|
|
704
|
+
if (axios.isAxiosError(error) && error.response?.status === 422) {
|
|
705
|
+
// User already exists, continue
|
|
706
|
+
console.log("User already exists");
|
|
707
|
+
} else {
|
|
708
|
+
throw error;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
```
|
|
712
|
+
|
|
713
|
+
### Issue: "Chat room not found" during deletion
|
|
714
|
+
|
|
715
|
+
**Solution:** The SDK handles this gracefully. The `deleteChatRoom` method returns `{ ok: false, reason: "Chat room not found" }` if the room doesn't exist, which is safe to ignore.
|
|
716
|
+
|
|
717
|
+
### Issue: TypeScript compilation errors
|
|
718
|
+
|
|
719
|
+
**Solution:** Ensure you're using TypeScript 5.0+ and have proper type definitions:
|
|
720
|
+
|
|
721
|
+
```bash
|
|
722
|
+
npm install --save-dev typescript@^5.0.0
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
## Next Steps
|
|
726
|
+
|
|
727
|
+
- Review the [API Reference](../README.md#api-reference) for detailed method documentation
|
|
728
|
+
- Check out the [Examples](../examples/) directory for complete integration examples
|
|
729
|
+
- See the [Healthcare Insurance Demo](../examples/healthcare-insurance/) for a real-world use case
|
|
730
|
+
|
|
731
|
+
## Support
|
|
732
|
+
|
|
733
|
+
For issues, questions, or contributions, please refer to the main [README.md](../README.md) file.
|
|
734
|
+
|
|
735
|
+
### TypeScript Configuration
|
|
736
|
+
|
|
737
|
+
The project uses strict TypeScript settings. See `tsconfig.json` for details.
|
|
738
|
+
|
|
739
|
+
## Contributing
|
|
740
|
+
|
|
741
|
+
1. Fork the repository
|
|
742
|
+
2. Create a feature branch
|
|
743
|
+
3. Make your changes
|
|
744
|
+
4. Add tests if applicable
|
|
745
|
+
5. Submit a pull request
|
|
746
|
+
|
|
747
|
+
## License
|
|
748
|
+
|
|
749
|
+
Apache 2.0
|
|
750
|
+
|
|
751
|
+
## Support
|
|
752
|
+
|
|
753
|
+
For issues and questions, please open an issue on the GitHub repository.
|