powr-sdk-api 2.5.2 → 3.0.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.
package/dist/config.js CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  const config = {
4
4
  mongoUri: process.env.POWR_DB_URI,
5
- jwtToken: process.env.JWT_TOKEN
5
+ jwtToken: process.env.JWT_TOKEN,
6
+ projectId: process.env.PROJECT_ID
6
7
  };
7
8
  module.exports = config;
@@ -14,7 +14,7 @@ router.get('/conversations', async (req, res) => {
14
14
  try {
15
15
  const userId = req.user.powrId;
16
16
  const userAccess = req.user.access;
17
- const projectId = req.projectId;
17
+ const projectId = req.query.projectId || req.projectId;
18
18
  console.log('Current user ID:', userId, 'Access:', userAccess, 'Project:', projectId);
19
19
  const db = await getDb();
20
20
  const conversations = await db.collection('conversations').find({
@@ -84,7 +84,7 @@ router.get('/conversations/:conversationId/messages', async (req, res) => {
84
84
  conversationId
85
85
  } = req.params;
86
86
  const userId = req.user.powrId;
87
- const projectId = req.projectId;
87
+ const projectId = req.query.projectId || req.projectId;
88
88
  const db = await getDb();
89
89
 
90
90
  // Verify user is part of conversation
@@ -141,10 +141,11 @@ router.post('/conversations/:conversationId/messages', async (req, res) => {
141
141
  conversationId
142
142
  } = req.params;
143
143
  const {
144
- content
144
+ content,
145
+ projectId: bodyProjectId
145
146
  } = req.body;
146
147
  const userId = req.user.powrId;
147
- const projectId = req.projectId;
148
+ const projectId = bodyProjectId || req.projectId;
148
149
  if (!content || content.trim() === '') {
149
150
  return res.status(400).json({
150
151
  success: false,
@@ -217,11 +218,12 @@ router.post('/conversations/:conversationId/messages', async (req, res) => {
217
218
  router.post('/conversations', async (req, res) => {
218
219
  try {
219
220
  const {
220
- participantId
221
+ participantId,
222
+ projectId: bodyProjectId
221
223
  } = req.body;
222
224
  const userId = req.user.powrId;
223
225
  const userAccess = req.user.access;
224
- const projectId = req.projectId;
226
+ const projectId = bodyProjectId || req.projectId;
225
227
  if (!participantId) {
226
228
  return res.status(400).json({
227
229
  success: false,
@@ -308,7 +310,7 @@ router.get('/users', async (req, res) => {
308
310
  try {
309
311
  const userId = req.user.powrId;
310
312
  const userAccess = req.user.access;
311
- const projectId = req.projectId;
313
+ const projectId = req.query.projectId || req.projectId;
312
314
  console.log('Current user access level:', userAccess, 'Project:', projectId);
313
315
  const db = await getDb();
314
316
 
@@ -316,7 +318,18 @@ router.get('/users', async (req, res) => {
316
318
  const projectUsers = await db.collection('profiles').find({
317
319
  projectId: projectId
318
320
  }).toArray();
321
+ console.log('Project users found:', projectUsers.length, 'for projectId:', projectId);
319
322
  const userIds = projectUsers.map(profile => profile.userId);
323
+ console.log('User IDs extracted:', userIds);
324
+
325
+ // If no users found for this project, return empty array
326
+ if (userIds.length === 0) {
327
+ console.log('No users found for project:', projectId);
328
+ return res.json({
329
+ success: true,
330
+ data: []
331
+ });
332
+ }
320
333
  let userQuery = {
321
334
  _id: {
322
335
  $in: userIds.map(id => new ObjectId(id))
@@ -7,6 +7,7 @@ const {
7
7
  const {
8
8
  verifyToken
9
9
  } = require('../middleware/jwtToken');
10
+ const plexxManager = require('../services/plexx');
10
11
 
11
12
  // Import all route modules
12
13
  const commentsRoutes = require('./comments');
@@ -24,9 +25,12 @@ const slidesRoutes = require('./slides');
24
25
  const notificationsRoutes = require('./notifications');
25
26
  const profilesRoutes = require('./profiles');
26
27
  const chatRoutes = require('./chat');
27
- const createPowrRoutes = (options = {}) => {
28
+ const createPowrRoutes = async (options = {}) => {
28
29
  const router = express.Router();
29
30
 
31
+ // Initialize Plexx manager with options
32
+ await plexxManager.initialize(options);
33
+
30
34
  // Use the dedicated projectId injection middleware
31
35
  router.use(injectProjectId(options));
32
36
  router.use('/comments', commentsRoutes);
@@ -2,15 +2,86 @@
2
2
 
3
3
  const express = require("express");
4
4
  const router = express.Router();
5
- const {
6
- getDb
7
- } = require("../services/mongo");
5
+ const plexxManager = require("../services/plexx");
6
+
7
+ // Get Plexx status
8
+ router.get("/status", async (req, res) => {
9
+ try {
10
+ const stats = plexxManager.getStats();
11
+ return res.status(200).json({
12
+ success: true,
13
+ data: {
14
+ ...stats,
15
+ message: stats.isEnabled ? "Plexx is running" : "Plexx is disabled"
16
+ }
17
+ });
18
+ } catch (err) {
19
+ console.error("Error getting Plexx status:", err);
20
+ return res.status(500).json({
21
+ success: false,
22
+ message: "Error getting Plexx status"
23
+ });
24
+ }
25
+ });
26
+
27
+ // Enable Plexx
28
+ router.post("/enable", async (req, res) => {
29
+ try {
30
+ plexxManager.enable();
31
+ return res.status(200).json({
32
+ success: true,
33
+ message: "Plexx enabled successfully",
34
+ data: plexxManager.getStats()
35
+ });
36
+ } catch (err) {
37
+ console.error("Error enabling Plexx:", err);
38
+ return res.status(500).json({
39
+ success: false,
40
+ message: "Error enabling Plexx"
41
+ });
42
+ }
43
+ });
44
+
45
+ // Disable Plexx
46
+ router.post("/disable", async (req, res) => {
47
+ try {
48
+ plexxManager.disable();
49
+ return res.status(200).json({
50
+ success: true,
51
+ message: "Plexx disabled successfully",
52
+ data: plexxManager.getStats()
53
+ });
54
+ } catch (err) {
55
+ console.error("Error disabling Plexx:", err);
56
+ return res.status(500).json({
57
+ success: false,
58
+ message: "Error disabling Plexx"
59
+ });
60
+ }
61
+ });
62
+
63
+ // Toggle Plexx
64
+ router.post("/toggle", async (req, res) => {
65
+ try {
66
+ const isEnabled = plexxManager.toggle();
67
+ return res.status(200).json({
68
+ success: true,
69
+ message: isEnabled ? "Plexx enabled" : "Plexx disabled",
70
+ data: plexxManager.getStats()
71
+ });
72
+ } catch (err) {
73
+ console.error("Error toggling Plexx:", err);
74
+ return res.status(500).json({
75
+ success: false,
76
+ message: "Error toggling Plexx"
77
+ });
78
+ }
79
+ });
8
80
  router.get("/:route", async (req, res) => {
9
81
  const {
10
82
  route
11
83
  } = req.params;
12
84
  const {
13
- name,
14
85
  projectId
15
86
  } = req.query;
16
87
  if (!projectId) {
@@ -19,23 +90,16 @@ router.get("/:route", async (req, res) => {
19
90
  message: 'projectId is required.'
20
91
  });
21
92
  }
22
- const logicEntry = await getDb().collection("routes").findOne({
23
- route,
24
- projectId
25
- });
26
- if (!logicEntry) {
27
- return res.status(404).json({
28
- success: false,
29
- message: "Logic not found"
30
- });
31
- }
32
- const codeString = logicEntry.code;
33
93
  try {
34
- const logicFunction = new Function("age", codeString);
35
- const status = logicFunction(name);
94
+ // Use pre-compiled function (NO COMPILATION HERE)
95
+ const params = {
96
+ ...req.query,
97
+ ...req.body
98
+ };
99
+ const result = await plexxManager.execute(projectId, route, params);
36
100
  return res.status(200).json({
37
101
  success: true,
38
- data: status
102
+ data: result
39
103
  });
40
104
  } catch (err) {
41
105
  console.error("Error executing logic:", err);
@@ -107,6 +107,8 @@ router.put('/:route', async (req, res) => {
107
107
  message: 'Route not found.'
108
108
  });
109
109
  }
110
+
111
+ // Update database
110
112
  const result = await db.collection('routes').updateOne(query, {
111
113
  $set: {
112
114
  code
@@ -118,6 +120,12 @@ router.put('/:route', async (req, res) => {
118
120
  message: 'Route not found.'
119
121
  });
120
122
  }
123
+
124
+ // Recompile the updated route using Plexx manager (only if enabled)
125
+ const plexxManager = require('../services/plexx');
126
+ if (plexxManager.getStats().isEnabled) {
127
+ await plexxManager.updateRoute(projectId, route, code);
128
+ }
121
129
  const updatedRoute = await db.collection('routes').findOne(query);
122
130
  return res.status(200).json({
123
131
  success: true,
@@ -0,0 +1,181 @@
1
+ "use strict";
2
+
3
+ const {
4
+ getDb
5
+ } = require("./mongo");
6
+ const crypto = require('crypto');
7
+ const config = require('../config');
8
+ class PlexxManager {
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.enablePlexx === true; // Default false unless explicitly enabled
18
+
19
+ if (!this.isEnabled) {
20
+ console.log("🚫 Plexx 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 routes dynamically");
27
+ this.isInitialized = true;
28
+ } else {
29
+ // Individual API: Load only this project's routes
30
+ const projectId = config.projectId;
31
+ console.log(`📦 Loading routes for project: ${projectId}`);
32
+ try {
33
+ const routes = await getDb().collection("routes").find({
34
+ projectId
35
+ }).toArray();
36
+ routes.forEach(route => {
37
+ this.compileAndCache(route);
38
+ });
39
+ this.isInitialized = true;
40
+ console.log(`✅ Pre-compiled ${routes.length} routes for project ${projectId}`);
41
+ } catch (error) {
42
+ console.error("❌ Failed to initialize routes:", error);
43
+ this.isInitialized = true; // Still mark as initialized to prevent blocking
44
+ }
45
+ }
46
+ }
47
+ compileAndCache(route) {
48
+ const cacheKey = `${route.projectId}:${route.route}`;
49
+ try {
50
+ // Security validation
51
+ if (!this.validateCode(route.code)) {
52
+ console.error(`❌ Invalid code in route: ${route.route}`);
53
+ return;
54
+ }
55
+
56
+ // Pre-compile function
57
+ const compiledFunction = new Function("params", route.code);
58
+ this.compiledFunctions.set(cacheKey, {
59
+ function: compiledFunction,
60
+ metadata: {
61
+ route: route.route,
62
+ projectId: route.projectId,
63
+ compiledAt: new Date(),
64
+ codeHash: this.generateHash(route.code)
65
+ }
66
+ });
67
+ console.log(`✅ Compiled: ${route.route}`);
68
+ } catch (error) {
69
+ console.error(`❌ Failed to compile ${route.route}:`, error);
70
+ }
71
+ }
72
+ async execute(projectId, route, params) {
73
+ if (!this.isEnabled) {
74
+ throw new Error("Plexx is disabled");
75
+ }
76
+ const cacheKey = `${projectId}:${route}`;
77
+ const cached = this.compiledFunctions.get(cacheKey);
78
+ if (!cached) {
79
+ if (this.isCentralService) {
80
+ // Try to load project routes dynamically
81
+ await this.loadProjectRoutes(projectId);
82
+ const retryCached = this.compiledFunctions.get(cacheKey);
83
+ if (retryCached) {
84
+ return retryCached.function(params);
85
+ }
86
+ }
87
+ throw new Error(`Route not found: ${route}`);
88
+ }
89
+ return cached.function(params);
90
+ }
91
+
92
+ // Dynamic loading for central service
93
+ async loadProjectRoutes(projectId) {
94
+ if (!this.isCentralService) {
95
+ throw new Error("Dynamic loading only available in central service mode");
96
+ }
97
+ try {
98
+ const routes = await getDb().collection("routes").find({
99
+ projectId
100
+ }).toArray();
101
+ routes.forEach(route => {
102
+ this.compileAndCache(route);
103
+ });
104
+ console.log(`✅ Loaded ${routes.length} routes for project ${projectId}`);
105
+ } catch (error) {
106
+ console.error(`❌ Failed to load routes for project ${projectId}:`, error);
107
+ throw error;
108
+ }
109
+ }
110
+
111
+ // Security validation
112
+ validateCode(code) {
113
+ const dangerousPatterns = [/process\./, /require\(/, /eval\(/, /setTimeout\(/, /setInterval\(/, /global\./, /__dirname/, /__filename/];
114
+ return !dangerousPatterns.some(pattern => pattern.test(code));
115
+ }
116
+
117
+ // Generate hash for change detection
118
+ generateHash(code) {
119
+ return crypto.createHash('md5').update(code).digest('hex');
120
+ }
121
+
122
+ // Update route (re-compile)
123
+ async updateRoute(projectId, route, newCode) {
124
+ const routeData = {
125
+ projectId,
126
+ route,
127
+ code: newCode
128
+ };
129
+ this.compileAndCache(routeData);
130
+
131
+ // Optionally save to database
132
+ try {
133
+ await getDb().collection("routes").updateOne({
134
+ projectId,
135
+ route
136
+ }, {
137
+ $set: {
138
+ code: newCode,
139
+ updatedAt: new Date()
140
+ }
141
+ });
142
+ } catch (error) {
143
+ console.error(`❌ Failed to update route in database: ${route}`, error);
144
+ }
145
+ }
146
+
147
+ // Get route statistics
148
+ getStats() {
149
+ return {
150
+ totalRoutes: this.compiledFunctions.size,
151
+ isInitialized: this.isInitialized,
152
+ isCentralService: this.isCentralService,
153
+ isEnabled: this.isEnabled,
154
+ cacheKeys: Array.from(this.compiledFunctions.keys())
155
+ };
156
+ }
157
+
158
+ // Clear cache (for testing or maintenance)
159
+ clearCache() {
160
+ this.compiledFunctions.clear();
161
+ console.log("🧹 Route cache cleared");
162
+ }
163
+
164
+ // Enable/Disable Plexx
165
+ enable() {
166
+ this.isEnabled = true;
167
+ console.log("✅ Plexx enabled");
168
+ }
169
+ disable() {
170
+ this.isEnabled = false;
171
+ console.log("🚫 Plexx disabled");
172
+ }
173
+
174
+ // Toggle Plexx
175
+ toggle() {
176
+ this.isEnabled = !this.isEnabled;
177
+ console.log(this.isEnabled ? "✅ Plexx enabled" : "🚫 Plexx disabled");
178
+ return this.isEnabled;
179
+ }
180
+ }
181
+ module.exports = new PlexxManager();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "powr-sdk-api",
3
- "version": "2.5.2",
3
+ "version": "3.0.0",
4
4
  "description": "Shared API core library for PowrStack projects",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",