powr-sdk-api 4.7.1 → 4.7.3

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/index.js CHANGED
@@ -22,7 +22,8 @@ const {
22
22
  initializeTools,
23
23
  executeTasks,
24
24
  executeTool,
25
- createTask
25
+ createTask,
26
+ createActivityFeedItem
26
27
  } = require("./managers");
27
28
  const {
28
29
  verifyToken
@@ -44,5 +45,6 @@ module.exports = {
44
45
  executeTasks,
45
46
  executeTool,
46
47
  createTask,
48
+ createActivityFeedItem,
47
49
  getPowrDb
48
50
  };
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+
3
+ const {
4
+ getDb
5
+ } = require("../services/mongo");
6
+ const {
7
+ ObjectId
8
+ } = require("mongodb");
9
+
10
+ /**
11
+ * Create a feed activity entry in the activities collection.
12
+ * Used by host apps (e.g. Spriny) in-process and by POST /activities (feed shape).
13
+ *
14
+ * @param {Object} params - action, userId, projectId?, targetType?, targetId?, metadata?, projectName?, projectSlug?
15
+ * @returns {Promise<{ success: boolean, id?: string, message?: string }>}
16
+ */
17
+ async function createActivityFeedItem(params) {
18
+ try {
19
+ const {
20
+ action,
21
+ userId,
22
+ projectId,
23
+ targetType,
24
+ targetId,
25
+ metadata = {},
26
+ projectName,
27
+ projectSlug
28
+ } = params;
29
+ if (!action || !userId) {
30
+ return {
31
+ success: false,
32
+ message: "action and userId are required"
33
+ };
34
+ }
35
+ const doc = {
36
+ action,
37
+ userId: new ObjectId(userId.toString()),
38
+ projectId: projectId ? new ObjectId(projectId.toString()) : null,
39
+ targetType: targetType || null,
40
+ targetId: targetId ? new ObjectId(targetId.toString()) : null,
41
+ metadata: metadata || {},
42
+ projectName: projectName || null,
43
+ projectSlug: projectSlug || null,
44
+ createdAt: new Date()
45
+ };
46
+ const db = await getDb();
47
+ const result = await db.collection("activities").insertOne(doc);
48
+ return {
49
+ success: true,
50
+ id: result.insertedId.toString()
51
+ };
52
+ } catch (error) {
53
+ console.error("createActivityFeedItem error:", error);
54
+ return {
55
+ success: false,
56
+ message: error.message
57
+ };
58
+ }
59
+ }
60
+ module.exports = {
61
+ createActivityFeedItem
62
+ };
@@ -3,6 +3,9 @@
3
3
  const functionsManager = require('./functions');
4
4
  const toolsManager = require('./tools');
5
5
  const scheduledTasksManager = require('./tasks');
6
+ const {
7
+ createActivityFeedItem
8
+ } = require('./activities');
6
9
 
7
10
  // Async Functions initialization function
8
11
  const initializeFunctions = async (options = {}) => {
@@ -33,5 +36,6 @@ module.exports = {
33
36
  initializeTools,
34
37
  executeTasks,
35
38
  executeTool,
36
- createTask
39
+ createTask,
40
+ createActivityFeedItem
37
41
  };
@@ -5,21 +5,119 @@ const router = express.Router();
5
5
  const {
6
6
  getDb
7
7
  } = require("../services/mongo");
8
+ const {
9
+ ObjectId
10
+ } = require("mongodb");
11
+ const {
12
+ createActivityFeedItem
13
+ } = require("../managers/activities");
8
14
 
9
- // Get all activities
15
+ /**
16
+ * GET /activities
17
+ * - Content-scoped (existing): ?contentId= → returns docs with projectId + contentId (shape: activity, createdAt).
18
+ * - Feed: ?projectId=...&from=&to=&action=&limit= → returns feed docs (action, userId, metadata, etc.) from same collection.
19
+ * Feed docs have "action" field; content-scoped docs have "contentId" and "activity".
20
+ */
10
21
  router.get("/", async (req, res) => {
11
22
  try {
12
23
  const {
13
- contentId
24
+ contentId,
25
+ from,
26
+ to,
27
+ action,
28
+ limit
14
29
  } = req.query;
15
- const projectId = req.projectId; // Use middleware-injected projectId
16
-
30
+ const projectId = req.projectId;
31
+ const isFeedRequest = from !== undefined || to !== undefined || req.query.projectId && contentId === undefined;
32
+ if (isFeedRequest) {
33
+ const projectIds = [].concat(req.query.projectId || []).filter(Boolean);
34
+ if (projectIds.length === 0) {
35
+ return res.json({
36
+ success: true,
37
+ activities: []
38
+ });
39
+ }
40
+ const query = {
41
+ projectId: {
42
+ $in: projectIds.map(id => new ObjectId(id))
43
+ },
44
+ action: {
45
+ $exists: true
46
+ }
47
+ };
48
+ if (from || to) {
49
+ query.createdAt = {};
50
+ if (from) query.createdAt.$gte = new Date(from);
51
+ if (to) query.createdAt.$lte = new Date(to);
52
+ }
53
+ if (action) query.action = action;
54
+ const db = await getDb();
55
+ const activities = await db.collection("activities").find(query).sort({
56
+ createdAt: -1
57
+ }).limit(Math.min(parseInt(limit, 10) || 50, 100)).toArray();
58
+ const actorIds = [...new Set(activities.map(a => {
59
+ var _a$userId;
60
+ return (_a$userId = a.userId) === null || _a$userId === void 0 ? void 0 : _a$userId.toString();
61
+ }).filter(Boolean))];
62
+ let actorDetails = {};
63
+ if (actorIds.length > 0) {
64
+ const actors = await db.collection("users").find({
65
+ _id: {
66
+ $in: actorIds.map(id => new ObjectId(id))
67
+ }
68
+ }).project({
69
+ _id: 1,
70
+ fullName: 1
71
+ }).toArray();
72
+ actorDetails = actors.reduce((acc, u) => {
73
+ acc[u._id.toString()] = u.fullName || "Unknown";
74
+ return acc;
75
+ }, {});
76
+ }
77
+ const assigneeIds = activities.filter(a => {
78
+ var _a$metadata;
79
+ return (_a$metadata = a.metadata) === null || _a$metadata === void 0 ? void 0 : _a$metadata.assigneeId;
80
+ }).map(a => a.metadata.assigneeId);
81
+ let assigneeDetails = {};
82
+ if (assigneeIds.length > 0) {
83
+ const assignees = await db.collection("users").find({
84
+ _id: {
85
+ $in: assigneeIds.map(id => new ObjectId(id))
86
+ }
87
+ }).project({
88
+ _id: 1,
89
+ fullName: 1
90
+ }).toArray();
91
+ assigneeDetails = assignees.reduce((acc, u) => {
92
+ acc[u._id.toString()] = u.fullName || "Unknown";
93
+ return acc;
94
+ }, {});
95
+ }
96
+ const enriched = activities.map(a => {
97
+ var _a$userId2, _a$projectId, _a$userId3, _a$metadata2;
98
+ return {
99
+ ...a,
100
+ _id: a._id.toString(),
101
+ userId: (_a$userId2 = a.userId) === null || _a$userId2 === void 0 ? void 0 : _a$userId2.toString(),
102
+ projectId: (_a$projectId = a.projectId) === null || _a$projectId === void 0 ? void 0 : _a$projectId.toString(),
103
+ actorName: actorDetails[(_a$userId3 = a.userId) === null || _a$userId3 === void 0 ? void 0 : _a$userId3.toString()] || "Unknown",
104
+ assigneeName: (_a$metadata2 = a.metadata) !== null && _a$metadata2 !== void 0 && _a$metadata2.assigneeId ? assigneeDetails[a.metadata.assigneeId] || "Unknown" : null,
105
+ projectName: a.projectName || null,
106
+ projectSlug: a.projectSlug || null
107
+ };
108
+ });
109
+ return res.json({
110
+ success: true,
111
+ activities: enriched
112
+ });
113
+ }
17
114
  const query = {
18
- projectId
115
+ activity: {
116
+ $exists: true
117
+ }
19
118
  };
20
- if (contentId) {
21
- query.contentId = contentId;
22
- }
119
+ if (projectId) query.projectId = new ObjectId(projectId);
120
+ if (contentId) query.contentId = contentId;
23
121
  const db = await getDb();
24
122
  const activities = await db.collection("activities").find(query).toArray();
25
123
  return res.json({
@@ -35,15 +133,36 @@ router.get("/", async (req, res) => {
35
133
  }
36
134
  });
37
135
 
38
- // Create new activity
136
+ /**
137
+ * POST /activities
138
+ * - Feed: body has action, userId → insert feed doc into activities collection.
139
+ * - Content-scoped (existing): body has contentId, activity → insert content-scoped doc.
140
+ */
39
141
  router.post("/", async (req, res) => {
40
142
  try {
143
+ const body = req.body;
144
+ const projectId = req.projectId;
145
+ if (body.action && body.userId) {
146
+ const payload = {
147
+ ...body,
148
+ projectId: body.projectId || projectId
149
+ };
150
+ const result = await createActivityFeedItem(payload);
151
+ if (!result.success) {
152
+ return res.status(400).json({
153
+ success: false,
154
+ message: result.message
155
+ });
156
+ }
157
+ return res.status(201).json({
158
+ success: true,
159
+ id: result.id
160
+ });
161
+ }
41
162
  const {
42
163
  contentId,
43
164
  activity
44
- } = req.body;
45
- const projectId = req.projectId; // Use middleware-injected projectId
46
-
165
+ } = body;
47
166
  if (!contentId || !activity) {
48
167
  return res.status(400).json({
49
168
  success: false,
@@ -51,7 +170,7 @@ router.post("/", async (req, res) => {
51
170
  });
52
171
  }
53
172
  const activityData = {
54
- projectId,
173
+ projectId: projectId ? new ObjectId(projectId) : undefined,
55
174
  contentId,
56
175
  activity,
57
176
  createdAt: new Date()
@@ -41,24 +41,37 @@ router.get("/", async (req, res) => {
41
41
  // Create new comment
42
42
  router.post("/", async (req, res) => {
43
43
  try {
44
+ var _req$user, _req$user2;
44
45
  const {
45
46
  contentId,
46
47
  comment,
48
+ files,
47
49
  ...customFields
48
50
  } = req.body;
49
- const projectId = req.projectId; // Use middleware-injected projectId
50
-
51
- if (!contentId || !comment) {
51
+ const projectId = req.projectId;
52
+ const userId = (_req$user = req.user) === null || _req$user === void 0 ? void 0 : _req$user.powrId;
53
+ const userName = (_req$user2 = req.user) === null || _req$user2 === void 0 ? void 0 : _req$user2.fullName;
54
+ if (!contentId || !comment && (!files || !files.length)) {
52
55
  return res.status(400).json({
53
56
  success: false,
54
- message: "Content ID and comment are required"
57
+ message: "Content ID and comment (or files) are required"
55
58
  });
56
59
  }
57
60
  const commentData = {
58
61
  projectId,
59
62
  contentId,
60
- comment,
63
+ comment: comment || "",
64
+ ...(files && {
65
+ files
66
+ }),
61
67
  ...customFields,
68
+ userId,
69
+ userName: userName || null,
70
+ createdBy: userId ? {
71
+ id: userId,
72
+ _id: userId,
73
+ name: userName
74
+ } : null,
62
75
  createdAt: new Date()
63
76
  };
64
77
  const db = await getDb();
@@ -76,4 +89,116 @@ router.post("/", async (req, res) => {
76
89
  });
77
90
  }
78
91
  });
92
+
93
+ // Update comment (owner only)
94
+ router.patch("/:id", async (req, res) => {
95
+ try {
96
+ var _req$user3;
97
+ const {
98
+ id
99
+ } = req.params;
100
+ const {
101
+ comment
102
+ } = req.body;
103
+ const projectId = req.projectId;
104
+ const userId = (_req$user3 = req.user) === null || _req$user3 === void 0 ? void 0 : _req$user3.powrId;
105
+ if (!ObjectId.isValid(id)) {
106
+ return res.status(400).json({
107
+ success: false,
108
+ message: "Invalid comment id"
109
+ });
110
+ }
111
+ if (comment === undefined) {
112
+ return res.status(400).json({
113
+ success: false,
114
+ message: "comment is required"
115
+ });
116
+ }
117
+ const db = await getDb();
118
+ const existing = await db.collection("comments").findOne({
119
+ _id: new ObjectId(id),
120
+ projectId
121
+ });
122
+ if (!existing) {
123
+ return res.status(404).json({
124
+ success: false,
125
+ message: "Comment not found"
126
+ });
127
+ }
128
+ if (existing.userId !== userId) {
129
+ return res.status(403).json({
130
+ success: false,
131
+ message: "Not allowed to edit this comment"
132
+ });
133
+ }
134
+ await db.collection("comments").updateOne({
135
+ _id: new ObjectId(id),
136
+ projectId
137
+ }, {
138
+ $set: {
139
+ comment,
140
+ updatedAt: new Date()
141
+ }
142
+ });
143
+ return res.json({
144
+ success: true,
145
+ message: "Comment updated"
146
+ });
147
+ } catch (error) {
148
+ console.error("Error updating comment:", error);
149
+ return res.status(500).json({
150
+ success: false,
151
+ message: "Failed to update comment."
152
+ });
153
+ }
154
+ });
155
+
156
+ // Delete comment (owner only)
157
+ router.delete("/:id", async (req, res) => {
158
+ try {
159
+ var _req$user4;
160
+ const {
161
+ id
162
+ } = req.params;
163
+ const projectId = req.projectId;
164
+ const userId = (_req$user4 = req.user) === null || _req$user4 === void 0 ? void 0 : _req$user4.powrId;
165
+ if (!ObjectId.isValid(id)) {
166
+ return res.status(400).json({
167
+ success: false,
168
+ message: "Invalid comment id"
169
+ });
170
+ }
171
+ const db = await getDb();
172
+ const existing = await db.collection("comments").findOne({
173
+ _id: new ObjectId(id),
174
+ projectId
175
+ });
176
+ if (!existing) {
177
+ return res.status(404).json({
178
+ success: false,
179
+ message: "Comment not found"
180
+ });
181
+ }
182
+ if (existing.userId !== userId) {
183
+ return res.status(403).json({
184
+ success: false,
185
+ message: "Not allowed to delete this comment"
186
+ });
187
+ }
188
+ await db.collection("comments").deleteOne({
189
+ _id: new ObjectId(id),
190
+ projectId
191
+ });
192
+ return res.json({
193
+ success: true,
194
+ message: "Comment deleted"
195
+ });
196
+ } catch (error) {
197
+ console.error("Error deleting comment:", error);
198
+ return res.status(500).json({
199
+ success: false,
200
+ message: "Failed to delete comment."
201
+ });
202
+ }
203
+ });
79
204
  module.exports = router;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "powr-sdk-api",
3
- "version": "4.7.1",
3
+ "version": "4.7.3",
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",