powr-sdk-api 2.4.6 → 2.5.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.
@@ -0,0 +1,332 @@
1
+ "use strict";
2
+
3
+ const express = require('express');
4
+ const router = express.Router();
5
+ const {
6
+ ObjectId
7
+ } = require('mongodb');
8
+ const {
9
+ getDb
10
+ } = require('../services/mongo');
11
+
12
+ // Get all conversations for a user
13
+ router.get('/conversations', async (req, res) => {
14
+ try {
15
+ const userId = req.user.powrId;
16
+ const userAccess = req.user.access;
17
+ const projectId = req.projectId;
18
+ console.log('Current user ID:', userId, 'Access:', userAccess, 'Project:', projectId);
19
+ const db = await getDb();
20
+ const conversations = await db.collection('conversations').find({
21
+ participants: userId,
22
+ projectId: projectId
23
+ }).toArray();
24
+
25
+ // Get user details for each conversation
26
+ const conversationsWithUsers = await Promise.all(conversations.map(async conv => {
27
+ const otherUserId = conv.participants.find(id => id !== userId);
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);
39
+
40
+ // Get last message
41
+ const lastMessage = await db.collection('messages').findOne({
42
+ conversationId: conv._id.toString()
43
+ }, {
44
+ sort: {
45
+ createdAt: -1
46
+ }
47
+ });
48
+
49
+ // Get unread count
50
+ const unreadCount = await db.collection('messages').countDocuments({
51
+ conversationId: conv._id.toString(),
52
+ senderId: {
53
+ $ne: userId
54
+ },
55
+ read: false
56
+ });
57
+ return {
58
+ id: conv._id.toString(),
59
+ name: (otherUser === null || otherUser === void 0 ? void 0 : otherUser.fullName) || 'Unknown User',
60
+ avatar: otherUser === null || otherUser === void 0 ? void 0 : otherUser.avatar,
61
+ lastMessage: (lastMessage === null || lastMessage === void 0 ? void 0 : lastMessage.content) || '',
62
+ lastMessageTime: (lastMessage === null || lastMessage === void 0 ? void 0 : lastMessage.createdAt) || conv.createdAt,
63
+ unreadCount,
64
+ isOnline: false // Simple implementation - could be enhanced later
65
+ };
66
+ }));
67
+ res.success('Conversations fetched successfully', 200, conversationsWithUsers);
68
+ } catch (error) {
69
+ console.error('Error fetching conversations:', error);
70
+ res.error('Failed to fetch conversations', 500, error);
71
+ }
72
+ });
73
+
74
+ // Get messages for a conversation
75
+ router.get('/conversations/:conversationId/messages', async (req, res) => {
76
+ try {
77
+ const {
78
+ conversationId
79
+ } = req.params;
80
+ const userId = req.user.powrId;
81
+ const projectId = req.projectId;
82
+ const db = await getDb();
83
+
84
+ // Verify user is part of conversation
85
+ const conversation = await db.collection('conversations').findOne({
86
+ _id: new ObjectId(conversationId),
87
+ participants: userId,
88
+ projectId: projectId
89
+ });
90
+ if (!conversation) {
91
+ return res.error('Conversation not found', 404);
92
+ }
93
+ const messages = await db.collection('messages').find({
94
+ conversationId: conversationId
95
+ }).sort({
96
+ createdAt: 1
97
+ }).toArray();
98
+
99
+ // Mark messages as read
100
+ await db.collection('messages').updateMany({
101
+ conversationId: conversationId,
102
+ senderId: {
103
+ $ne: userId
104
+ },
105
+ read: false
106
+ }, {
107
+ $set: {
108
+ read: true
109
+ }
110
+ });
111
+ const messagesWithOwnership = messages.map(msg => ({
112
+ id: msg._id.toString(),
113
+ sender: msg.senderName,
114
+ message: msg.content,
115
+ timestamp: msg.createdAt,
116
+ isOwn: msg.senderId === userId
117
+ }));
118
+ res.success('Messages fetched successfully', 200, messagesWithOwnership);
119
+ } catch (error) {
120
+ console.error('Error fetching messages:', error);
121
+ res.error('Failed to fetch messages', 500, error);
122
+ }
123
+ });
124
+
125
+ // Send a message
126
+ router.post('/conversations/:conversationId/messages', async (req, res) => {
127
+ try {
128
+ const {
129
+ conversationId
130
+ } = req.params;
131
+ const {
132
+ content
133
+ } = req.body;
134
+ const userId = req.user.powrId;
135
+ const projectId = req.projectId;
136
+ if (!content || content.trim() === '') {
137
+ return res.error('Message content is required', 400);
138
+ }
139
+ const db = await getDb();
140
+
141
+ // Verify user is part of conversation
142
+ const conversation = await db.collection('conversations').findOne({
143
+ _id: new ObjectId(conversationId),
144
+ participants: userId,
145
+ projectId: projectId
146
+ });
147
+ if (!conversation) {
148
+ return res.error('Conversation not found', 404);
149
+ }
150
+
151
+ // Get current user's name from database
152
+ const currentUser = await db.collection('users').findOne({
153
+ _id: new ObjectId(userId)
154
+ }, {
155
+ projection: {
156
+ fullName: 1
157
+ }
158
+ });
159
+ const message = {
160
+ conversationId: conversationId,
161
+ senderId: userId,
162
+ senderName: (currentUser === null || currentUser === void 0 ? void 0 : currentUser.fullName) || 'Unknown User',
163
+ content: content.trim(),
164
+ createdAt: new Date(),
165
+ read: false,
166
+ projectId: projectId
167
+ };
168
+ const result = await db.collection('messages').insertOne(message);
169
+
170
+ // Update conversation last message
171
+ await db.collection('conversations').updateOne({
172
+ _id: new ObjectId(conversationId)
173
+ }, {
174
+ $set: {
175
+ updatedAt: new Date()
176
+ }
177
+ });
178
+ const responseMessage = {
179
+ id: result.insertedId.toString(),
180
+ sender: message.senderName,
181
+ message: message.content,
182
+ timestamp: message.createdAt,
183
+ isOwn: true
184
+ };
185
+ res.success('Message sent successfully', 201, responseMessage);
186
+ } catch (error) {
187
+ console.error('Error sending message:', error);
188
+ res.error('Failed to send message', 500, error);
189
+ }
190
+ });
191
+
192
+ // Create a new conversation
193
+ router.post('/conversations', async (req, res) => {
194
+ try {
195
+ const {
196
+ participantId
197
+ } = req.body;
198
+ const userId = req.user.powrId;
199
+ const userAccess = req.user.access;
200
+ const projectId = req.projectId;
201
+ if (!participantId) {
202
+ return res.error('Participant ID is required', 400);
203
+ }
204
+ const db = await getDb();
205
+
206
+ // Check if user has permission to chat with this participant
207
+ const participant = await db.collection('users').findOne({
208
+ _id: new ObjectId(participantId)
209
+ }, {
210
+ projection: {
211
+ access: 1
212
+ }
213
+ });
214
+ if (!participant) {
215
+ return res.error('Participant not found', 404);
216
+ }
217
+
218
+ // Validate access permissions
219
+ let hasPermission = false;
220
+ if (userAccess === 100) {
221
+ // Admin can chat with everyone
222
+ hasPermission = true;
223
+ } else if (userAccess === 900) {
224
+ // Client can only chat with admins
225
+ hasPermission = participant.access === 100;
226
+ } else {
227
+ // Employee can chat with employees and admins
228
+ hasPermission = participant.access === 100 || participant.access === null;
229
+ }
230
+ if (!hasPermission) {
231
+ return res.error('You do not have permission to chat with this user', 403);
232
+ }
233
+
234
+ // Check if conversation already exists
235
+ const existingConversation = await db.collection('conversations').findOne({
236
+ participants: {
237
+ $all: [userId, participantId]
238
+ },
239
+ projectId: projectId
240
+ });
241
+ if (existingConversation) {
242
+ return res.success('Conversation already exists', 200, {
243
+ id: existingConversation._id.toString(),
244
+ message: 'Conversation already exists'
245
+ });
246
+ }
247
+ const conversation = {
248
+ participants: [userId, participantId],
249
+ projectId: projectId,
250
+ createdAt: new Date(),
251
+ updatedAt: new Date()
252
+ };
253
+ const result = await db.collection('conversations').insertOne(conversation);
254
+ res.success('Conversation created successfully', 201, {
255
+ id: result.insertedId.toString(),
256
+ message: 'Conversation created successfully'
257
+ });
258
+ } catch (error) {
259
+ console.error('Error creating conversation:', error);
260
+ res.error('Failed to create conversation', 500, error);
261
+ }
262
+ });
263
+
264
+ // Get users for starting new conversations
265
+ router.get('/users', async (req, res) => {
266
+ try {
267
+ const userId = req.user.powrId;
268
+ const userAccess = req.user.access;
269
+ const projectId = req.projectId;
270
+ console.log('Current user access level:', userAccess, 'Project:', projectId);
271
+ const db = await getDb();
272
+
273
+ // Get users from the current project
274
+ const projectUsers = await db.collection('profiles').find({
275
+ projectId: projectId
276
+ }).toArray();
277
+ const userIds = projectUsers.map(profile => profile.userId);
278
+ let userQuery = {
279
+ _id: {
280
+ $in: userIds.map(id => new ObjectId(id))
281
+ },
282
+ _id: {
283
+ $ne: new ObjectId(userId)
284
+ }
285
+ };
286
+
287
+ // Apply role-based filtering
288
+ if (userAccess === 100) {
289
+ // Admin can chat with everyone
290
+ console.log('Admin access - can chat with everyone');
291
+ } else if (userAccess === 900) {
292
+ // Client can only chat with admins
293
+ userQuery.access = 100;
294
+ console.log('Client access - can only chat with admins');
295
+ } else {
296
+ // Employee (access null) can chat with employees and admins
297
+ userQuery.$or = [{
298
+ access: 100
299
+ },
300
+ // Admins
301
+ {
302
+ access: null
303
+ } // Employees
304
+ ];
305
+ console.log('Employee access - can chat with employees and admins');
306
+ }
307
+
308
+ // Get filtered users
309
+ const users = await db.collection('users').find(userQuery, {
310
+ projection: {
311
+ fullName: 1,
312
+ username: 1,
313
+ avatar: 1,
314
+ _id: 1,
315
+ access: 1
316
+ }
317
+ }).toArray();
318
+ const formattedUsers = users.map(user => ({
319
+ id: user._id.toString(),
320
+ name: user.fullName || 'Unknown User',
321
+ username: user.username,
322
+ avatar: user.avatar,
323
+ access: user.access
324
+ }));
325
+ console.log('Available users for chat:', formattedUsers);
326
+ res.success('Users fetched successfully', 200, formattedUsers);
327
+ } catch (error) {
328
+ console.error('Error fetching users:', error);
329
+ res.error('Failed to fetch users', 500, error);
330
+ }
331
+ });
332
+ module.exports = router;
@@ -4,6 +4,9 @@ const express = require('express');
4
4
  const {
5
5
  injectProjectId
6
6
  } = require('../middleware/projectId');
7
+ const {
8
+ verifyToken
9
+ } = require('../middleware/jwtToken');
7
10
 
8
11
  // Import all route modules
9
12
  const commentsRoutes = require('./comments');
@@ -20,6 +23,7 @@ const authRoutes = require('./auth');
20
23
  const slidesRoutes = require('./slides');
21
24
  const notificationsRoutes = require('./notifications');
22
25
  const profilesRoutes = require('./profiles');
26
+ const chatRoutes = require('./chat');
23
27
  const createPowrRoutes = (options = {}) => {
24
28
  const router = express.Router();
25
29
 
@@ -38,7 +42,8 @@ const createPowrRoutes = (options = {}) => {
38
42
  router.use('/auth', authRoutes);
39
43
  router.use('/slides', slidesRoutes);
40
44
  router.use('/notifications', notificationsRoutes);
41
- router.use('/profiles', profilesRoutes);
45
+ router.use('/profiles', verifyToken, profilesRoutes);
46
+ router.use('/chat', verifyToken, chatRoutes);
42
47
  return router;
43
48
  };
44
49
  module.exports = {
package/package.json CHANGED
@@ -1,67 +1,67 @@
1
- {
2
- "name": "powr-sdk-api",
3
- "version": "2.4.6",
4
- "description": "Shared API core library for PowrStack projects",
5
- "main": "dist/index.js",
6
- "types": "dist/index.d.ts",
7
- "files": [
8
- "dist",
9
- "README.md"
10
- ],
11
- "scripts": {
12
- "test": "jest --passWithNoTests",
13
- "lint": "eslint .",
14
- "build": "babel src -d dist",
15
- "prepare": "npm run build",
16
- "prepublishOnly": "npm run test"
17
- },
18
- "keywords": [
19
- "api",
20
- "express",
21
- "middleware",
22
- "error-handling",
23
- "response-formatting",
24
- "logging",
25
- "swagger",
26
- "documentation"
27
- ],
28
- "author": "PowrStack",
29
- "license": "ISC",
30
- "repository": {
31
- "type": "git",
32
- "url": "git+https://github.com/powrstack/powr-sdk-api.git"
33
- },
34
- "bugs": {
35
- "url": "https://github.com/powrstack/powr-sdk-api/issues"
36
- },
37
- "homepage": "https://github.com/powrstack/powr-sdk-api#readme",
38
- "dependencies": {
39
- "@aws-sdk/client-s3": "^3.787.0",
40
- "@google-cloud/storage": "^7.16.0",
41
- "express": "^4.18.2",
42
- "jsonwebtoken": "^9.0.2",
43
- "swagger-jsdoc": "^6.2.8",
44
- "swagger-ui-express": "^5.0.0",
45
- "winston": "^3.17.0",
46
- "mongodb": "^6.3.0",
47
- "multer": "^1.4.5-lts.1",
48
- "bcrypt": "^5.1.1"
49
- },
50
- "devDependencies": {
51
- "@babel/cli": "^7.23.9",
52
- "@babel/core": "^7.24.0",
53
- "@babel/preset-env": "^7.24.0",
54
- "@types/express": "^4.17.21",
55
- "@types/swagger-jsdoc": "^6.0.4",
56
- "@types/swagger-ui-express": "^4.1.6",
57
- "eslint": "^8.57.0",
58
- "jest": "^29.7.0",
59
- "typescript": "^5.3.3"
60
- },
61
- "peerDependencies": {
62
- "express": "^4.18.2"
63
- },
64
- "engines": {
65
- "node": ">=14.0.0"
66
- }
67
- }
1
+ {
2
+ "name": "powr-sdk-api",
3
+ "version": "2.5.1",
4
+ "description": "Shared API core library for PowrStack projects",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist",
9
+ "README.md"
10
+ ],
11
+ "scripts": {
12
+ "test": "jest --passWithNoTests",
13
+ "lint": "eslint .",
14
+ "build": "babel src -d dist",
15
+ "prepare": "npm run build",
16
+ "prepublishOnly": "npm run test"
17
+ },
18
+ "keywords": [
19
+ "api",
20
+ "express",
21
+ "middleware",
22
+ "error-handling",
23
+ "response-formatting",
24
+ "logging",
25
+ "swagger",
26
+ "documentation"
27
+ ],
28
+ "author": "Lawazia Tech",
29
+ "license": "ISC",
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "git+https://github.com/powrstack/powr-sdk-api.git"
33
+ },
34
+ "bugs": {
35
+ "url": "https://github.com/powrstack/powr-sdk-api/issues"
36
+ },
37
+ "homepage": "https://github.com/powrstack/powr-sdk-api#readme",
38
+ "dependencies": {
39
+ "@aws-sdk/client-s3": "^3.787.0",
40
+ "@google-cloud/storage": "^7.16.0",
41
+ "express": "^4.18.2",
42
+ "jsonwebtoken": "^9.0.2",
43
+ "swagger-jsdoc": "^6.2.8",
44
+ "swagger-ui-express": "^5.0.0",
45
+ "winston": "^3.17.0",
46
+ "mongodb": "^6.3.0",
47
+ "multer": "^1.4.5-lts.1",
48
+ "bcrypt": "^5.1.1"
49
+ },
50
+ "devDependencies": {
51
+ "@babel/cli": "^7.23.9",
52
+ "@babel/core": "^7.24.0",
53
+ "@babel/preset-env": "^7.24.0",
54
+ "@types/express": "^4.17.21",
55
+ "@types/swagger-jsdoc": "^6.0.4",
56
+ "@types/swagger-ui-express": "^4.1.6",
57
+ "eslint": "^8.57.0",
58
+ "jest": "^29.7.0",
59
+ "typescript": "^5.3.3"
60
+ },
61
+ "peerDependencies": {
62
+ "express": "^4.18.2"
63
+ },
64
+ "engines": {
65
+ "node": ">=14.0.0"
66
+ }
67
+ }