powr-sdk-api 4.1.8 → 4.1.10
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 +3 -1
- package/dist/routes/index.js +11 -1
- package/dist/routes/scheduledTasks.js +240 -0
- package/dist/services/scheduledTasks.js +459 -0
- package/dist/services/tools.js +62 -0
- package/package.json +71 -68
package/dist/index.js
CHANGED
|
@@ -16,7 +16,8 @@ const {
|
|
|
16
16
|
const {
|
|
17
17
|
createPowrRoutes,
|
|
18
18
|
initializeFunctions,
|
|
19
|
-
initializeTools
|
|
19
|
+
initializeTools,
|
|
20
|
+
initializeScheduledTasks
|
|
20
21
|
} = require("./routes");
|
|
21
22
|
const {
|
|
22
23
|
verifyToken
|
|
@@ -30,5 +31,6 @@ module.exports = {
|
|
|
30
31
|
createPowrRoutes,
|
|
31
32
|
initializeFunctions,
|
|
32
33
|
initializeTools,
|
|
34
|
+
initializeScheduledTasks,
|
|
33
35
|
verifyToken
|
|
34
36
|
};
|
package/dist/routes/index.js
CHANGED
|
@@ -9,6 +9,7 @@ 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');
|
|
12
13
|
|
|
13
14
|
// Import all route modules
|
|
14
15
|
const commentsRoutes = require('./comments');
|
|
@@ -28,6 +29,7 @@ const profilesRoutes = require('./profiles');
|
|
|
28
29
|
const chatRoutes = require('./chat');
|
|
29
30
|
const feedsRoutes = require('./feeds');
|
|
30
31
|
const toolsRoutes = require('./tools');
|
|
32
|
+
const scheduledTasksRoutes = require('./scheduledTasks');
|
|
31
33
|
|
|
32
34
|
// Synchronous router creation
|
|
33
35
|
const createPowrRoutes = (options = {}) => {
|
|
@@ -52,6 +54,7 @@ const createPowrRoutes = (options = {}) => {
|
|
|
52
54
|
router.use('/chat', verifyToken, chatRoutes);
|
|
53
55
|
router.use('/feeds', verifyToken, feedsRoutes);
|
|
54
56
|
router.use('/tools', verifyToken, toolsRoutes);
|
|
57
|
+
router.use('/scheduled-tasks', verifyToken, scheduledTasksRoutes);
|
|
55
58
|
return router;
|
|
56
59
|
};
|
|
57
60
|
|
|
@@ -66,8 +69,15 @@ const initializeTools = async (options = {}) => {
|
|
|
66
69
|
// Initialize Tools manager with options
|
|
67
70
|
await toolsManager.initialize(options);
|
|
68
71
|
};
|
|
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
|
+
};
|
|
69
78
|
module.exports = {
|
|
70
79
|
createPowrRoutes,
|
|
71
80
|
initializeFunctions,
|
|
72
|
-
initializeTools
|
|
81
|
+
initializeTools,
|
|
82
|
+
initializeScheduledTasks
|
|
73
83
|
};
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const express = require('express');
|
|
4
|
+
const router = express.Router();
|
|
5
|
+
const scheduledTasksManager = require('../services/scheduledTasks');
|
|
6
|
+
const toolsManager = require('../services/tools');
|
|
7
|
+
|
|
8
|
+
// GET /scheduled-tasks - Get all tasks for user
|
|
9
|
+
router.get('/', async (req, res) => {
|
|
10
|
+
try {
|
|
11
|
+
const isAdmin = req.user.role === 'admin';
|
|
12
|
+
const result = await scheduledTasksManager.getUserTasks(req.user.powrId, req.projectId, isAdmin);
|
|
13
|
+
res.json(result);
|
|
14
|
+
} catch (error) {
|
|
15
|
+
console.error('❌ Error getting scheduled tasks:', error);
|
|
16
|
+
res.status(500).json({
|
|
17
|
+
success: false,
|
|
18
|
+
message: 'Failed to get scheduled tasks'
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// GET /scheduled-tasks/:taskId - Get specific task
|
|
24
|
+
router.get('/:taskId', async (req, res) => {
|
|
25
|
+
try {
|
|
26
|
+
const isAdmin = req.user.role === 'admin';
|
|
27
|
+
const result = await scheduledTasksManager.getTask(req.params.taskId, req.user.powrId, req.projectId, isAdmin);
|
|
28
|
+
if (!result.success) {
|
|
29
|
+
return res.status(404).json(result);
|
|
30
|
+
}
|
|
31
|
+
res.json(result);
|
|
32
|
+
} catch (error) {
|
|
33
|
+
console.error('❌ Error getting scheduled task:', error);
|
|
34
|
+
res.status(500).json({
|
|
35
|
+
success: false,
|
|
36
|
+
message: 'Failed to get scheduled task'
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// POST /scheduled-tasks - Create new task
|
|
42
|
+
router.post('/', async (req, res) => {
|
|
43
|
+
try {
|
|
44
|
+
var _tool$actions;
|
|
45
|
+
const {
|
|
46
|
+
name,
|
|
47
|
+
description,
|
|
48
|
+
cronExpression,
|
|
49
|
+
toolId,
|
|
50
|
+
actionId,
|
|
51
|
+
params,
|
|
52
|
+
isActive
|
|
53
|
+
} = req.body;
|
|
54
|
+
|
|
55
|
+
// Validate required fields
|
|
56
|
+
if (!name || !cronExpression || !toolId || !actionId) {
|
|
57
|
+
return res.status(400).json({
|
|
58
|
+
success: false,
|
|
59
|
+
message: 'Missing required fields: name, cronExpression, toolId, actionId'
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Validate tool exists
|
|
64
|
+
const tool = toolsManager.getTool(toolId);
|
|
65
|
+
if (!tool) {
|
|
66
|
+
return res.status(400).json({
|
|
67
|
+
success: false,
|
|
68
|
+
message: `Tool not found: ${toolId}`
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Validate action exists
|
|
73
|
+
const action = (_tool$actions = tool.actions) === null || _tool$actions === void 0 ? void 0 : _tool$actions.find(a => a.id === actionId);
|
|
74
|
+
if (!action) {
|
|
75
|
+
return res.status(400).json({
|
|
76
|
+
success: false,
|
|
77
|
+
message: `Action not found: ${actionId} for tool ${toolId}`
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
const taskData = {
|
|
81
|
+
name,
|
|
82
|
+
description,
|
|
83
|
+
cronExpression,
|
|
84
|
+
toolId,
|
|
85
|
+
actionId,
|
|
86
|
+
params: params || {},
|
|
87
|
+
userId: req.user.powrId,
|
|
88
|
+
projectId: req.projectId,
|
|
89
|
+
// Add projectId
|
|
90
|
+
isActive: isActive !== false // Default to true
|
|
91
|
+
};
|
|
92
|
+
const result = await scheduledTasksManager.createTask(taskData);
|
|
93
|
+
res.status(201).json(result);
|
|
94
|
+
} catch (error) {
|
|
95
|
+
console.error('❌ Error creating scheduled task:', error);
|
|
96
|
+
res.status(500).json({
|
|
97
|
+
success: false,
|
|
98
|
+
message: 'Failed to create scheduled task'
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// PUT /scheduled-tasks/:taskId - Update task
|
|
104
|
+
router.put('/:taskId', async (req, res) => {
|
|
105
|
+
try {
|
|
106
|
+
const {
|
|
107
|
+
name,
|
|
108
|
+
description,
|
|
109
|
+
cronExpression,
|
|
110
|
+
toolId,
|
|
111
|
+
actionId,
|
|
112
|
+
params,
|
|
113
|
+
isActive
|
|
114
|
+
} = req.body;
|
|
115
|
+
|
|
116
|
+
// Validate tool exists if toolId is being updated
|
|
117
|
+
if (toolId) {
|
|
118
|
+
const tool = toolsManager.getTool(toolId);
|
|
119
|
+
if (!tool) {
|
|
120
|
+
return res.status(400).json({
|
|
121
|
+
success: false,
|
|
122
|
+
message: `Tool not found: ${toolId}`
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Validate action exists if actionId is being updated
|
|
127
|
+
if (actionId) {
|
|
128
|
+
var _tool$actions2;
|
|
129
|
+
const action = (_tool$actions2 = tool.actions) === null || _tool$actions2 === void 0 ? void 0 : _tool$actions2.find(a => a.id === actionId);
|
|
130
|
+
if (!action) {
|
|
131
|
+
return res.status(400).json({
|
|
132
|
+
success: false,
|
|
133
|
+
message: `Action not found: ${actionId} for tool ${toolId}`
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
const updates = {};
|
|
139
|
+
if (name !== undefined) updates.name = name;
|
|
140
|
+
if (description !== undefined) updates.description = description;
|
|
141
|
+
if (cronExpression !== undefined) updates.cronExpression = cronExpression;
|
|
142
|
+
if (toolId !== undefined) updates.toolId = toolId;
|
|
143
|
+
if (actionId !== undefined) updates.actionId = actionId;
|
|
144
|
+
if (params !== undefined) updates.params = params;
|
|
145
|
+
if (isActive !== undefined) updates.isActive = isActive;
|
|
146
|
+
const isAdmin = req.user.role === 'admin';
|
|
147
|
+
const result = await scheduledTasksManager.updateTask(req.params.taskId, req.user.powrId, req.projectId, updates, isAdmin);
|
|
148
|
+
if (!result.success) {
|
|
149
|
+
return res.status(404).json(result);
|
|
150
|
+
}
|
|
151
|
+
res.json(result);
|
|
152
|
+
} catch (error) {
|
|
153
|
+
console.error('❌ Error updating scheduled task:', error);
|
|
154
|
+
res.status(500).json({
|
|
155
|
+
success: false,
|
|
156
|
+
message: 'Failed to update scheduled task'
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// DELETE /scheduled-tasks/:taskId - Delete task
|
|
162
|
+
router.delete('/:taskId', async (req, res) => {
|
|
163
|
+
try {
|
|
164
|
+
const isAdmin = req.user.role === 'admin';
|
|
165
|
+
const result = await scheduledTasksManager.deleteTask(req.params.taskId, req.user.powrId, req.projectId, isAdmin);
|
|
166
|
+
if (!result.success) {
|
|
167
|
+
return res.status(404).json(result);
|
|
168
|
+
}
|
|
169
|
+
res.json(result);
|
|
170
|
+
} catch (error) {
|
|
171
|
+
console.error('❌ Error deleting scheduled task:', error);
|
|
172
|
+
res.status(500).json({
|
|
173
|
+
success: false,
|
|
174
|
+
message: 'Failed to delete scheduled task'
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// POST /scheduled-tasks/:taskId/toggle - Toggle task active status
|
|
180
|
+
router.post('/:taskId/toggle', async (req, res) => {
|
|
181
|
+
try {
|
|
182
|
+
const {
|
|
183
|
+
isActive
|
|
184
|
+
} = req.body;
|
|
185
|
+
if (typeof isActive !== 'boolean') {
|
|
186
|
+
return res.status(400).json({
|
|
187
|
+
success: false,
|
|
188
|
+
message: 'isActive must be a boolean value'
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
const isAdmin = req.user.role === 'admin';
|
|
192
|
+
const result = await scheduledTasksManager.toggleTask(req.params.taskId, req.user.powrId, req.projectId, isActive, isAdmin);
|
|
193
|
+
if (!result.success) {
|
|
194
|
+
return res.status(404).json(result);
|
|
195
|
+
}
|
|
196
|
+
res.json(result);
|
|
197
|
+
} catch (error) {
|
|
198
|
+
console.error('❌ Error toggling scheduled task:', error);
|
|
199
|
+
res.status(500).json({
|
|
200
|
+
success: false,
|
|
201
|
+
message: 'Failed to toggle scheduled task'
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// POST /scheduled-tasks/:taskId/execute - Execute task manually
|
|
207
|
+
router.post('/:taskId/execute', async (req, res) => {
|
|
208
|
+
try {
|
|
209
|
+
const isAdmin = req.user.role === 'admin';
|
|
210
|
+
const result = await scheduledTasksManager.executeTask(req.params.taskId, req.user.powrId, req.projectId, isAdmin);
|
|
211
|
+
if (!result.success) {
|
|
212
|
+
return res.status(404).json(result);
|
|
213
|
+
}
|
|
214
|
+
res.json(result);
|
|
215
|
+
} catch (error) {
|
|
216
|
+
console.error('❌ Error executing scheduled task:', error);
|
|
217
|
+
res.status(500).json({
|
|
218
|
+
success: false,
|
|
219
|
+
message: 'Failed to execute scheduled task'
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// GET /scheduled-tasks/stats - Get scheduler statistics
|
|
225
|
+
router.get('/stats', async (req, res) => {
|
|
226
|
+
try {
|
|
227
|
+
const stats = scheduledTasksManager.getStats();
|
|
228
|
+
res.json({
|
|
229
|
+
success: true,
|
|
230
|
+
stats
|
|
231
|
+
});
|
|
232
|
+
} catch (error) {
|
|
233
|
+
console.error('❌ Error getting scheduler stats:', error);
|
|
234
|
+
res.status(500).json({
|
|
235
|
+
success: false,
|
|
236
|
+
message: 'Failed to get scheduler stats'
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
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/dist/services/tools.js
CHANGED
|
@@ -301,6 +301,9 @@ class ToolsManager {
|
|
|
301
301
|
switch (tool.name.toLowerCase()) {
|
|
302
302
|
case 'gmail':
|
|
303
303
|
return await this.executeGmailAction(actionId, params, config);
|
|
304
|
+
case 'weather':
|
|
305
|
+
case 'openweather':
|
|
306
|
+
return await this.executeWeatherAction(actionId, params, config, tool);
|
|
304
307
|
case 'slack':
|
|
305
308
|
return await this.executeSlackAction(actionId, params, config);
|
|
306
309
|
case 'github':
|
|
@@ -364,6 +367,65 @@ class ToolsManager {
|
|
|
364
367
|
};
|
|
365
368
|
}
|
|
366
369
|
}
|
|
370
|
+
async executeWeatherAction(actionId, params, config, tool) {
|
|
371
|
+
const axios = require('axios');
|
|
372
|
+
try {
|
|
373
|
+
var _tool$systemConfig;
|
|
374
|
+
// Get weather data from system config (database) or fallback to env var
|
|
375
|
+
const weatherApiKey = (tool === null || tool === void 0 || (_tool$systemConfig = tool.systemConfig) === null || _tool$systemConfig === void 0 ? void 0 : _tool$systemConfig.apiKey) || process.env.WEATHER_API_KEY || 'demo';
|
|
376
|
+
const location = config.location;
|
|
377
|
+
let weatherData;
|
|
378
|
+
if (weatherApiKey === 'demo') {
|
|
379
|
+
// Demo weather data for testing
|
|
380
|
+
weatherData = {
|
|
381
|
+
weather: [{
|
|
382
|
+
main: params.condition || 'Rain',
|
|
383
|
+
description: 'light rain'
|
|
384
|
+
}]
|
|
385
|
+
};
|
|
386
|
+
console.log('🌤️ Using demo weather data');
|
|
387
|
+
} else {
|
|
388
|
+
// Real weather API call
|
|
389
|
+
const weatherResponse = await axios.get(`https://api.openweathermap.org/data/2.5/weather?q=${encodeURIComponent(location)}&appid=${weatherApiKey}&units=metric`);
|
|
390
|
+
weatherData = weatherResponse.data;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// Check if condition matches
|
|
394
|
+
const currentWeather = weatherData.weather[0].main.toLowerCase();
|
|
395
|
+
const targetCondition = params.condition.toLowerCase();
|
|
396
|
+
const operator = params.operator || 'is';
|
|
397
|
+
let conditionMet = false;
|
|
398
|
+
switch (operator) {
|
|
399
|
+
case 'is':
|
|
400
|
+
conditionMet = currentWeather === targetCondition;
|
|
401
|
+
break;
|
|
402
|
+
case 'is_not':
|
|
403
|
+
conditionMet = currentWeather !== targetCondition;
|
|
404
|
+
break;
|
|
405
|
+
case 'contains':
|
|
406
|
+
conditionMet = currentWeather.includes(targetCondition) || weatherData.weather[0].description.toLowerCase().includes(targetCondition);
|
|
407
|
+
break;
|
|
408
|
+
default:
|
|
409
|
+
conditionMet = currentWeather === targetCondition;
|
|
410
|
+
}
|
|
411
|
+
return {
|
|
412
|
+
success: true,
|
|
413
|
+
message: `Weather check completed. Current: ${currentWeather}, Required: ${targetCondition}`,
|
|
414
|
+
data: {
|
|
415
|
+
currentWeather: currentWeather,
|
|
416
|
+
requiredCondition: targetCondition,
|
|
417
|
+
conditionMet: conditionMet,
|
|
418
|
+
location: location
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
} catch (error) {
|
|
422
|
+
console.error('❌ Weather check error:', error);
|
|
423
|
+
return {
|
|
424
|
+
success: false,
|
|
425
|
+
message: `Failed to check weather: ${error.message}`
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
}
|
|
367
429
|
async executeSlackAction(actionId, params, config) {
|
|
368
430
|
// TODO: Implement Slack actions
|
|
369
431
|
return {
|
package/package.json
CHANGED
|
@@ -1,68 +1,71 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "powr-sdk-api",
|
|
3
|
-
"version": "4.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
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"mongodb": "^6.3.0",
|
|
47
|
-
"multer": "^1.4.5-lts.1",
|
|
48
|
-
"
|
|
49
|
-
"nodemailer": "^6.10.0"
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
"
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
"@
|
|
56
|
-
"@
|
|
57
|
-
"@
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
"
|
|
64
|
-
},
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
}
|
|
68
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "powr-sdk-api",
|
|
3
|
+
"version": "4.1.10",
|
|
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
|
+
"axios": "^1.6.0",
|
|
42
|
+
"bcrypt": "^5.1.1",
|
|
43
|
+
"cron-parser": "^4.9.0",
|
|
44
|
+
"express": "^4.18.2",
|
|
45
|
+
"jsonwebtoken": "^9.0.2",
|
|
46
|
+
"mongodb": "^6.3.0",
|
|
47
|
+
"multer": "^1.4.5-lts.1",
|
|
48
|
+
"node-cron": "^3.0.3",
|
|
49
|
+
"nodemailer": "^6.10.0",
|
|
50
|
+
"swagger-jsdoc": "^6.2.8",
|
|
51
|
+
"swagger-ui-express": "^5.0.0",
|
|
52
|
+
"winston": "^3.17.0"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@babel/cli": "^7.23.9",
|
|
56
|
+
"@babel/core": "^7.24.0",
|
|
57
|
+
"@babel/preset-env": "^7.24.0",
|
|
58
|
+
"@types/express": "^4.17.21",
|
|
59
|
+
"@types/swagger-jsdoc": "^6.0.4",
|
|
60
|
+
"@types/swagger-ui-express": "^4.1.6",
|
|
61
|
+
"eslint": "^8.57.0",
|
|
62
|
+
"jest": "^29.7.0",
|
|
63
|
+
"typescript": "^5.3.3"
|
|
64
|
+
},
|
|
65
|
+
"peerDependencies": {
|
|
66
|
+
"express": "^4.18.2"
|
|
67
|
+
},
|
|
68
|
+
"engines": {
|
|
69
|
+
"node": ">=14.0.0"
|
|
70
|
+
}
|
|
71
|
+
}
|