powr-sdk-api 4.1.10 → 4.2.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.
@@ -11,7 +11,7 @@ const config = require('../config');
11
11
  */
12
12
  const injectProjectId = (options = {}) => {
13
13
  const isCentralService = options.isCentralService || false;
14
- const excludePaths = options.excludePaths || ['/auth/login', '/auth/register', '/health'];
14
+ const excludePaths = options.excludePaths || ['/health'];
15
15
  return (req, res, next) => {
16
16
  try {
17
17
  // Skip validation for excluded paths
@@ -9,8 +9,6 @@ const {
9
9
  } = require('../middleware/jwtToken');
10
10
  const functionsManager = require('../services/functions');
11
11
  const toolsManager = require('../services/tools');
12
- const scheduledTasksManager = require('../services/scheduledTasks');
13
-
14
12
  // Import all route modules
15
13
  const commentsRoutes = require('./comments');
16
14
  const filesRoutes = require('./files');
@@ -29,7 +27,7 @@ const profilesRoutes = require('./profiles');
29
27
  const chatRoutes = require('./chat');
30
28
  const feedsRoutes = require('./feeds');
31
29
  const toolsRoutes = require('./tools');
32
- const scheduledTasksRoutes = require('./scheduledTasks');
30
+ const tasksRoutes = require('./tasks');
33
31
 
34
32
  // Synchronous router creation
35
33
  const createPowrRoutes = (options = {}) => {
@@ -54,7 +52,7 @@ const createPowrRoutes = (options = {}) => {
54
52
  router.use('/chat', verifyToken, chatRoutes);
55
53
  router.use('/feeds', verifyToken, feedsRoutes);
56
54
  router.use('/tools', verifyToken, toolsRoutes);
57
- router.use('/scheduled-tasks', verifyToken, scheduledTasksRoutes);
55
+ router.use('/tasks', verifyToken, tasksRoutes);
58
56
  return router;
59
57
  };
60
58
 
@@ -69,15 +67,8 @@ const initializeTools = async (options = {}) => {
69
67
  // Initialize Tools manager with options
70
68
  await toolsManager.initialize(options);
71
69
  };
72
-
73
- // Async Scheduled Tasks initialization function
74
- const initializeScheduledTasks = async (options = {}) => {
75
- // Initialize Scheduled Tasks manager with options
76
- await scheduledTasksManager.initialize(options);
77
- };
78
70
  module.exports = {
79
71
  createPowrRoutes,
80
72
  initializeFunctions,
81
- initializeTools,
82
- initializeScheduledTasks
73
+ initializeTools
83
74
  };
@@ -0,0 +1,183 @@
1
+ "use strict";
2
+
3
+ const express = require("express");
4
+ const router = express.Router();
5
+ const toolsManager = require("../services/tools");
6
+ const {
7
+ getDb
8
+ } = require("../services/mongo");
9
+
10
+ // GET /tasks - Get all tasks
11
+ router.get("/", async (req, res) => {
12
+ const projectId = req.projectId;
13
+ try {
14
+ const query = {
15
+ projectId
16
+ };
17
+ if (req.user.access !== 100) {
18
+ query.userId = req.user.powrId;
19
+ }
20
+ const db = await getDb();
21
+ const tasksData = await db.collection("tasks").find(query).toArray();
22
+ return res.status(200).json({
23
+ success: true,
24
+ tasks: tasksData
25
+ });
26
+ } catch (error) {
27
+ console.error("Error retrieving tasks entries:", error);
28
+ return res.status(500).json({
29
+ success: false,
30
+ message: "Failed to retrieve tasks entries."
31
+ });
32
+ }
33
+ });
34
+
35
+ // POST /tasks - Create new task
36
+ router.post("/", async (req, res) => {
37
+ try {
38
+ var _tool$actions;
39
+ const {
40
+ name,
41
+ description,
42
+ cronExpression,
43
+ toolId,
44
+ actionId,
45
+ params,
46
+ isActive
47
+ } = req.body;
48
+
49
+ // Validate required fields
50
+ if (!name || !cronExpression || !toolId || !actionId) {
51
+ return res.status(400).json({
52
+ success: false,
53
+ message: "Missing required fields: name, cronExpression, toolId, actionId"
54
+ });
55
+ }
56
+
57
+ // Validate tool exists
58
+ const tool = toolsManager.getTool(toolId);
59
+ if (!tool) {
60
+ return res.status(400).json({
61
+ success: false,
62
+ message: `Tool not found: ${toolId}`
63
+ });
64
+ }
65
+
66
+ // Validate action exists
67
+ const action = (_tool$actions = tool.actions) === null || _tool$actions === void 0 ? void 0 : _tool$actions.find(a => a.id === actionId);
68
+ if (!action) {
69
+ return res.status(400).json({
70
+ success: false,
71
+ message: `Action not found: ${actionId} for tool ${toolId}`
72
+ });
73
+ }
74
+ const taskData = {
75
+ name,
76
+ description,
77
+ cronExpression,
78
+ toolId,
79
+ actionId,
80
+ params: params || {},
81
+ projectId: req.projectId,
82
+ userId: req.user.access === 100 ? null : req.user.powrId,
83
+ isActive: isActive !== false,
84
+ // Default to true
85
+ createdAt: new Date(),
86
+ updatedAt: new Date()
87
+ };
88
+ const db = await getDb();
89
+ await db.collection("tasks").insertOne(taskData);
90
+ console.log(`✅ Created scheduled task: ${taskData.name}`);
91
+ res.status(201).json({
92
+ success: true,
93
+ message: "Task created successfully"
94
+ });
95
+ } catch (error) {
96
+ console.error("❌ Error creating scheduled task:", error);
97
+ res.status(500).json({
98
+ success: false,
99
+ message: "Failed to create scheduled task"
100
+ });
101
+ }
102
+ });
103
+
104
+ // PUT /tasks/:taskId - Update task
105
+ router.put("/:taskId", async (req, res) => {
106
+ try {
107
+ const {
108
+ name,
109
+ description,
110
+ cronExpression,
111
+ toolId,
112
+ actionId,
113
+ params,
114
+ isActive
115
+ } = req.body;
116
+
117
+ // Validate tool exists if toolId is being updated
118
+ if (toolId) {
119
+ const tool = toolsManager.getTool(toolId);
120
+ if (!tool) {
121
+ return res.status(400).json({
122
+ success: false,
123
+ message: `Tool not found: ${toolId}`
124
+ });
125
+ }
126
+
127
+ // Validate action exists if actionId is being updated
128
+ if (actionId) {
129
+ var _tool$actions2;
130
+ const action = (_tool$actions2 = tool.actions) === null || _tool$actions2 === void 0 ? void 0 : _tool$actions2.find(a => a.id === actionId);
131
+ if (!action) {
132
+ return res.status(400).json({
133
+ success: false,
134
+ message: `Action not found: ${actionId} for tool ${toolId}`
135
+ });
136
+ }
137
+ }
138
+ }
139
+ const updates = {};
140
+ if (name !== undefined) updates.name = name;
141
+ if (description !== undefined) updates.description = description;
142
+ if (cronExpression !== undefined) updates.cronExpression = cronExpression;
143
+ if (toolId !== undefined) updates.toolId = toolId;
144
+ if (actionId !== undefined) updates.actionId = actionId;
145
+ if (params !== undefined) updates.params = params;
146
+ if (isActive !== undefined) updates.isActive = isActive;
147
+
148
+ // Build query based on user role
149
+ let query = {
150
+ id: req.params.taskId
151
+ };
152
+ const db = await getDb();
153
+ // Get existing task
154
+ const existingTask = await db.collection("tasks").findOne(query);
155
+ if (!existingTask) {
156
+ return res.status(404).json({
157
+ success: false,
158
+ message: "Task not found"
159
+ });
160
+ }
161
+
162
+ // Update fields
163
+ const updatedTask = {
164
+ ...existingTask,
165
+ ...updates,
166
+ updatedAt: new Date()
167
+ };
168
+ await db.collection("tasks").updateOne(query, {
169
+ $set: updatedTask
170
+ });
171
+ res.status(200).json({
172
+ success: true,
173
+ message: "Task updated successfully"
174
+ });
175
+ } catch (error) {
176
+ console.error("❌ Error updating scheduled task:", error);
177
+ res.status(500).json({
178
+ success: false,
179
+ message: "Failed to update scheduled task"
180
+ });
181
+ }
182
+ });
183
+ module.exports = router;
@@ -0,0 +1,459 @@
1
+ "use strict";
2
+
3
+ const {
4
+ getDb
5
+ } = require("./mongo");
6
+ const cron = require('node-cron');
7
+ class ScheduledTasksManager {
8
+ constructor() {
9
+ this.scheduledJobs = new Map();
10
+ this.isInitialized = false;
11
+ }
12
+ async initialize() {
13
+ try {
14
+ console.log("📅 Initializing scheduled tasks manager...");
15
+
16
+ // Load existing tasks from database
17
+ const db = await getDb();
18
+ const tasks = await db.collection("scheduled_tasks").find({
19
+ isActive: true
20
+ }).toArray();
21
+
22
+ // Schedule existing active tasks
23
+ tasks.forEach(task => {
24
+ this.scheduleTask(task);
25
+ });
26
+ this.isInitialized = true;
27
+ console.log(`✅ Scheduled ${tasks.length} active tasks`);
28
+ } catch (error) {
29
+ console.error("❌ Failed to initialize scheduled tasks:", error);
30
+ this.isInitialized = true; // Still mark as initialized
31
+ }
32
+ }
33
+
34
+ // Create a new scheduled task
35
+ async createTask(taskData) {
36
+ try {
37
+ const db = await getDb();
38
+ const task = {
39
+ id: taskData.id || this.generateTaskId(),
40
+ name: taskData.name,
41
+ description: taskData.description,
42
+ cronExpression: taskData.cronExpression,
43
+ toolId: taskData.toolId,
44
+ actionId: taskData.actionId,
45
+ params: taskData.params,
46
+ userId: taskData.userId,
47
+ projectId: taskData.projectId,
48
+ // Add projectId
49
+ isActive: taskData.isActive !== false,
50
+ // Default to true
51
+ lastRun: null,
52
+ nextRun: this.getNextRunTime(taskData.cronExpression),
53
+ createdAt: new Date(),
54
+ updatedAt: new Date()
55
+ };
56
+ await db.collection("scheduled_tasks").insertOne(task);
57
+
58
+ // Schedule the task if it's active
59
+ if (task.isActive) {
60
+ this.scheduleTask(task);
61
+ }
62
+ console.log(`✅ Created scheduled task: ${task.name} (${task.id})`);
63
+ return {
64
+ success: true,
65
+ task
66
+ };
67
+ } catch (error) {
68
+ console.error("❌ Failed to create scheduled task:", error);
69
+ return {
70
+ success: false,
71
+ message: error.message
72
+ };
73
+ }
74
+ }
75
+
76
+ // Get all tasks for a user (filtered by projectId and userId)
77
+ async getUserTasks(userId, projectId, isAdmin = false) {
78
+ try {
79
+ const db = await getDb();
80
+
81
+ // Build query based on user role
82
+ let query = {};
83
+ if (isAdmin) {
84
+ // Admin sees all tasks in the project
85
+ query.projectId = projectId;
86
+ } else {
87
+ // Regular users see only their tasks in the project
88
+ query.userId = userId;
89
+ query.projectId = projectId;
90
+ }
91
+ const tasks = await db.collection("scheduled_tasks").find(query).sort({
92
+ createdAt: -1
93
+ }).toArray();
94
+ return {
95
+ success: true,
96
+ tasks
97
+ };
98
+ } catch (error) {
99
+ console.error("❌ Failed to get user tasks:", error);
100
+ return {
101
+ success: false,
102
+ message: error.message
103
+ };
104
+ }
105
+ }
106
+
107
+ // Get a specific task (with project and user validation)
108
+ async getTask(taskId, userId, projectId, isAdmin = false) {
109
+ try {
110
+ const db = await getDb();
111
+
112
+ // Build query based on user role
113
+ let query = {
114
+ id: taskId
115
+ };
116
+ if (isAdmin) {
117
+ // Admin can access any task in the project
118
+ query.projectId = projectId;
119
+ } else {
120
+ // Regular users can only access their own tasks
121
+ query.userId = userId;
122
+ query.projectId = projectId;
123
+ }
124
+ const task = await db.collection("scheduled_tasks").findOne(query);
125
+ if (!task) {
126
+ return {
127
+ success: false,
128
+ message: "Task not found"
129
+ };
130
+ }
131
+ return {
132
+ success: true,
133
+ task
134
+ };
135
+ } catch (error) {
136
+ console.error("❌ Failed to get task:", error);
137
+ return {
138
+ success: false,
139
+ message: error.message
140
+ };
141
+ }
142
+ }
143
+
144
+ // Update a task (with project and user validation)
145
+ async updateTask(taskId, userId, projectId, updates, isAdmin = false) {
146
+ try {
147
+ const db = await getDb();
148
+
149
+ // Build query based on user role
150
+ let query = {
151
+ id: taskId
152
+ };
153
+ if (isAdmin) {
154
+ // Admin can update any task in the project
155
+ query.projectId = projectId;
156
+ } else {
157
+ // Regular users can only update their own tasks
158
+ query.userId = userId;
159
+ query.projectId = projectId;
160
+ }
161
+
162
+ // Get existing task
163
+ const existingTask = await db.collection("scheduled_tasks").findOne(query);
164
+ if (!existingTask) {
165
+ return {
166
+ success: false,
167
+ message: "Task not found"
168
+ };
169
+ }
170
+
171
+ // Update fields
172
+ const updatedTask = {
173
+ ...existingTask,
174
+ ...updates,
175
+ updatedAt: new Date()
176
+ };
177
+
178
+ // Update next run time if cron expression changed
179
+ if (updates.cronExpression && updates.cronExpression !== existingTask.cronExpression) {
180
+ updatedTask.nextRun = this.getNextRunTime(updates.cronExpression);
181
+ }
182
+ await db.collection("scheduled_tasks").updateOne(query, {
183
+ $set: updatedTask
184
+ });
185
+
186
+ // Reschedule if active
187
+ if (updatedTask.isActive) {
188
+ this.unscheduleTask(taskId);
189
+ this.scheduleTask(updatedTask);
190
+ } else {
191
+ this.unscheduleTask(taskId);
192
+ }
193
+ console.log(`✅ Updated scheduled task: ${updatedTask.name} (${taskId})`);
194
+ return {
195
+ success: true,
196
+ task: updatedTask
197
+ };
198
+ } catch (error) {
199
+ console.error("❌ Failed to update scheduled task:", error);
200
+ return {
201
+ success: false,
202
+ message: error.message
203
+ };
204
+ }
205
+ }
206
+
207
+ // Delete a task (with project and user validation)
208
+ async deleteTask(taskId, userId, projectId, isAdmin = false) {
209
+ try {
210
+ const db = await getDb();
211
+
212
+ // Build query based on user role
213
+ let query = {
214
+ id: taskId
215
+ };
216
+ if (isAdmin) {
217
+ // Admin can delete any task in the project
218
+ query.projectId = projectId;
219
+ } else {
220
+ // Regular users can only delete their own tasks
221
+ query.userId = userId;
222
+ query.projectId = projectId;
223
+ }
224
+
225
+ // Get task to check if it exists
226
+ const task = await db.collection("scheduled_tasks").findOne(query);
227
+ if (!task) {
228
+ return {
229
+ success: false,
230
+ message: "Task not found"
231
+ };
232
+ }
233
+
234
+ // Remove from database
235
+ await db.collection("scheduled_tasks").deleteOne(query);
236
+
237
+ // Unschedule the task
238
+ this.unscheduleTask(taskId);
239
+ console.log(`✅ Deleted scheduled task: ${task.name} (${taskId})`);
240
+ return {
241
+ success: true
242
+ };
243
+ } catch (error) {
244
+ console.error("❌ Failed to delete scheduled task:", error);
245
+ return {
246
+ success: false,
247
+ message: error.message
248
+ };
249
+ }
250
+ }
251
+
252
+ // Toggle task active status (with project and user validation)
253
+ async toggleTask(taskId, userId, projectId, isActive, isAdmin = false) {
254
+ try {
255
+ const db = await getDb();
256
+
257
+ // Build query based on user role
258
+ let query = {
259
+ id: taskId
260
+ };
261
+ if (isAdmin) {
262
+ // Admin can toggle any task in the project
263
+ query.projectId = projectId;
264
+ } else {
265
+ // Regular users can only toggle their own tasks
266
+ query.userId = userId;
267
+ query.projectId = projectId;
268
+ }
269
+ const task = await db.collection("scheduled_tasks").findOne(query);
270
+ if (!task) {
271
+ return {
272
+ success: false,
273
+ message: "Task not found"
274
+ };
275
+ }
276
+ if (isActive) {
277
+ // Activate task
278
+ await db.collection("scheduled_tasks").updateOne(query, {
279
+ $set: {
280
+ isActive: true,
281
+ updatedAt: new Date()
282
+ }
283
+ });
284
+ this.scheduleTask({
285
+ ...task,
286
+ isActive: true
287
+ });
288
+ } else {
289
+ // Deactivate task
290
+ await db.collection("scheduled_tasks").updateOne(query, {
291
+ $set: {
292
+ isActive: false,
293
+ updatedAt: new Date()
294
+ }
295
+ });
296
+ this.unscheduleTask(taskId);
297
+ }
298
+ console.log(`${isActive ? '✅' : '❌'} Task ${taskId} ${isActive ? 'activated' : 'deactivated'}`);
299
+ return {
300
+ success: true
301
+ };
302
+ } catch (error) {
303
+ console.error("❌ Failed to toggle task:", error);
304
+ return {
305
+ success: false,
306
+ message: error.message
307
+ };
308
+ }
309
+ }
310
+
311
+ // Execute a task manually (with project and user validation)
312
+ async executeTask(taskId, userId, projectId, isAdmin = false) {
313
+ try {
314
+ const db = await getDb();
315
+
316
+ // Build query based on user role
317
+ let query = {
318
+ id: taskId
319
+ };
320
+ if (isAdmin) {
321
+ // Admin can execute any task in the project
322
+ query.projectId = projectId;
323
+ } else {
324
+ // Regular users can only execute their own tasks
325
+ query.userId = userId;
326
+ query.projectId = projectId;
327
+ }
328
+ const task = await db.collection("scheduled_tasks").findOne(query);
329
+ if (!task) {
330
+ return {
331
+ success: false,
332
+ message: "Task not found"
333
+ };
334
+ }
335
+ console.log(`🚀 Manually executing task: ${task.name} (${taskId})`);
336
+
337
+ // Execute the task
338
+ const result = await this.executeTaskAction(task);
339
+
340
+ // Update last run time
341
+ await db.collection("scheduled_tasks").updateOne(query, {
342
+ $set: {
343
+ lastRun: new Date()
344
+ }
345
+ });
346
+ return {
347
+ success: true,
348
+ result
349
+ };
350
+ } catch (error) {
351
+ console.error("❌ Failed to execute task:", error);
352
+ return {
353
+ success: false,
354
+ message: error.message
355
+ };
356
+ }
357
+ }
358
+
359
+ // Schedule a task
360
+ scheduleTask(task) {
361
+ try {
362
+ // Unschedule if already scheduled
363
+ this.unscheduleTask(task.id);
364
+
365
+ // Validate cron expression
366
+ if (!cron.validate(task.cronExpression)) {
367
+ console.error(`❌ Invalid cron expression for task ${task.id}: ${task.cronExpression}`);
368
+ return;
369
+ }
370
+
371
+ // Schedule the task
372
+ const job = cron.schedule(task.cronExpression, async () => {
373
+ console.log(`⏰ Executing scheduled task: ${task.name} (${task.id})`);
374
+ await this.executeTaskAction(task);
375
+ });
376
+ this.scheduledJobs.set(task.id, job);
377
+ console.log(`✅ Scheduled task: ${task.name} (${task.id})`);
378
+ } catch (error) {
379
+ console.error(`❌ Failed to schedule task ${task.id}:`, error);
380
+ }
381
+ }
382
+
383
+ // Unschedule a task
384
+ unscheduleTask(taskId) {
385
+ const job = this.scheduledJobs.get(taskId);
386
+ if (job) {
387
+ job.stop();
388
+ this.scheduledJobs.delete(taskId);
389
+ console.log(`🛑 Unscheduled task: ${taskId}`);
390
+ }
391
+ }
392
+
393
+ // Execute the actual task action
394
+ async executeTaskAction(task) {
395
+ try {
396
+ // Import tools manager
397
+ const toolsManager = require('./tools');
398
+
399
+ // Execute the tool action
400
+ const result = await toolsManager.executeToolAction(task.userId, task.toolId, task.actionId, task.params);
401
+
402
+ // Log execution
403
+ await this.logTaskExecution(task, result);
404
+ return result;
405
+ } catch (error) {
406
+ console.error(`❌ Task execution failed for ${task.id}:`, error);
407
+ return {
408
+ success: false,
409
+ message: error.message
410
+ };
411
+ }
412
+ }
413
+
414
+ // Log task execution
415
+ async logTaskExecution(task, result) {
416
+ try {
417
+ const db = await getDb();
418
+ await db.collection("task_executions").insertOne({
419
+ taskId: task.id,
420
+ userId: task.userId,
421
+ toolId: task.toolId,
422
+ actionId: task.actionId,
423
+ params: task.params,
424
+ result: result,
425
+ executedAt: new Date()
426
+ });
427
+ } catch (error) {
428
+ console.error("❌ Failed to log task execution:", error);
429
+ }
430
+ }
431
+
432
+ // Helper methods
433
+ generateTaskId() {
434
+ return `task_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
435
+ }
436
+ getNextRunTime(cronExpression) {
437
+ try {
438
+ const parser = require('cron-parser');
439
+ const interval = parser.parseExpression(cronExpression);
440
+ return interval.next().toDate();
441
+ } catch (error) {
442
+ console.error("❌ Failed to parse cron expression:", error);
443
+ return null;
444
+ }
445
+ }
446
+
447
+ // Get statistics
448
+ getStats() {
449
+ return {
450
+ isInitialized: this.isInitialized,
451
+ activeJobs: this.scheduledJobs.size,
452
+ totalJobs: this.scheduledJobs.size
453
+ };
454
+ }
455
+ }
456
+
457
+ // Create and export singleton instance
458
+ const scheduledTasksManager = new ScheduledTasksManager();
459
+ module.exports = scheduledTasksManager;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "powr-sdk-api",
3
- "version": "4.1.10",
3
+ "version": "4.2.0",
4
4
  "description": "Shared API core library for PowrStack projects",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",