powr-sdk-api 2.4.5 → 2.5.0

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.
@@ -55,7 +55,8 @@ router.post("/register", async (req, res) => {
55
55
  }]
56
56
  };
57
57
  console.log(q);
58
- const existingUser = await getDb().collection("users").findOne(q);
58
+ const db = await getDb();
59
+ const existingUser = await db.collection("users").findOne(q);
59
60
  if (existingUser) {
60
61
  return res.status(400).json({
61
62
  success: false,
@@ -72,7 +73,7 @@ router.post("/register", async (req, res) => {
72
73
  password: hashedPassword,
73
74
  createdAt: new Date()
74
75
  };
75
- const result = await getDb().collection("users").insertOne(newUser);
76
+ const result = await db.collection("users").insertOne(newUser);
76
77
  if (projectId) {
77
78
  const newProfile = {
78
79
  userId: result.insertedId,
@@ -80,7 +81,7 @@ router.post("/register", async (req, res) => {
80
81
  createdAt: new Date(),
81
82
  updatedAt: new Date()
82
83
  };
83
- await getDb().collection("profiles").insertOne(newProfile);
84
+ await db.collection("profiles").insertOne(newProfile);
84
85
  }
85
86
  return res.status(201).json({
86
87
  success: true,
@@ -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;
@@ -20,6 +20,7 @@ const authRoutes = require('./auth');
20
20
  const slidesRoutes = require('./slides');
21
21
  const notificationsRoutes = require('./notifications');
22
22
  const profilesRoutes = require('./profiles');
23
+ const chatRoutes = require('./chat');
23
24
  const createPowrRoutes = (options = {}) => {
24
25
  const router = express.Router();
25
26
 
@@ -39,6 +40,7 @@ const createPowrRoutes = (options = {}) => {
39
40
  router.use('/slides', slidesRoutes);
40
41
  router.use('/notifications', notificationsRoutes);
41
42
  router.use('/profiles', profilesRoutes);
43
+ router.use('/chat', chatRoutes);
42
44
  return router;
43
45
  };
44
46
  module.exports = {
@@ -22,7 +22,8 @@ router.get('/', async (req, res) => {
22
22
  message: 'projectId is required.'
23
23
  });
24
24
  }
25
- const invoiceData = await getDb().collection("invoices").find(query).toArray();
25
+ const db = await getDb();
26
+ const invoiceData = await db.collection("invoices").find(query).toArray();
26
27
  return res.status(200).json({
27
28
  success: true,
28
29
  invoices: invoiceData
@@ -50,7 +51,8 @@ router.get('/:invoiceId', async (req, res) => {
50
51
  });
51
52
  }
52
53
  const _id = new ObjectId(invoiceId);
53
- const invoiceData = await getDb().collection("invoices").findOne({
54
+ const db = await getDb();
55
+ const invoiceData = await db.collection("invoices").findOne({
54
56
  _id,
55
57
  projectId
56
58
  });
@@ -95,7 +97,8 @@ router.post('/', async (req, res) => {
95
97
  };
96
98
  invoice.projectId = projectId;
97
99
  try {
98
- const result = await getDb().collection("invoices").insertOne(invoice);
100
+ const db = await getDb();
101
+ const result = await db.collection("invoices").insertOne(invoice);
99
102
  if (result && result.insertedId) {
100
103
  return res.status(201).json({
101
104
  success: true,
@@ -144,7 +147,8 @@ router.put('/:invoiceId', async (req, res) => {
144
147
  }
145
148
  const updateData = req.body;
146
149
  try {
147
- const result = await getDb().collection("invoices").updateOne(filter, {
150
+ const db = await getDb();
151
+ const result = await db.collection("invoices").updateOne(filter, {
148
152
  $set: updateData
149
153
  });
150
154
  return res.status(200).json({
@@ -37,7 +37,8 @@ router.post('/', async (req, res) => {
37
37
  query.contentId = contentId;
38
38
  setObj.contentId = contentId;
39
39
  }
40
- const response = await getDb().collection('likes').updateOne(query, {
40
+ const db = await getDb();
41
+ const response = await db.collection('likes').updateOne(query, {
41
42
  $set: setObj
42
43
  }, {
43
44
  upsert: true
@@ -73,7 +74,8 @@ router.get('/', async (req, res) => {
73
74
  if (contentId) {
74
75
  query.contentId = contentId;
75
76
  }
76
- const likes = await getDb().collection('likes').find(query).toArray();
77
+ const db = await getDb();
78
+ const likes = await db.collection('likes').find(query).toArray();
77
79
  const likesCount = likes.filter(like => like.liked === true).length;
78
80
  const unlikesCount = likes.filter(like => like.liked === false).length;
79
81
  return res.status(200).json({
@@ -104,7 +106,8 @@ router.get('/:likeId', async (req, res) => {
104
106
  };
105
107
  if (projectId) filter.projectId = projectId;
106
108
  if (contentId) filter.contentId = contentId;
107
- const like = await getDb().collection('likes').findOne(filter);
109
+ const db = await getDb();
110
+ const like = await db.collection('likes').findOne(filter);
108
111
  if (!like) {
109
112
  return res.status(404).json({
110
113
  success: false,
@@ -17,7 +17,8 @@ router.get('/', async (req, res) => {
17
17
  projectId
18
18
  };
19
19
  console.log("Filter.....:", query);
20
- const notificationData = await getDb().collection('notifications').find(query).toArray();
20
+ const db = await getDb();
21
+ const notificationData = await db.collection('notifications').find(query).toArray();
21
22
  if (!notificationData || notificationData.length === 0) {
22
23
  console.log("Data not found.");
23
24
  return res.status(200).json({
@@ -64,7 +65,8 @@ router.post('/', async (req, res) => {
64
65
  createdAt: new Date()
65
66
  };
66
67
  try {
67
- const result = await getDb().collection('notifications').insertOne(notificationData);
68
+ const db = await getDb();
69
+ const result = await db.collection('notifications').insertOne(notificationData);
68
70
  if (result && result.insertedId) {
69
71
  console.log("Notification added with ID:", result.insertedId);
70
72
 
@@ -21,7 +21,8 @@ router.get('/', async (req, res) => {
21
21
  message: 'projectId is required.'
22
22
  });
23
23
  }
24
- const routesData = await getDb().collection("routes").find(query).toArray();
24
+ const db = await getDb();
25
+ const routesData = await db.collection("routes").find(query).toArray();
25
26
  return res.status(200).json({
26
27
  success: true,
27
28
  allroutes: routesData
@@ -55,7 +56,8 @@ router.post('/', async (req, res) => {
55
56
  message: 'Request body is empty or invalid.'
56
57
  });
57
58
  }
58
- const result = await getDb().collection('routes').insertOne(newRoute);
59
+ const db = await getDb();
60
+ const result = await db.collection('routes').insertOne(newRoute);
59
61
  return res.status(201).json({
60
62
  success: true,
61
63
  entry: result.ops ? result.ops[0] : newRoute
@@ -97,14 +99,15 @@ router.put('/:route', async (req, res) => {
97
99
  route,
98
100
  projectId
99
101
  };
100
- const existingRoute = await getDb().collection('routes').findOne(query);
102
+ const db = await getDb();
103
+ const existingRoute = await db.collection('routes').findOne(query);
101
104
  if (!existingRoute) {
102
105
  return res.status(404).json({
103
106
  success: false,
104
107
  message: 'Route not found.'
105
108
  });
106
109
  }
107
- const result = await getDb().collection('routes').updateOne(query, {
110
+ const result = await db.collection('routes').updateOne(query, {
108
111
  $set: {
109
112
  code
110
113
  }
@@ -115,7 +118,7 @@ router.put('/:route', async (req, res) => {
115
118
  message: 'Route not found.'
116
119
  });
117
120
  }
118
- const updatedRoute = await getDb().collection('routes').findOne(query);
121
+ const updatedRoute = await db.collection('routes').findOne(query);
119
122
  return res.status(200).json({
120
123
  success: true,
121
124
  entry: updatedRoute
package/package.json CHANGED
@@ -1,67 +1,67 @@
1
- {
2
- "name": "powr-sdk-api",
3
- "version": "2.4.5",
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.0",
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
+ }