powr-sdk-api 4.3.12 → 4.3.13
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/admin/activities.js +81 -0
- package/dist/admin/auth.js +234 -0
- package/dist/admin/blogs.js +94 -0
- package/dist/admin/comments.js +83 -0
- package/dist/admin/files.js +56 -0
- package/dist/admin/forms.js +242 -0
- package/dist/admin/index.js +53 -0
- package/dist/admin/invoice.js +163 -0
- package/dist/admin/likes.js +126 -0
- package/dist/admin/notifications.js +93 -0
- package/dist/admin/plexx.js +53 -0
- package/dist/admin/ratings.js +189 -0
- package/dist/admin/routes.js +132 -0
- package/dist/admin/slides.js +101 -0
- package/dist/admin/users.js +175 -0
- package/dist/admin/waitlists.js +94 -0
- package/dist/index.js +3 -1
- package/dist/logger/gcs.js +78 -0
- package/dist/logger/s3.js +78 -0
- package/dist/middleware/auth.js +76 -0
- package/dist/middleware/logger.js +45 -0
- package/dist/middleware/response.js +53 -0
- package/dist/routes/admin/index.js +520 -0
- package/dist/routes/blogs.js +94 -0
- package/dist/routes/index.js +25 -1
- package/dist/routes/invoice.js +167 -0
- package/dist/routes/plexx.js +269 -0
- package/dist/routes/routes.js +143 -0
- package/dist/services/dbService.js +42 -0
- package/dist/services/functions.js +229 -0
- package/dist/services/logger.js +35 -0
- package/dist/services/plexx.js +229 -0
- package/dist/utils/auth.js +19 -0
- package/dist/utils/logger.js +57 -0
- package/dist/utils/s3-transport.js +61 -0
- package/dist/utils/s3.js +78 -0
- package/package.json +69 -69
|
@@ -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();
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const winston = require('winston');
|
|
4
|
+
const {
|
|
5
|
+
S3Transport
|
|
6
|
+
} = require('../utils/s3');
|
|
7
|
+
|
|
8
|
+
// Custom format for logs
|
|
9
|
+
const logFormat = winston.format.combine(winston.format.timestamp(), winston.format.errors({
|
|
10
|
+
stack: true
|
|
11
|
+
}), winston.format.json());
|
|
12
|
+
|
|
13
|
+
// Create base transports array with console transport
|
|
14
|
+
const transports = [new winston.transports.Console()];
|
|
15
|
+
|
|
16
|
+
// Add S3 transport only in production
|
|
17
|
+
if (process.env.NODE_ENV === 'production') {
|
|
18
|
+
transports.push(new S3Transport({
|
|
19
|
+
bucket: process.env.AWS_LOG_BUCKET_NAME,
|
|
20
|
+
prefix: 'logs',
|
|
21
|
+
bufferSize: 100,
|
|
22
|
+
flushInterval: 5000
|
|
23
|
+
}));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Create the logger
|
|
27
|
+
const logger = winston.createLogger({
|
|
28
|
+
level: process.env.LOG_LEVEL || 'info',
|
|
29
|
+
format: logFormat,
|
|
30
|
+
defaultMeta: {
|
|
31
|
+
service: process.env.LOG_SERVICE_NAME
|
|
32
|
+
},
|
|
33
|
+
transports
|
|
34
|
+
});
|
|
35
|
+
module.exports = logger;
|
|
@@ -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 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 db = await getDb();
|
|
34
|
+
const routes = await db.collection("routes").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} routes for project ${projectId}`);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
console.error("❌ Failed to initialize routes:", 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 route: ${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: "Plexx 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 route 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: `Route not found: ${route}`,
|
|
95
|
+
data: null
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
const result = cached.function(params);
|
|
100
|
+
return {
|
|
101
|
+
success: true,
|
|
102
|
+
message: "Route executed successfully",
|
|
103
|
+
data: result
|
|
104
|
+
};
|
|
105
|
+
} catch (error) {
|
|
106
|
+
return {
|
|
107
|
+
success: false,
|
|
108
|
+
message: `Error executing route: ${error.message}`,
|
|
109
|
+
data: null,
|
|
110
|
+
error: error.message
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Load specific route 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("routes").findOne({
|
|
123
|
+
projectId,
|
|
124
|
+
route
|
|
125
|
+
});
|
|
126
|
+
if (routeData) {
|
|
127
|
+
this.compileAndCache(routeData);
|
|
128
|
+
console.log(`✅ Loaded route: ${route} for project ${projectId}`);
|
|
129
|
+
} else {
|
|
130
|
+
console.log(`❌ Route not found: ${route} for project ${projectId}`);
|
|
131
|
+
}
|
|
132
|
+
} catch (error) {
|
|
133
|
+
console.error(`❌ Failed to load route ${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("routes").find({
|
|
146
|
+
projectId
|
|
147
|
+
}).toArray();
|
|
148
|
+
routes.forEach(route => {
|
|
149
|
+
this.compileAndCache(route);
|
|
150
|
+
});
|
|
151
|
+
console.log(`✅ Loaded ${routes.length} routes for project ${projectId}`);
|
|
152
|
+
} catch (error) {
|
|
153
|
+
console.error(`❌ Failed to load routes 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 route (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("routes").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 route in database: ${route}`, error);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Get route statistics
|
|
196
|
+
getStats() {
|
|
197
|
+
return {
|
|
198
|
+
totalRoutes: 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("🧹 Route cache cleared");
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Enable/Disable Plexx
|
|
213
|
+
enable() {
|
|
214
|
+
this.isEnabled = true;
|
|
215
|
+
console.log("✅ Plexx enabled");
|
|
216
|
+
}
|
|
217
|
+
disable() {
|
|
218
|
+
this.isEnabled = false;
|
|
219
|
+
console.log("🚫 Plexx disabled");
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Toggle Plexx
|
|
223
|
+
toggle() {
|
|
224
|
+
this.isEnabled = !this.isEnabled;
|
|
225
|
+
console.log(this.isEnabled ? "✅ Plexx enabled" : "🚫 Plexx disabled");
|
|
226
|
+
return this.isEnabled;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
module.exports = new PlexxManager();
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const jwt = require('jsonwebtoken');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Generates a JWT token for a user
|
|
7
|
+
* @param {Object} user - The user object
|
|
8
|
+
* @returns {string} - The generated JWT token
|
|
9
|
+
*/
|
|
10
|
+
const generateToken = user => {
|
|
11
|
+
return jwt.sign({
|
|
12
|
+
userId: user.userId
|
|
13
|
+
}, process.env.JWT_SECRET, {
|
|
14
|
+
expiresIn: process.env.JWT_EXPIRES_IN || '24h'
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
module.exports = {
|
|
18
|
+
generateToken
|
|
19
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Logger utility for Express applications
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const winston = require('winston');
|
|
8
|
+
const S3Transport = require('./s3-transport');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Creates a Winston logger instance with configured transports
|
|
12
|
+
* @param {Object} options - Logger configuration options
|
|
13
|
+
* @param {string} options.service - Service name for logging
|
|
14
|
+
* @param {string} options.level - Log level (default: 'info')
|
|
15
|
+
* @param {Object} options.s3 - S3 transport configuration
|
|
16
|
+
* @param {string} options.s3.bucket - S3 bucket name
|
|
17
|
+
* @param {string} options.s3.prefix - S3 key prefix
|
|
18
|
+
* @param {number} options.s3.bufferSize - Buffer size for S3 uploads
|
|
19
|
+
* @param {number} options.s3.flushInterval - Flush interval in milliseconds
|
|
20
|
+
* @returns {winston.Logger} Configured Winston logger instance
|
|
21
|
+
*/
|
|
22
|
+
const createLogger = (options = {}) => {
|
|
23
|
+
const {
|
|
24
|
+
service = 'api',
|
|
25
|
+
level = process.env.LOG_LEVEL || 'info',
|
|
26
|
+
s3 = null
|
|
27
|
+
} = options;
|
|
28
|
+
|
|
29
|
+
// Custom format for logs
|
|
30
|
+
const logFormat = winston.format.combine(winston.format.timestamp(), winston.format.errors({
|
|
31
|
+
stack: true
|
|
32
|
+
}), winston.format.json());
|
|
33
|
+
|
|
34
|
+
// Create base transports array with console transport
|
|
35
|
+
const transports = [new winston.transports.Console()];
|
|
36
|
+
|
|
37
|
+
// Add S3 transport if configured
|
|
38
|
+
if (s3 && process.env.NODE_ENV === 'production') {
|
|
39
|
+
transports.push(new S3Transport({
|
|
40
|
+
bucket: s3.bucket,
|
|
41
|
+
prefix: s3.prefix || 'logs',
|
|
42
|
+
bufferSize: s3.bufferSize || 100,
|
|
43
|
+
flushInterval: s3.flushInterval || 5000
|
|
44
|
+
}));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Create and return the logger
|
|
48
|
+
return winston.createLogger({
|
|
49
|
+
level,
|
|
50
|
+
format: logFormat,
|
|
51
|
+
defaultMeta: {
|
|
52
|
+
service
|
|
53
|
+
},
|
|
54
|
+
transports
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
module.exports = createLogger;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* S3 Transport for Winston logger
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const winston = require('winston');
|
|
8
|
+
const {
|
|
9
|
+
S3Client,
|
|
10
|
+
PutObjectCommand
|
|
11
|
+
} = require('@aws-sdk/client-s3');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Custom Winston transport for logging to S3
|
|
15
|
+
*/
|
|
16
|
+
class S3Transport extends winston.Transport {
|
|
17
|
+
constructor(opts) {
|
|
18
|
+
super(opts);
|
|
19
|
+
this.bucket = opts.bucket;
|
|
20
|
+
this.prefix = opts.prefix || '';
|
|
21
|
+
this.buffer = [];
|
|
22
|
+
this.bufferSize = opts.bufferSize || 100;
|
|
23
|
+
this.flushInterval = opts.flushInterval || 5000;
|
|
24
|
+
this.setupFlushInterval();
|
|
25
|
+
}
|
|
26
|
+
setupFlushInterval() {
|
|
27
|
+
setInterval(() => {
|
|
28
|
+
this.flush();
|
|
29
|
+
}, this.flushInterval);
|
|
30
|
+
}
|
|
31
|
+
async flush() {
|
|
32
|
+
if (this.buffer.length === 0) return;
|
|
33
|
+
const logs = this.buffer.splice(0, this.buffer.length);
|
|
34
|
+
const date = new Date().toISOString().split('T')[0];
|
|
35
|
+
const key = `${this.prefix}/${date}/${Date.now()}.json`;
|
|
36
|
+
try {
|
|
37
|
+
const command = new PutObjectCommand({
|
|
38
|
+
Bucket: this.bucket,
|
|
39
|
+
Key: key,
|
|
40
|
+
Body: JSON.stringify(logs),
|
|
41
|
+
ContentType: 'application/json'
|
|
42
|
+
});
|
|
43
|
+
await this.s3Client.send(command);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error('Error flushing logs to S3:', error);
|
|
46
|
+
// Put logs back in buffer
|
|
47
|
+
this.buffer.unshift(...logs);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
log(info, callback) {
|
|
51
|
+
setImmediate(() => {
|
|
52
|
+
this.emit('logged', info);
|
|
53
|
+
});
|
|
54
|
+
this.buffer.push(info);
|
|
55
|
+
if (this.buffer.length >= this.bufferSize) {
|
|
56
|
+
this.flush();
|
|
57
|
+
}
|
|
58
|
+
callback();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
module.exports = S3Transport;
|
package/dist/utils/s3.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const winston = require('winston');
|
|
4
|
+
const {
|
|
5
|
+
S3Client,
|
|
6
|
+
PutObjectCommand
|
|
7
|
+
} = require('@aws-sdk/client-s3');
|
|
8
|
+
|
|
9
|
+
// Create S3 client
|
|
10
|
+
const s3Client = new S3Client({
|
|
11
|
+
region: process.env.AWS_REGION,
|
|
12
|
+
credentials: {
|
|
13
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
14
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Custom Winston transport for logging to S3
|
|
20
|
+
*/
|
|
21
|
+
class S3Transport extends winston.Transport {
|
|
22
|
+
constructor(opts) {
|
|
23
|
+
super(opts);
|
|
24
|
+
this.bucket = opts.bucket;
|
|
25
|
+
this.prefix = opts.prefix || '';
|
|
26
|
+
this.buffer = [];
|
|
27
|
+
this.bufferSize = opts.bufferSize || 100;
|
|
28
|
+
this.flushInterval = opts.flushInterval || 5000;
|
|
29
|
+
this.setupFlushInterval();
|
|
30
|
+
}
|
|
31
|
+
setupFlushInterval() {
|
|
32
|
+
setInterval(() => {
|
|
33
|
+
this.flush();
|
|
34
|
+
}, this.flushInterval);
|
|
35
|
+
}
|
|
36
|
+
async flush() {
|
|
37
|
+
if (this.buffer.length === 0) return;
|
|
38
|
+
const logs = this.buffer.splice(0, this.buffer.length);
|
|
39
|
+
const date = new Date().toISOString().split('T')[0];
|
|
40
|
+
const key = `${this.prefix}/${date}/${Date.now()}.json`;
|
|
41
|
+
try {
|
|
42
|
+
const command = new PutObjectCommand({
|
|
43
|
+
Bucket: this.bucket,
|
|
44
|
+
Key: key,
|
|
45
|
+
Body: JSON.stringify(logs),
|
|
46
|
+
ContentType: 'application/json'
|
|
47
|
+
});
|
|
48
|
+
await s3Client.send(command);
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.error('Failed to write logs to S3:', {
|
|
51
|
+
error: error.message,
|
|
52
|
+
code: error.code,
|
|
53
|
+
bucket: this.bucket,
|
|
54
|
+
key: key,
|
|
55
|
+
region: process.env.AWS_REGION,
|
|
56
|
+
stack: error.stack
|
|
57
|
+
});
|
|
58
|
+
// Put the logs back in the buffer
|
|
59
|
+
this.buffer.unshift(...logs);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
log(info, callback) {
|
|
63
|
+
setImmediate(() => {
|
|
64
|
+
this.emit('logged', info);
|
|
65
|
+
});
|
|
66
|
+
this.buffer.push({
|
|
67
|
+
timestamp: new Date().toISOString(),
|
|
68
|
+
...info
|
|
69
|
+
});
|
|
70
|
+
if (this.buffer.length >= this.bufferSize) {
|
|
71
|
+
this.flush();
|
|
72
|
+
}
|
|
73
|
+
callback();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
module.exports = {
|
|
77
|
+
S3Transport
|
|
78
|
+
};
|