powr-sdk-api 3.1.4 → 3.1.6

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.
@@ -6,13 +6,11 @@ const config = require('../config');
6
6
  * Middleware to inject and validate projectId into requests
7
7
  * @param {Object} options - Configuration options
8
8
  * @param {boolean} options.isCentralService - Whether this is a central service (like powr-base-cloud)
9
- * @param {boolean} options.requireProjectId - Whether to require projectId validation (default: true)
10
9
  * @param {Array} options.excludePaths - Array of paths to exclude from projectId validation
11
10
  * @returns {Function} Express middleware function
12
11
  */
13
12
  const injectProjectId = (options = {}) => {
14
13
  const isCentralService = options.isCentralService || false;
15
- const requireProjectId = options.requireProjectId !== false; // Default to true
16
14
  const excludePaths = options.excludePaths || ['/auth/login', '/auth/register', '/health'];
17
15
  return (req, res, next) => {
18
16
  try {
@@ -23,21 +21,14 @@ const injectProjectId = (options = {}) => {
23
21
  }
24
22
  if (isCentralService) {
25
23
  // For central services, get projectId from request
26
- const projectId = req.query.projectId || req.body.projectId;
27
- if (requireProjectId && !projectId) {
28
- return res.status(400).json({
29
- success: false,
30
- message: 'projectId is required'
31
- });
32
- }
33
- req.projectId = projectId;
24
+ req.projectId = req.query.projectId || req.body.projectId;
34
25
  } else {
35
26
  // For individual APIs, use config.projectId
36
27
  req.projectId = config.projectId;
37
28
  }
38
29
 
39
- // Validate projectId if required
40
- if (requireProjectId && !req.projectId) {
30
+ // Validate projectId (always required except for excluded paths)
31
+ if (!req.projectId) {
41
32
  return res.status(400).json({
42
33
  success: false,
43
34
  message: 'projectId is required'
@@ -0,0 +1,346 @@
1
+ "use strict";
2
+
3
+ const express = require('express');
4
+ const router = express.Router();
5
+ const {
6
+ getDb
7
+ } = require('../services/mongo');
8
+ const {
9
+ ObjectId
10
+ } = require('mongodb');
11
+
12
+ // Create new post
13
+ router.post('/', async (req, res) => {
14
+ try {
15
+ const {
16
+ content,
17
+ media,
18
+ isPublic = true,
19
+ tags,
20
+ mentions
21
+ } = req.body;
22
+ const projectId = req.projectId;
23
+ const userId = req.user.id;
24
+ if (!content && (!media || media.length === 0)) {
25
+ return res.status(400).json({
26
+ success: false,
27
+ message: 'Content or media is required'
28
+ });
29
+ }
30
+ const db = await getDb();
31
+ const newPost = {
32
+ projectId,
33
+ authorId: new ObjectId(userId),
34
+ content: content || '',
35
+ media: media || [],
36
+ likesCount: 0,
37
+ commentsCount: 0,
38
+ isPublic,
39
+ tags: tags || [],
40
+ mentions: mentions ? mentions.map(id => new ObjectId(id)) : [],
41
+ createdAt: new Date(),
42
+ updatedAt: new Date()
43
+ };
44
+ const result = await db.collection('feeds').insertOne(newPost);
45
+
46
+ // Populate author info
47
+ const author = await db.collection('users').findOne({
48
+ _id: new ObjectId(userId)
49
+ }, {
50
+ projection: {
51
+ name: 1,
52
+ email: 1,
53
+ avatar: 1
54
+ }
55
+ });
56
+ const createdPost = {
57
+ ...newPost,
58
+ _id: result.insertedId,
59
+ author
60
+ };
61
+ return res.status(201).json({
62
+ success: true,
63
+ message: 'Post created successfully',
64
+ data: createdPost
65
+ });
66
+ } catch (error) {
67
+ console.error('Error creating post:', error);
68
+ return res.status(500).json({
69
+ success: false,
70
+ message: 'Failed to create post'
71
+ });
72
+ }
73
+ });
74
+
75
+ // Get feed with pagination and filters
76
+ router.get('/', async (req, res) => {
77
+ try {
78
+ const {
79
+ page = 1,
80
+ limit = 10,
81
+ userId,
82
+ tag,
83
+ search
84
+ } = req.query;
85
+ const projectId = req.projectId;
86
+ const skip = (parseInt(page) - 1) * parseInt(limit);
87
+ const query = {
88
+ projectId
89
+ };
90
+ if (userId) {
91
+ query.authorId = new ObjectId(userId);
92
+ }
93
+ if (tag) {
94
+ query.tags = {
95
+ $in: [tag]
96
+ };
97
+ }
98
+ if (search) {
99
+ query.$or = [{
100
+ content: {
101
+ $regex: search,
102
+ $options: 'i'
103
+ }
104
+ }, {
105
+ tags: {
106
+ $in: [new RegExp(search, 'i')]
107
+ }
108
+ }];
109
+ }
110
+ const db = await getDb();
111
+
112
+ // Get posts with author info
113
+ const posts = await db.collection('feeds').aggregate([{
114
+ $match: query
115
+ }, {
116
+ $sort: {
117
+ createdAt: -1
118
+ }
119
+ }, {
120
+ $skip: skip
121
+ }, {
122
+ $limit: parseInt(limit)
123
+ }, {
124
+ $lookup: {
125
+ from: 'users',
126
+ localField: 'authorId',
127
+ foreignField: '_id',
128
+ as: 'author'
129
+ }
130
+ }, {
131
+ $unwind: '$author'
132
+ }, {
133
+ $project: {
134
+ 'author.password': 0,
135
+ 'author.access': 0
136
+ }
137
+ }]).toArray();
138
+ const total = await db.collection('feeds').countDocuments(query);
139
+ return res.json({
140
+ success: true,
141
+ data: posts,
142
+ pagination: {
143
+ page: parseInt(page),
144
+ limit: parseInt(limit),
145
+ total,
146
+ pages: Math.ceil(total / parseInt(limit))
147
+ }
148
+ });
149
+ } catch (error) {
150
+ console.error('Error fetching feed:', error);
151
+ return res.status(500).json({
152
+ success: false,
153
+ message: 'Failed to fetch feed'
154
+ });
155
+ }
156
+ });
157
+
158
+ // Get single post
159
+ router.get('/:id', async (req, res) => {
160
+ try {
161
+ const {
162
+ id
163
+ } = req.params;
164
+ const projectId = req.projectId;
165
+ const db = await getDb();
166
+ const post = await db.collection('feeds').aggregate([{
167
+ $match: {
168
+ _id: new ObjectId(id),
169
+ projectId
170
+ }
171
+ }, {
172
+ $lookup: {
173
+ from: 'users',
174
+ localField: 'authorId',
175
+ foreignField: '_id',
176
+ as: 'author'
177
+ }
178
+ }, {
179
+ $unwind: '$author'
180
+ }, {
181
+ $project: {
182
+ 'author.password': 0,
183
+ 'author.access': 0
184
+ }
185
+ }]).next();
186
+ if (!post) {
187
+ return res.status(404).json({
188
+ success: false,
189
+ message: 'Post not found'
190
+ });
191
+ }
192
+ return res.json({
193
+ success: true,
194
+ data: post
195
+ });
196
+ } catch (error) {
197
+ console.error('Error fetching post:', error);
198
+ return res.status(500).json({
199
+ success: false,
200
+ message: 'Failed to fetch post'
201
+ });
202
+ }
203
+ });
204
+
205
+ // Update post
206
+ router.put('/:id', async (req, res) => {
207
+ try {
208
+ const {
209
+ id
210
+ } = req.params;
211
+ const {
212
+ content,
213
+ media,
214
+ isPublic,
215
+ tags,
216
+ mentions
217
+ } = req.body;
218
+ const projectId = req.projectId;
219
+ const userId = req.user.id;
220
+ const db = await getDb();
221
+ const post = await db.collection('feeds').findOne({
222
+ _id: new ObjectId(id),
223
+ projectId,
224
+ authorId: new ObjectId(userId)
225
+ });
226
+ if (!post) {
227
+ return res.status(404).json({
228
+ success: false,
229
+ message: 'Post not found or unauthorized'
230
+ });
231
+ }
232
+ const updateData = {
233
+ updatedAt: new Date()
234
+ };
235
+ if (content !== undefined) updateData.content = content;
236
+ if (media !== undefined) updateData.media = media;
237
+ if (isPublic !== undefined) updateData.isPublic = isPublic;
238
+ if (tags !== undefined) updateData.tags = tags;
239
+ if (mentions !== undefined) updateData.mentions = mentions.map(id => new ObjectId(id));
240
+ await db.collection('feeds').updateOne({
241
+ _id: new ObjectId(id)
242
+ }, {
243
+ $set: updateData
244
+ });
245
+ return res.json({
246
+ success: true,
247
+ message: 'Post updated successfully'
248
+ });
249
+ } catch (error) {
250
+ console.error('Error updating post:', error);
251
+ return res.status(500).json({
252
+ success: false,
253
+ message: 'Failed to update post'
254
+ });
255
+ }
256
+ });
257
+
258
+ // Delete post
259
+ router.delete('/:id', async (req, res) => {
260
+ try {
261
+ const {
262
+ id
263
+ } = req.params;
264
+ const projectId = req.projectId;
265
+ const userId = req.user.id;
266
+ const db = await getDb();
267
+ const post = await db.collection('feeds').findOne({
268
+ _id: new ObjectId(id),
269
+ projectId,
270
+ authorId: new ObjectId(userId)
271
+ });
272
+ if (!post) {
273
+ return res.status(404).json({
274
+ success: false,
275
+ message: 'Post not found or unauthorized'
276
+ });
277
+ }
278
+ await db.collection('feeds').deleteOne({
279
+ _id: new ObjectId(id)
280
+ });
281
+ return res.json({
282
+ success: true,
283
+ message: 'Post deleted successfully'
284
+ });
285
+ } catch (error) {
286
+ console.error('Error deleting post:', error);
287
+ return res.status(500).json({
288
+ success: false,
289
+ message: 'Failed to delete post'
290
+ });
291
+ }
292
+ });
293
+
294
+ // Like/unlike post
295
+ router.post('/:id/like', async (req, res) => {
296
+ try {
297
+ const {
298
+ id
299
+ } = req.params;
300
+ const {
301
+ liked
302
+ } = req.body;
303
+ const projectId = req.projectId;
304
+ const userId = req.user.id;
305
+ const db = await getDb();
306
+
307
+ // Update like count
308
+ const updateOperation = liked ? {
309
+ $inc: {
310
+ likesCount: 1
311
+ }
312
+ } : {
313
+ $inc: {
314
+ likesCount: -1
315
+ }
316
+ };
317
+ await db.collection('feeds').updateOne({
318
+ _id: new ObjectId(id),
319
+ projectId
320
+ }, updateOperation);
321
+
322
+ // Update user's like status
323
+ await db.collection('likes').updateOne({
324
+ userId: new ObjectId(userId),
325
+ contentId: id,
326
+ projectId
327
+ }, {
328
+ $set: {
329
+ liked
330
+ }
331
+ }, {
332
+ upsert: true
333
+ });
334
+ return res.json({
335
+ success: true,
336
+ message: liked ? 'Post liked' : 'Post unliked'
337
+ });
338
+ } catch (error) {
339
+ console.error('Error updating like:', error);
340
+ return res.status(500).json({
341
+ success: false,
342
+ message: 'Failed to update like'
343
+ });
344
+ }
345
+ });
346
+ module.exports = router;
@@ -52,12 +52,6 @@ router.get('/:formName', async (req, res) => {
52
52
  formName
53
53
  } = req.params;
54
54
  const projectId = req.projectId;
55
- if (!projectId) {
56
- return res.status(400).json({
57
- success: false,
58
- message: 'projectId is required'
59
- });
60
- }
61
55
  const db = await getDb();
62
56
  const collection = db.collection("powrForm");
63
57
  const formData = await collection.findOne({
@@ -170,15 +164,7 @@ router.get('/getCount/:formName', async (req, res) => {
170
164
  // POST /create-form - Upload JSON file and store form data
171
165
  router.post('/create-form', upload.single('jsonFile'), async (req, res) => {
172
166
  try {
173
- const {
174
- projectId
175
- } = req.query;
176
- if (!projectId) {
177
- return res.status(400).json({
178
- success: false,
179
- message: 'projectId is required as query parameter'
180
- });
181
- }
167
+ const projectId = req.projectId;
182
168
  if (!req.file) {
183
169
  return res.status(400).json({
184
170
  success: false,
@@ -25,6 +25,7 @@ const slidesRoutes = require('./slides');
25
25
  const notificationsRoutes = require('./notifications');
26
26
  const profilesRoutes = require('./profiles');
27
27
  const chatRoutes = require('./chat');
28
+ const feedsRoutes = require('./feeds');
28
29
 
29
30
  // Synchronous router creation
30
31
  const createPowrRoutes = (options = {}) => {
@@ -47,6 +48,7 @@ const createPowrRoutes = (options = {}) => {
47
48
  router.use('/notifications', verifyToken, notificationsRoutes);
48
49
  router.use('/profiles', verifyToken, profilesRoutes);
49
50
  router.use('/chat', verifyToken, chatRoutes);
51
+ router.use('/feeds', verifyToken, feedsRoutes);
50
52
  return router;
51
53
  };
52
54
 
@@ -34,12 +34,6 @@ router.get('/:invoiceId', async (req, res) => {
34
34
  } = req.params;
35
35
  const projectId = req.projectId;
36
36
  try {
37
- if (!projectId) {
38
- return res.status(400).json({
39
- success: false,
40
- message: 'projectId is required.'
41
- });
42
- }
43
37
  const _id = new ObjectId(invoiceId);
44
38
  const db = await getDb();
45
39
  const invoiceData = await db.collection("invoices").findOne({
@@ -68,12 +62,6 @@ router.post('/', async (req, res) => {
68
62
  serviceSection,
69
63
  paymentSection
70
64
  } = req.body;
71
- if (!projectId) {
72
- return res.status(400).json({
73
- success: false,
74
- message: "projectId is required."
75
- });
76
- }
77
65
  const invoice = {
78
66
  invoiceNumber,
79
67
  dateOfIssue,
@@ -113,12 +101,6 @@ router.put('/:invoiceId', async (req, res) => {
113
101
  invoiceId
114
102
  } = req.params;
115
103
  const projectId = req.projectId;
116
- if (!projectId) {
117
- return res.status(400).json({
118
- success: false,
119
- message: "projectId is required."
120
- });
121
- }
122
104
  let filter;
123
105
  try {
124
106
  filter = {
@@ -16,13 +16,13 @@ router.post('/', async (req, res) => {
16
16
  } = req.body;
17
17
  const {
18
18
  userId,
19
- projectId,
20
19
  contentId
21
20
  } = req.query;
22
- if (!userId || typeof liked !== 'boolean' || !projectId) {
21
+ const projectId = req.projectId;
22
+ if (!userId || typeof liked !== 'boolean') {
23
23
  return res.status(400).json({
24
24
  success: false,
25
- message: 'userId and projectId are required in query, liked (boolean) is required in body.'
25
+ message: 'userId is required in query, liked (boolean) is required in body.'
26
26
  });
27
27
  }
28
28
  try {
@@ -58,15 +58,9 @@ router.post('/', async (req, res) => {
58
58
  });
59
59
  router.get('/', async (req, res) => {
60
60
  const {
61
- projectId,
62
61
  contentId
63
62
  } = req.query;
64
- if (!projectId) {
65
- return res.status(400).json({
66
- success: false,
67
- message: 'projectId is required in query.'
68
- });
69
- }
63
+ const projectId = req.projectId;
70
64
  try {
71
65
  const query = {
72
66
  projectId
@@ -97,14 +91,14 @@ router.get('/:likeId', async (req, res) => {
97
91
  likeId
98
92
  } = req.params;
99
93
  const {
100
- projectId,
101
94
  contentId
102
95
  } = req.query;
96
+ const projectId = req.projectId;
103
97
  try {
104
98
  const filter = {
105
- userId: new ObjectId(likeId)
99
+ userId: new ObjectId(likeId),
100
+ projectId
106
101
  };
107
- if (projectId) filter.projectId = projectId;
108
102
  if (contentId) filter.contentId = contentId;
109
103
  const db = await getDb();
110
104
  const like = await db.collection('likes').findOne(filter);
@@ -65,12 +65,6 @@ router.get("/", async (req, res) => {
65
65
  userId
66
66
  } = req.query;
67
67
  const projectId = req.projectId;
68
- if (!projectId) {
69
- return res.status(400).json({
70
- success: false,
71
- message: "projectId is required."
72
- });
73
- }
74
68
  try {
75
69
  const query = {
76
70
  projectId
@@ -106,10 +100,10 @@ router.delete("/", async (req, res) => {
106
100
  ratingId
107
101
  } = req.query;
108
102
  const projectId = req.projectId;
109
- if (!ratingId || !projectId) {
103
+ if (!ratingId) {
110
104
  return res.status(400).json({
111
105
  success: false,
112
- message: "Both ratingId and projectId are required to delete a rating."
106
+ message: "ratingId is required to delete a rating."
113
107
  });
114
108
  }
115
109
  try {
@@ -143,10 +137,10 @@ router.get("/average", async (req, res) => {
143
137
  itemId
144
138
  } = req.query;
145
139
  const projectId = req.projectId;
146
- if (!projectId || !itemId) {
140
+ if (!itemId) {
147
141
  return res.status(400).json({
148
142
  success: false,
149
- message: "Both projectId and itemId are required."
143
+ message: "itemId is required."
150
144
  });
151
145
  }
152
146
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "powr-sdk-api",
3
- "version": "3.1.4",
3
+ "version": "3.1.6",
4
4
  "description": "Shared API core library for PowrStack projects",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",