powr-sdk-api 3.0.6 → 3.0.8
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 +2 -2
- package/dist/routes/functions.js +269 -0
- package/dist/routes/index.js +8 -8
- package/dist/routes/users.js +15 -11
- package/dist/routes/waitlists.js +2 -1
- package/dist/services/functions.js +229 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -15,7 +15,7 @@ const {
|
|
|
15
15
|
} = require("./swagger");
|
|
16
16
|
const {
|
|
17
17
|
createPowrRoutes,
|
|
18
|
-
|
|
18
|
+
initializeFunctions
|
|
19
19
|
} = require("./routes");
|
|
20
20
|
const {
|
|
21
21
|
verifyToken
|
|
@@ -27,6 +27,6 @@ module.exports = {
|
|
|
27
27
|
createSwaggerSpec,
|
|
28
28
|
notFoundHandler,
|
|
29
29
|
createPowrRoutes,
|
|
30
|
-
|
|
30
|
+
initializeFunctions,
|
|
31
31
|
verifyToken
|
|
32
32
|
};
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const express = require("express");
|
|
4
|
+
const router = express.Router();
|
|
5
|
+
const functionsManager = require("../services/functions");
|
|
6
|
+
const {
|
|
7
|
+
getDb
|
|
8
|
+
} = require("../services/mongo");
|
|
9
|
+
|
|
10
|
+
// Get Functions status
|
|
11
|
+
router.get("/status", async (req, res) => {
|
|
12
|
+
try {
|
|
13
|
+
const stats = functionsManager.getStats();
|
|
14
|
+
return res.status(200).json({
|
|
15
|
+
success: true,
|
|
16
|
+
data: {
|
|
17
|
+
...stats,
|
|
18
|
+
message: stats.isEnabled ? "Functions is running" : "Functions is disabled"
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
} catch (err) {
|
|
22
|
+
console.error("Error getting Functions status:", err);
|
|
23
|
+
return res.status(500).json({
|
|
24
|
+
success: false,
|
|
25
|
+
message: "Error getting Functions status"
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Enable Functions
|
|
31
|
+
router.post("/enable", async (req, res) => {
|
|
32
|
+
try {
|
|
33
|
+
functionsManager.enable();
|
|
34
|
+
return res.status(200).json({
|
|
35
|
+
success: true,
|
|
36
|
+
message: "Functions enabled successfully",
|
|
37
|
+
data: functionsManager.getStats()
|
|
38
|
+
});
|
|
39
|
+
} catch (err) {
|
|
40
|
+
console.error("Error enabling Functions:", err);
|
|
41
|
+
return res.status(500).json({
|
|
42
|
+
success: false,
|
|
43
|
+
message: "Error enabling Functions"
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// Disable Functions
|
|
49
|
+
router.post("/disable", async (req, res) => {
|
|
50
|
+
try {
|
|
51
|
+
functionsManager.disable();
|
|
52
|
+
return res.status(200).json({
|
|
53
|
+
success: true,
|
|
54
|
+
message: "Functions disabled successfully",
|
|
55
|
+
data: functionsManager.getStats()
|
|
56
|
+
});
|
|
57
|
+
} catch (err) {
|
|
58
|
+
console.error("Error disabling Functions:", err);
|
|
59
|
+
return res.status(500).json({
|
|
60
|
+
success: false,
|
|
61
|
+
message: "Error disabling Functions"
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Toggle Functions
|
|
67
|
+
router.post("/toggle", async (req, res) => {
|
|
68
|
+
try {
|
|
69
|
+
const isEnabled = functionsManager.toggle();
|
|
70
|
+
return res.status(200).json({
|
|
71
|
+
success: true,
|
|
72
|
+
message: isEnabled ? "Functions enabled" : "Functions disabled",
|
|
73
|
+
data: functionsManager.getStats()
|
|
74
|
+
});
|
|
75
|
+
} catch (err) {
|
|
76
|
+
console.error("Error toggling Functions:", err);
|
|
77
|
+
return res.status(500).json({
|
|
78
|
+
success: false,
|
|
79
|
+
message: "Error toggling Functions"
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// CRUD Operations for Functions
|
|
85
|
+
|
|
86
|
+
// Get all functions entries
|
|
87
|
+
router.get('/', async (req, res) => {
|
|
88
|
+
const {
|
|
89
|
+
projectId
|
|
90
|
+
} = req.query;
|
|
91
|
+
try {
|
|
92
|
+
const query = {
|
|
93
|
+
projectId
|
|
94
|
+
};
|
|
95
|
+
if (!projectId) {
|
|
96
|
+
return res.status(400).json({
|
|
97
|
+
success: false,
|
|
98
|
+
message: 'projectId is required.'
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
const db = await getDb();
|
|
102
|
+
const functionsData = await db.collection("functions").find(query).toArray();
|
|
103
|
+
return res.status(200).json({
|
|
104
|
+
success: true,
|
|
105
|
+
allfunctions: functionsData
|
|
106
|
+
});
|
|
107
|
+
} catch (error) {
|
|
108
|
+
console.error("Error retrieving functions entries:", error);
|
|
109
|
+
return res.status(500).json({
|
|
110
|
+
success: false,
|
|
111
|
+
message: "Failed to retrieve functions entries."
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Create a new function
|
|
117
|
+
router.post('/', async (req, res) => {
|
|
118
|
+
try {
|
|
119
|
+
const {
|
|
120
|
+
projectId
|
|
121
|
+
} = req.query;
|
|
122
|
+
if (!projectId) {
|
|
123
|
+
return res.status(400).json({
|
|
124
|
+
success: false,
|
|
125
|
+
message: 'projectId is required.'
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
const newFunction = req.body;
|
|
129
|
+
newFunction.projectId = projectId;
|
|
130
|
+
if (!newFunction || Object.keys(newFunction).length === 0) {
|
|
131
|
+
return res.status(400).json({
|
|
132
|
+
success: false,
|
|
133
|
+
message: 'Request body is empty or invalid.'
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
const db = await getDb();
|
|
137
|
+
const result = await db.collection('functions').insertOne(newFunction);
|
|
138
|
+
|
|
139
|
+
// Recompile the new function using Functions manager (only if enabled)
|
|
140
|
+
if (functionsManager.getStats().isEnabled) {
|
|
141
|
+
await functionsManager.updateRoute(projectId, newFunction.route, newFunction.code);
|
|
142
|
+
}
|
|
143
|
+
return res.status(201).json({
|
|
144
|
+
success: true,
|
|
145
|
+
entry: result.ops ? result.ops[0] : newFunction
|
|
146
|
+
});
|
|
147
|
+
} catch (error) {
|
|
148
|
+
console.error('Error inserting function entry:', error);
|
|
149
|
+
return res.status(500).json({
|
|
150
|
+
success: false,
|
|
151
|
+
message: 'Failed to insert function entry.'
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// Update function code
|
|
157
|
+
router.put('/:function', async (req, res) => {
|
|
158
|
+
try {
|
|
159
|
+
const {
|
|
160
|
+
function: functionName
|
|
161
|
+
} = req.params;
|
|
162
|
+
const {
|
|
163
|
+
code
|
|
164
|
+
} = req.body;
|
|
165
|
+
const {
|
|
166
|
+
projectId
|
|
167
|
+
} = req.query;
|
|
168
|
+
if (!projectId) {
|
|
169
|
+
return res.status(400).json({
|
|
170
|
+
success: false,
|
|
171
|
+
message: 'projectId is required.'
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
if (!code) {
|
|
175
|
+
return res.status(400).json({
|
|
176
|
+
success: false,
|
|
177
|
+
message: 'Code field is required.'
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
const query = {
|
|
181
|
+
route: functionName,
|
|
182
|
+
projectId
|
|
183
|
+
};
|
|
184
|
+
const db = await getDb();
|
|
185
|
+
const existingFunction = await db.collection('functions').findOne(query);
|
|
186
|
+
if (!existingFunction) {
|
|
187
|
+
return res.status(404).json({
|
|
188
|
+
success: false,
|
|
189
|
+
message: 'Function not found.'
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Update database
|
|
194
|
+
const result = await db.collection('functions').updateOne(query, {
|
|
195
|
+
$set: {
|
|
196
|
+
code
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
if (result.matchedCount === 0) {
|
|
200
|
+
return res.status(404).json({
|
|
201
|
+
success: false,
|
|
202
|
+
message: 'Function not found.'
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Recompile the updated function using Functions manager (only if enabled)
|
|
207
|
+
if (functionsManager.getStats().isEnabled) {
|
|
208
|
+
await functionsManager.updateRoute(projectId, functionName, code);
|
|
209
|
+
}
|
|
210
|
+
const updatedFunction = await db.collection('functions').findOne(query);
|
|
211
|
+
return res.status(200).json({
|
|
212
|
+
success: true,
|
|
213
|
+
entry: updatedFunction
|
|
214
|
+
});
|
|
215
|
+
} catch (error) {
|
|
216
|
+
console.error('Error updating function code:', error);
|
|
217
|
+
return res.status(500).json({
|
|
218
|
+
success: false,
|
|
219
|
+
message: 'Failed to update function code.',
|
|
220
|
+
error: error.message
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
// Execute dynamic function
|
|
226
|
+
router.post("/:function", async (req, res) => {
|
|
227
|
+
const {
|
|
228
|
+
function: functionName
|
|
229
|
+
} = req.params;
|
|
230
|
+
const {
|
|
231
|
+
projectId
|
|
232
|
+
} = req.query;
|
|
233
|
+
if (!projectId) {
|
|
234
|
+
return res.status(400).json({
|
|
235
|
+
success: false,
|
|
236
|
+
message: 'projectId is required.'
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
try {
|
|
240
|
+
// Use pre-compiled function (NO COMPILATION HERE)
|
|
241
|
+
const params = {
|
|
242
|
+
...req.query,
|
|
243
|
+
...req.body
|
|
244
|
+
};
|
|
245
|
+
const result = await functionsManager.execute(projectId, functionName, params);
|
|
246
|
+
|
|
247
|
+
// Handle the standardized response format
|
|
248
|
+
if (result.success) {
|
|
249
|
+
return res.status(200).json({
|
|
250
|
+
success: true,
|
|
251
|
+
data: result.data,
|
|
252
|
+
message: result.message
|
|
253
|
+
});
|
|
254
|
+
} else {
|
|
255
|
+
return res.status(404).json({
|
|
256
|
+
success: false,
|
|
257
|
+
message: result.message,
|
|
258
|
+
data: result.data
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
} catch (err) {
|
|
262
|
+
console.error("Error executing function:", err);
|
|
263
|
+
return res.status(500).json({
|
|
264
|
+
success: false,
|
|
265
|
+
message: "Error executing function"
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
module.exports = router;
|
package/dist/routes/index.js
CHANGED
|
@@ -7,7 +7,7 @@ const {
|
|
|
7
7
|
const {
|
|
8
8
|
verifyToken
|
|
9
9
|
} = require('../middleware/jwtToken');
|
|
10
|
-
const
|
|
10
|
+
const functionsManager = require('../services/functions');
|
|
11
11
|
|
|
12
12
|
// Import all route modules
|
|
13
13
|
const commentsRoutes = require('./comments');
|
|
@@ -15,7 +15,7 @@ const filesRoutes = require('./files');
|
|
|
15
15
|
const formsRoutes = require('./forms');
|
|
16
16
|
const invoiceRoutes = require('./invoice');
|
|
17
17
|
const likesRoutes = require('./likes');
|
|
18
|
-
const
|
|
18
|
+
const functionsRoutes = require('./functions');
|
|
19
19
|
const ratingsRoutes = require('./ratings');
|
|
20
20
|
const usersRoutes = require('./users');
|
|
21
21
|
const waitlistsRoutes = require('./waitlists');
|
|
@@ -37,7 +37,7 @@ const createPowrRoutes = (options = {}) => {
|
|
|
37
37
|
router.use('/forms', formsRoutes);
|
|
38
38
|
router.use('/invoice', invoiceRoutes);
|
|
39
39
|
router.use('/likes', likesRoutes);
|
|
40
|
-
router.use('/
|
|
40
|
+
router.use('/functions', functionsRoutes);
|
|
41
41
|
router.use('/ratings', ratingsRoutes);
|
|
42
42
|
router.use('/users', usersRoutes);
|
|
43
43
|
router.use('/waitlists', waitlistsRoutes);
|
|
@@ -50,12 +50,12 @@ const createPowrRoutes = (options = {}) => {
|
|
|
50
50
|
return router;
|
|
51
51
|
};
|
|
52
52
|
|
|
53
|
-
// Async
|
|
54
|
-
const
|
|
55
|
-
// Initialize
|
|
56
|
-
await
|
|
53
|
+
// Async Functions initialization function
|
|
54
|
+
const initializeFunctions = async (options = {}) => {
|
|
55
|
+
// Initialize Functions manager with options
|
|
56
|
+
await functionsManager.initialize(options);
|
|
57
57
|
};
|
|
58
58
|
module.exports = {
|
|
59
59
|
createPowrRoutes,
|
|
60
|
-
|
|
60
|
+
initializeFunctions
|
|
61
61
|
};
|
package/dist/routes/users.js
CHANGED
|
@@ -23,7 +23,8 @@ router.post("/", verifyToken, async (req, res) => {
|
|
|
23
23
|
|
|
24
24
|
try {
|
|
25
25
|
// Check if user already exists
|
|
26
|
-
const
|
|
26
|
+
const db = await getDb();
|
|
27
|
+
const existingUser = await db.collection("users").findOne({
|
|
27
28
|
$or: [{
|
|
28
29
|
phoneNumber
|
|
29
30
|
}, {
|
|
@@ -43,7 +44,7 @@ router.post("/", verifyToken, async (req, res) => {
|
|
|
43
44
|
createdAt: new Date(),
|
|
44
45
|
updatedAt: new Date()
|
|
45
46
|
};
|
|
46
|
-
const result = await
|
|
47
|
+
const result = await db.collection("users").insertOne(newUser);
|
|
47
48
|
|
|
48
49
|
// Create profile for this project
|
|
49
50
|
const newProfile = {
|
|
@@ -53,7 +54,7 @@ router.post("/", verifyToken, async (req, res) => {
|
|
|
53
54
|
createdAt: new Date(),
|
|
54
55
|
updatedAt: new Date()
|
|
55
56
|
};
|
|
56
|
-
await
|
|
57
|
+
await db.collection("profiles").insertOne(newProfile);
|
|
57
58
|
return res.status(201).json({
|
|
58
59
|
success: true,
|
|
59
60
|
message: "User created successfully",
|
|
@@ -90,11 +91,12 @@ router.get("/", verifyToken, async (req, res) => {
|
|
|
90
91
|
if (email) {
|
|
91
92
|
query.email = email;
|
|
92
93
|
}
|
|
93
|
-
const
|
|
94
|
+
const db = await getDb();
|
|
95
|
+
const users = await db.collection("users").find(query).toArray();
|
|
94
96
|
|
|
95
97
|
// Get profiles for this project
|
|
96
98
|
const userIds = users.map(user => user._id);
|
|
97
|
-
const profiles = await
|
|
99
|
+
const profiles = await db.collection("profiles").find({
|
|
98
100
|
userId: {
|
|
99
101
|
$in: userIds
|
|
100
102
|
},
|
|
@@ -137,7 +139,8 @@ router.put("/:id", verifyToken, async (req, res) => {
|
|
|
137
139
|
|
|
138
140
|
try {
|
|
139
141
|
// Check if user exists
|
|
140
|
-
const
|
|
142
|
+
const db = await getDb();
|
|
143
|
+
const existingUser = await db.collection("users").findOne({
|
|
141
144
|
_id: new ObjectId(id)
|
|
142
145
|
});
|
|
143
146
|
if (!existingUser) {
|
|
@@ -154,7 +157,7 @@ router.put("/:id", verifyToken, async (req, res) => {
|
|
|
154
157
|
email,
|
|
155
158
|
updatedAt: new Date()
|
|
156
159
|
};
|
|
157
|
-
await
|
|
160
|
+
await db.collection("users").updateOne({
|
|
158
161
|
_id: new ObjectId(id)
|
|
159
162
|
}, {
|
|
160
163
|
$set: updateData
|
|
@@ -162,7 +165,7 @@ router.put("/:id", verifyToken, async (req, res) => {
|
|
|
162
165
|
|
|
163
166
|
// Update profile if access level is provided
|
|
164
167
|
if (userData.access !== undefined) {
|
|
165
|
-
await
|
|
168
|
+
await db.collection("profiles").updateOne({
|
|
166
169
|
userId: new ObjectId(id),
|
|
167
170
|
projectId: projectId
|
|
168
171
|
}, {
|
|
@@ -196,7 +199,8 @@ router.delete("/:id", verifyToken, async (req, res) => {
|
|
|
196
199
|
|
|
197
200
|
try {
|
|
198
201
|
// Check if user exists
|
|
199
|
-
const
|
|
202
|
+
const db = await getDb();
|
|
203
|
+
const existingUser = await db.collection("users").findOne({
|
|
200
204
|
_id: new ObjectId(id)
|
|
201
205
|
});
|
|
202
206
|
if (!existingUser) {
|
|
@@ -207,12 +211,12 @@ router.delete("/:id", verifyToken, async (req, res) => {
|
|
|
207
211
|
}
|
|
208
212
|
|
|
209
213
|
// Delete user
|
|
210
|
-
await
|
|
214
|
+
await db.collection("users").deleteOne({
|
|
211
215
|
_id: new ObjectId(id)
|
|
212
216
|
});
|
|
213
217
|
|
|
214
218
|
// Delete profile for this project
|
|
215
|
-
await
|
|
219
|
+
await db.collection("profiles").deleteOne({
|
|
216
220
|
userId: new ObjectId(id),
|
|
217
221
|
projectId: projectId
|
|
218
222
|
});
|
package/dist/routes/waitlists.js
CHANGED
|
@@ -14,7 +14,8 @@ router.get("/", async (req, res) => {
|
|
|
14
14
|
const projectId = req.projectId; // Use middleware-injected projectId
|
|
15
15
|
|
|
16
16
|
try {
|
|
17
|
-
const
|
|
17
|
+
const db = await getDb();
|
|
18
|
+
const waitlists = await db.collection("waitlists").find({
|
|
18
19
|
projectId
|
|
19
20
|
}).toArray();
|
|
20
21
|
return res.json({
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
getDb
|
|
5
|
+
} = require("./mongo");
|
|
6
|
+
const crypto = require('crypto');
|
|
7
|
+
const config = require('../config');
|
|
8
|
+
class FunctionsManager {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.compiledFunctions = new Map();
|
|
11
|
+
this.isInitialized = false;
|
|
12
|
+
this.isCentralService = false;
|
|
13
|
+
this.isEnabled = false; // Default disabled
|
|
14
|
+
}
|
|
15
|
+
async initialize(options = {}) {
|
|
16
|
+
this.isCentralService = options.isCentralService || false;
|
|
17
|
+
this.isEnabled = options.enableFunctions === true; // Default false unless explicitly enabled
|
|
18
|
+
|
|
19
|
+
if (!this.isEnabled) {
|
|
20
|
+
console.log("🚫 Functions is disabled");
|
|
21
|
+
this.isInitialized = true;
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
if (this.isCentralService) {
|
|
25
|
+
// Central service: Don't pre-load, load dynamically
|
|
26
|
+
console.log("🔄 Central service mode - loading functions dynamically");
|
|
27
|
+
this.isInitialized = true;
|
|
28
|
+
} else {
|
|
29
|
+
// Individual API: Load only this project's functions
|
|
30
|
+
const projectId = config.projectId;
|
|
31
|
+
console.log(`📦 Loading functions for project: ${projectId}`);
|
|
32
|
+
try {
|
|
33
|
+
const db = await getDb();
|
|
34
|
+
const routes = await db.collection("functions").find({
|
|
35
|
+
projectId
|
|
36
|
+
}).toArray();
|
|
37
|
+
routes.forEach(route => {
|
|
38
|
+
this.compileAndCache(route);
|
|
39
|
+
});
|
|
40
|
+
this.isInitialized = true;
|
|
41
|
+
console.log(`✅ Pre-compiled ${routes.length} functions for project ${projectId}`);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
console.error("❌ Failed to initialize functions:", error);
|
|
44
|
+
this.isInitialized = true; // Still mark as initialized to prevent blocking
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
compileAndCache(route) {
|
|
49
|
+
const cacheKey = `${route.projectId}:${route.route}`;
|
|
50
|
+
try {
|
|
51
|
+
// Security validation
|
|
52
|
+
if (!this.validateCode(route.code)) {
|
|
53
|
+
console.error(`❌ Invalid code in function: ${route.route}`);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Pre-compile function
|
|
58
|
+
const compiledFunction = new Function("params", route.code);
|
|
59
|
+
this.compiledFunctions.set(cacheKey, {
|
|
60
|
+
function: compiledFunction,
|
|
61
|
+
metadata: {
|
|
62
|
+
route: route.route,
|
|
63
|
+
projectId: route.projectId,
|
|
64
|
+
compiledAt: new Date(),
|
|
65
|
+
codeHash: this.generateHash(route.code)
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
console.log(`✅ Compiled: ${route.route}`);
|
|
69
|
+
} catch (error) {
|
|
70
|
+
console.error(`❌ Failed to compile ${route.route}:`, error);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async execute(projectId, route, params) {
|
|
74
|
+
if (!this.isEnabled) {
|
|
75
|
+
return {
|
|
76
|
+
success: false,
|
|
77
|
+
message: "Functions is disabled",
|
|
78
|
+
data: null
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
const cacheKey = `${projectId}:${route}`;
|
|
82
|
+
const cached = this.compiledFunctions.get(cacheKey);
|
|
83
|
+
if (!cached) {
|
|
84
|
+
if (this.isCentralService) {
|
|
85
|
+
// Try to load only the specific function dynamically
|
|
86
|
+
await this.loadSpecificRoute(projectId, route);
|
|
87
|
+
const retryCached = this.compiledFunctions.get(cacheKey);
|
|
88
|
+
if (retryCached) {
|
|
89
|
+
return retryCached.function(params);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
success: false,
|
|
94
|
+
message: `Function not found: ${route}`,
|
|
95
|
+
data: null
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
const result = cached.function(params);
|
|
100
|
+
return {
|
|
101
|
+
success: true,
|
|
102
|
+
message: "Function executed successfully",
|
|
103
|
+
data: result
|
|
104
|
+
};
|
|
105
|
+
} catch (error) {
|
|
106
|
+
return {
|
|
107
|
+
success: false,
|
|
108
|
+
message: `Error executing function: ${error.message}`,
|
|
109
|
+
data: null,
|
|
110
|
+
error: error.message
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Load specific function for central service
|
|
116
|
+
async loadSpecificRoute(projectId, route) {
|
|
117
|
+
if (!this.isCentralService) {
|
|
118
|
+
throw new Error("Dynamic loading only available in central service mode");
|
|
119
|
+
}
|
|
120
|
+
try {
|
|
121
|
+
const db = await getDb();
|
|
122
|
+
const routeData = await db.collection("functions").findOne({
|
|
123
|
+
projectId,
|
|
124
|
+
route
|
|
125
|
+
});
|
|
126
|
+
if (routeData) {
|
|
127
|
+
this.compileAndCache(routeData);
|
|
128
|
+
console.log(`✅ Loaded function: ${route} for project ${projectId}`);
|
|
129
|
+
} else {
|
|
130
|
+
console.log(`❌ Function not found: ${route} for project ${projectId}`);
|
|
131
|
+
}
|
|
132
|
+
} catch (error) {
|
|
133
|
+
console.error(`❌ Failed to load function ${route} for project ${projectId}:`, error);
|
|
134
|
+
throw error;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Dynamic loading for central service
|
|
139
|
+
async loadProjectRoutes(projectId) {
|
|
140
|
+
if (!this.isCentralService) {
|
|
141
|
+
throw new Error("Dynamic loading only available in central service mode");
|
|
142
|
+
}
|
|
143
|
+
try {
|
|
144
|
+
const db = await getDb();
|
|
145
|
+
const routes = await db.collection("functions").find({
|
|
146
|
+
projectId
|
|
147
|
+
}).toArray();
|
|
148
|
+
routes.forEach(route => {
|
|
149
|
+
this.compileAndCache(route);
|
|
150
|
+
});
|
|
151
|
+
console.log(`✅ Loaded ${routes.length} functions for project ${projectId}`);
|
|
152
|
+
} catch (error) {
|
|
153
|
+
console.error(`❌ Failed to load functions for project ${projectId}:`, error);
|
|
154
|
+
throw error;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Security validation
|
|
159
|
+
validateCode(code) {
|
|
160
|
+
const dangerousPatterns = [/process\./, /require\(/, /eval\(/, /setTimeout\(/, /setInterval\(/, /global\./, /__dirname/, /__filename/];
|
|
161
|
+
return !dangerousPatterns.some(pattern => pattern.test(code));
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Generate hash for change detection
|
|
165
|
+
generateHash(code) {
|
|
166
|
+
return crypto.createHash('md5').update(code).digest('hex');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Update function (re-compile)
|
|
170
|
+
async updateRoute(projectId, route, newCode) {
|
|
171
|
+
const routeData = {
|
|
172
|
+
projectId,
|
|
173
|
+
route,
|
|
174
|
+
code: newCode
|
|
175
|
+
};
|
|
176
|
+
this.compileAndCache(routeData);
|
|
177
|
+
|
|
178
|
+
// Optionally save to database
|
|
179
|
+
try {
|
|
180
|
+
const db = await getDb();
|
|
181
|
+
await db.collection("functions").updateOne({
|
|
182
|
+
projectId,
|
|
183
|
+
route
|
|
184
|
+
}, {
|
|
185
|
+
$set: {
|
|
186
|
+
code: newCode,
|
|
187
|
+
updatedAt: new Date()
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
} catch (error) {
|
|
191
|
+
console.error(`❌ Failed to update function in database: ${route}`, error);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Get function statistics
|
|
196
|
+
getStats() {
|
|
197
|
+
return {
|
|
198
|
+
totalFunctions: this.compiledFunctions.size,
|
|
199
|
+
isInitialized: this.isInitialized,
|
|
200
|
+
isCentralService: this.isCentralService,
|
|
201
|
+
isEnabled: this.isEnabled,
|
|
202
|
+
cacheKeys: Array.from(this.compiledFunctions.keys())
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Clear cache (for testing or maintenance)
|
|
207
|
+
clearCache() {
|
|
208
|
+
this.compiledFunctions.clear();
|
|
209
|
+
console.log("🧹 Function cache cleared");
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Enable/Disable Functions
|
|
213
|
+
enable() {
|
|
214
|
+
this.isEnabled = true;
|
|
215
|
+
console.log("✅ Functions enabled");
|
|
216
|
+
}
|
|
217
|
+
disable() {
|
|
218
|
+
this.isEnabled = false;
|
|
219
|
+
console.log("🚫 Functions disabled");
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Toggle Functions
|
|
223
|
+
toggle() {
|
|
224
|
+
this.isEnabled = !this.isEnabled;
|
|
225
|
+
console.log(this.isEnabled ? "✅ Functions enabled" : "🚫 Functions disabled");
|
|
226
|
+
return this.isEnabled;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
module.exports = new FunctionsManager();
|