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 CHANGED
@@ -15,7 +15,7 @@ const {
15
15
  } = require("./swagger");
16
16
  const {
17
17
  createPowrRoutes,
18
- initializePlexx
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
- initializePlexx,
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;
@@ -7,7 +7,7 @@ const {
7
7
  const {
8
8
  verifyToken
9
9
  } = require('../middleware/jwtToken');
10
- const plexxManager = require('../services/plexx');
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 plexxRoutes = require('./plexx');
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('/plexx', plexxRoutes);
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 Plexx initialization function
54
- const initializePlexx = async (options = {}) => {
55
- // Initialize Plexx manager with options
56
- await plexxManager.initialize(options);
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
- initializePlexx
60
+ initializeFunctions
61
61
  };
@@ -23,7 +23,8 @@ router.post("/", verifyToken, async (req, res) => {
23
23
 
24
24
  try {
25
25
  // Check if user already exists
26
- const existingUser = await getDb().collection("users").findOne({
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 getDb().collection("users").insertOne(newUser);
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 getDb().collection("profiles").insertOne(newProfile);
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 users = await getDb().collection("users").find(query).toArray();
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 getDb().collection("profiles").find({
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 existingUser = await getDb().collection("users").findOne({
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 getDb().collection("users").updateOne({
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 getDb().collection("profiles").updateOne({
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 existingUser = await getDb().collection("users").findOne({
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 getDb().collection("users").deleteOne({
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 getDb().collection("profiles").deleteOne({
219
+ await db.collection("profiles").deleteOne({
216
220
  userId: new ObjectId(id),
217
221
  projectId: projectId
218
222
  });
@@ -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 waitlists = await getDb().collection("waitlists").find({
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();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "powr-sdk-api",
3
- "version": "3.0.6",
3
+ "version": "3.0.8",
4
4
  "description": "Shared API core library for PowrStack projects",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",