powr-sdk-api 4.6.5 → 4.7.1
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/dist/routes/chat.js +99 -29
- package/package.json +1 -1
package/dist/routes/chat.js
CHANGED
|
@@ -17,25 +17,14 @@ router.get('/conversations', async (req, res) => {
|
|
|
17
17
|
const projectId = req.projectId;
|
|
18
18
|
console.log('Current user ID:', userId, 'Access:', userAccess, 'Project:', projectId);
|
|
19
19
|
const db = await getDb();
|
|
20
|
+
// Show all conversations user is in (1:1 DMs and groups, any project)
|
|
20
21
|
const conversations = await db.collection('conversations').find({
|
|
21
|
-
participants: userId
|
|
22
|
-
projectId: projectId
|
|
22
|
+
participants: userId
|
|
23
23
|
}).toArray();
|
|
24
24
|
|
|
25
|
-
//
|
|
25
|
+
// Build response for each conversation
|
|
26
26
|
const conversationsWithUsers = await Promise.all(conversations.map(async conv => {
|
|
27
|
-
const
|
|
28
|
-
console.log('Looking for other user with ID:', otherUserId, 'type:', typeof otherUserId);
|
|
29
|
-
const otherUser = await db.collection('users').findOne({
|
|
30
|
-
_id: new ObjectId(otherUserId)
|
|
31
|
-
}, {
|
|
32
|
-
projection: {
|
|
33
|
-
fullName: 1,
|
|
34
|
-
username: 1,
|
|
35
|
-
avatar: 1
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
|
-
console.log('Found other user:', otherUser);
|
|
27
|
+
const isGroup = conv.type === 'group';
|
|
39
28
|
|
|
40
29
|
// Get last message
|
|
41
30
|
const lastMessage = await db.collection('messages').findOne({
|
|
@@ -54,14 +43,32 @@ router.get('/conversations', async (req, res) => {
|
|
|
54
43
|
},
|
|
55
44
|
read: false
|
|
56
45
|
});
|
|
46
|
+
let displayName, avatar;
|
|
47
|
+
if (isGroup) {
|
|
48
|
+
displayName = conv.name || 'Group';
|
|
49
|
+
avatar = null; // Groups typically use icon, not user avatar
|
|
50
|
+
} else {
|
|
51
|
+
const otherUserId = conv.participants.find(p => String(p) !== String(userId));
|
|
52
|
+
const otherUser = otherUserId ? await db.collection('users').findOne({
|
|
53
|
+
_id: new ObjectId(otherUserId)
|
|
54
|
+
}, {
|
|
55
|
+
projection: {
|
|
56
|
+
fullName: 1,
|
|
57
|
+
avatar: 1
|
|
58
|
+
}
|
|
59
|
+
}) : null;
|
|
60
|
+
displayName = (otherUser === null || otherUser === void 0 ? void 0 : otherUser.fullName) || 'Unknown User';
|
|
61
|
+
avatar = otherUser === null || otherUser === void 0 ? void 0 : otherUser.avatar;
|
|
62
|
+
}
|
|
57
63
|
return {
|
|
58
64
|
id: conv._id.toString(),
|
|
59
|
-
|
|
60
|
-
|
|
65
|
+
type: isGroup ? 'group' : 'dm',
|
|
66
|
+
name: displayName,
|
|
67
|
+
avatar,
|
|
61
68
|
lastMessage: (lastMessage === null || lastMessage === void 0 ? void 0 : lastMessage.content) || '',
|
|
62
69
|
lastMessageTime: (lastMessage === null || lastMessage === void 0 ? void 0 : lastMessage.createdAt) || conv.createdAt,
|
|
63
70
|
unreadCount,
|
|
64
|
-
isOnline: false
|
|
71
|
+
isOnline: false
|
|
65
72
|
};
|
|
66
73
|
}));
|
|
67
74
|
return res.json({
|
|
@@ -90,11 +97,13 @@ router.get('/conversations/:conversationId/messages', async (req, res) => {
|
|
|
90
97
|
// Verify user is part of conversation
|
|
91
98
|
const conversation = await db.collection('conversations').findOne({
|
|
92
99
|
_id: new ObjectId(conversationId),
|
|
93
|
-
participants: userId
|
|
94
|
-
projectId: projectId
|
|
100
|
+
participants: userId
|
|
95
101
|
});
|
|
96
102
|
if (!conversation) {
|
|
97
|
-
return res.
|
|
103
|
+
return res.status(404).json({
|
|
104
|
+
success: false,
|
|
105
|
+
message: 'Conversation not found'
|
|
106
|
+
});
|
|
98
107
|
}
|
|
99
108
|
const messages = await db.collection('messages').find({
|
|
100
109
|
conversationId: conversationId
|
|
@@ -157,8 +166,7 @@ router.post('/conversations/:conversationId/messages', async (req, res) => {
|
|
|
157
166
|
// Verify user is part of conversation
|
|
158
167
|
const conversation = await db.collection('conversations').findOne({
|
|
159
168
|
_id: new ObjectId(conversationId),
|
|
160
|
-
participants: userId
|
|
161
|
-
projectId: projectId
|
|
169
|
+
participants: userId
|
|
162
170
|
});
|
|
163
171
|
if (!conversation) {
|
|
164
172
|
return res.status(404).json({
|
|
@@ -214,23 +222,84 @@ router.post('/conversations/:conversationId/messages', async (req, res) => {
|
|
|
214
222
|
}
|
|
215
223
|
});
|
|
216
224
|
|
|
217
|
-
// Create a new conversation
|
|
225
|
+
// Create a new conversation (DM or group)
|
|
218
226
|
router.post('/conversations', async (req, res) => {
|
|
219
227
|
try {
|
|
220
228
|
const {
|
|
221
229
|
participantId,
|
|
230
|
+
participantIds,
|
|
231
|
+
type,
|
|
232
|
+
name,
|
|
222
233
|
projectId: bodyProjectId
|
|
223
234
|
} = req.body;
|
|
224
235
|
const userId = req.user.powrId;
|
|
225
236
|
const userAccess = req.user.access;
|
|
226
237
|
const projectId = bodyProjectId || req.projectId;
|
|
238
|
+
const db = await getDb();
|
|
239
|
+
|
|
240
|
+
// --- GROUP ---
|
|
241
|
+
if (type === 'group') {
|
|
242
|
+
if (!participantIds || !Array.isArray(participantIds) || participantIds.length === 0) {
|
|
243
|
+
return res.status(400).json({
|
|
244
|
+
success: false,
|
|
245
|
+
message: "participantIds (array) is required for group"
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
if (!name || typeof name !== 'string' || !name.trim()) {
|
|
249
|
+
return res.status(400).json({
|
|
250
|
+
success: false,
|
|
251
|
+
message: "name is required for group"
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Normalize participant IDs (ensure strings, dedupe, include creator)
|
|
256
|
+
const normalizedIds = [...new Set([userId, ...participantIds.map(id => String(id))])];
|
|
257
|
+
|
|
258
|
+
// Check if group with same projectId + name already exists
|
|
259
|
+
const existingGroup = await db.collection('conversations').findOne({
|
|
260
|
+
type: 'group',
|
|
261
|
+
projectId: projectId,
|
|
262
|
+
name: name.trim()
|
|
263
|
+
});
|
|
264
|
+
if (existingGroup) {
|
|
265
|
+
// Return existing if creator is a participant
|
|
266
|
+
const isParticipant = existingGroup.participants.some(p => String(p) === String(userId));
|
|
267
|
+
if (isParticipant) {
|
|
268
|
+
return res.json({
|
|
269
|
+
success: true,
|
|
270
|
+
data: {
|
|
271
|
+
id: existingGroup._id.toString(),
|
|
272
|
+
message: 'Group already exists'
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
const conversation = {
|
|
278
|
+
type: 'group',
|
|
279
|
+
participants: normalizedIds,
|
|
280
|
+
name: name.trim(),
|
|
281
|
+
projectId: projectId,
|
|
282
|
+
createdBy: userId,
|
|
283
|
+
createdAt: new Date(),
|
|
284
|
+
updatedAt: new Date()
|
|
285
|
+
};
|
|
286
|
+
const result = await db.collection('conversations').insertOne(conversation);
|
|
287
|
+
return res.status(201).json({
|
|
288
|
+
success: true,
|
|
289
|
+
data: {
|
|
290
|
+
id: result.insertedId.toString(),
|
|
291
|
+
message: 'Group created successfully'
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// --- DM (default) ---
|
|
227
297
|
if (!participantId) {
|
|
228
298
|
return res.status(400).json({
|
|
229
299
|
success: false,
|
|
230
|
-
message: "Participant ID is required"
|
|
300
|
+
message: "Participant ID is required for DM (or use type: 'group' with participantIds)"
|
|
231
301
|
});
|
|
232
302
|
}
|
|
233
|
-
const db = await getDb();
|
|
234
303
|
|
|
235
304
|
// Check if user has permission to chat with this participant
|
|
236
305
|
const participant = await db.collection('users').findOne({
|
|
@@ -250,13 +319,10 @@ router.post('/conversations', async (req, res) => {
|
|
|
250
319
|
// Validate access permissions
|
|
251
320
|
let hasPermission = false;
|
|
252
321
|
if (userAccess === 100) {
|
|
253
|
-
// Admin can chat with everyone
|
|
254
322
|
hasPermission = true;
|
|
255
323
|
} else if (userAccess === 900) {
|
|
256
|
-
// Client can only chat with admins
|
|
257
324
|
hasPermission = participant.access === 100;
|
|
258
325
|
} else {
|
|
259
|
-
// Employee can chat with employees and admins
|
|
260
326
|
hasPermission = participant.access === 100 || participant.access === null;
|
|
261
327
|
}
|
|
262
328
|
if (!hasPermission) {
|
|
@@ -268,6 +334,9 @@ router.post('/conversations', async (req, res) => {
|
|
|
268
334
|
|
|
269
335
|
// Check if conversation already exists
|
|
270
336
|
const existingConversation = await db.collection('conversations').findOne({
|
|
337
|
+
type: {
|
|
338
|
+
$ne: 'group'
|
|
339
|
+
},
|
|
271
340
|
participants: {
|
|
272
341
|
$all: [userId, participantId]
|
|
273
342
|
},
|
|
@@ -283,6 +352,7 @@ router.post('/conversations', async (req, res) => {
|
|
|
283
352
|
});
|
|
284
353
|
}
|
|
285
354
|
const conversation = {
|
|
355
|
+
type: 'dm',
|
|
286
356
|
participants: [userId, participantId],
|
|
287
357
|
projectId: projectId,
|
|
288
358
|
createdAt: new Date(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "powr-sdk-api",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.7.1",
|
|
4
4
|
"description": "Shared API core library for PowrStack projects. Zero dependencies - works with Express, Next.js API routes, and other frameworks. All features are optional and install only what you need.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|