@feardread/fear 1.0.1
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/FEAR.js +459 -0
- package/FEARServer.js +280 -0
- package/controllers/agent.js +438 -0
- package/controllers/auth/index.js +345 -0
- package/controllers/auth/token.js +50 -0
- package/controllers/blog.js +105 -0
- package/controllers/brand.js +10 -0
- package/controllers/cart.js +425 -0
- package/controllers/category.js +9 -0
- package/controllers/coupon.js +63 -0
- package/controllers/crud/crud.js +508 -0
- package/controllers/crud/index.js +36 -0
- package/controllers/email.js +34 -0
- package/controllers/enquiry.js +65 -0
- package/controllers/events.js +9 -0
- package/controllers/order.js +125 -0
- package/controllers/payment.js +31 -0
- package/controllers/product.js +147 -0
- package/controllers/review.js +247 -0
- package/controllers/tag.js +10 -0
- package/controllers/task.js +10 -0
- package/controllers/upload.js +41 -0
- package/controllers/user.js +401 -0
- package/index.js +7 -0
- package/libs/agent/index.js +561 -0
- package/libs/agent/modules/ai/ai.js +285 -0
- package/libs/agent/modules/ai/chat.js +518 -0
- package/libs/agent/modules/ai/config.js +688 -0
- package/libs/agent/modules/ai/operations.js +787 -0
- package/libs/agent/modules/analyze/api.js +546 -0
- package/libs/agent/modules/analyze/dorks.js +395 -0
- package/libs/agent/modules/ccard/README.md +454 -0
- package/libs/agent/modules/ccard/audit.js +479 -0
- package/libs/agent/modules/ccard/checker.js +674 -0
- package/libs/agent/modules/ccard/payment-processors.json +16 -0
- package/libs/agent/modules/ccard/validator.js +629 -0
- package/libs/agent/modules/code/analyzer.js +303 -0
- package/libs/agent/modules/code/jquery.js +1093 -0
- package/libs/agent/modules/code/react.js +1536 -0
- package/libs/agent/modules/code/refactor.js +499 -0
- package/libs/agent/modules/crypto/exchange.js +564 -0
- package/libs/agent/modules/net/proxy.js +409 -0
- package/libs/agent/modules/security/cve.js +442 -0
- package/libs/agent/modules/security/monitor.js +360 -0
- package/libs/agent/modules/security/scanner.js +300 -0
- package/libs/agent/modules/security/vulnerability.js +506 -0
- package/libs/agent/modules/security/web.js +465 -0
- package/libs/agent/modules/utils/browser.js +492 -0
- package/libs/agent/modules/utils/colorizer.js +285 -0
- package/libs/agent/modules/utils/manager.js +478 -0
- package/libs/cloud/index.js +228 -0
- package/libs/config/db.js +21 -0
- package/libs/config/validator.js +82 -0
- package/libs/db/index.js +318 -0
- package/libs/emailer/imap.js +126 -0
- package/libs/emailer/info.js +41 -0
- package/libs/emailer/smtp.js +77 -0
- package/libs/handler/async.js +3 -0
- package/libs/handler/error.js +66 -0
- package/libs/handler/index.js +161 -0
- package/libs/logger/index.js +49 -0
- package/libs/logger/morgan.js +24 -0
- package/libs/passport/passport.js +109 -0
- package/libs/search/api.js +384 -0
- package/libs/search/features.js +219 -0
- package/libs/search/service.js +64 -0
- package/libs/swagger/config.js +18 -0
- package/libs/swagger/index.js +35 -0
- package/libs/validator/index.js +254 -0
- package/models/blog.js +31 -0
- package/models/brand.js +12 -0
- package/models/cart.js +14 -0
- package/models/category.js +11 -0
- package/models/coupon.js +9 -0
- package/models/customer.js +0 -0
- package/models/enquiry.js +29 -0
- package/models/events.js +13 -0
- package/models/order.js +94 -0
- package/models/product.js +32 -0
- package/models/review.js +14 -0
- package/models/tag.js +10 -0
- package/models/task.js +11 -0
- package/models/user.js +68 -0
- package/package.json +12 -0
- package/routes/agent.js +615 -0
- package/routes/auth.js +13 -0
- package/routes/blog.js +19 -0
- package/routes/brand.js +15 -0
- package/routes/cart.js +105 -0
- package/routes/category.js +16 -0
- package/routes/coupon.js +15 -0
- package/routes/enquiry.js +14 -0
- package/routes/events.js +16 -0
- package/routes/mail.js +170 -0
- package/routes/order.js +19 -0
- package/routes/product.js +22 -0
- package/routes/review.js +11 -0
- package/routes/task.js +12 -0
- package/routes/user.js +17 -0
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
// modules/services/manager.js - Background Service Manager
|
|
2
|
+
const fs = require('fs').promises;
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const colorizer = require('../utils/colorizer');
|
|
5
|
+
const EventEmitter = require('events');
|
|
6
|
+
|
|
7
|
+
const ServiceManager = function(agent) {
|
|
8
|
+
this.agent = agent;
|
|
9
|
+
this.services = new Map();
|
|
10
|
+
this.logs = new Map();
|
|
11
|
+
this.maxLogEntries = 1000;
|
|
12
|
+
|
|
13
|
+
// Register built-in services
|
|
14
|
+
this.registerBuiltInServices();
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
ServiceManager.prototype = {
|
|
18
|
+
|
|
19
|
+
registerBuiltInServices() {
|
|
20
|
+
// Auto-scan service - monitors for security issues
|
|
21
|
+
this.registerService('auto-scan', {
|
|
22
|
+
name: 'Auto Security Scanner',
|
|
23
|
+
description: 'Periodic security scanning of project files',
|
|
24
|
+
interval: 300000, // 5 minutes
|
|
25
|
+
action: () => this.runAutoScan()
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Log monitor service - watches for suspicious patterns
|
|
29
|
+
this.registerService('log-monitor', {
|
|
30
|
+
name: 'Log Monitor',
|
|
31
|
+
description: 'Monitors application logs for security events',
|
|
32
|
+
interval: 60000, // 1 minute
|
|
33
|
+
action: () => this.runLogMonitor()
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// CVE checker service - checks for new vulnerabilities
|
|
37
|
+
this.registerService('cve-check', {
|
|
38
|
+
name: 'CVE Vulnerability Checker',
|
|
39
|
+
description: 'Checks dependencies for new CVEs',
|
|
40
|
+
interval: 3600000, // 1 hour
|
|
41
|
+
action: () => this.runCVECheck()
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Network monitor service - monitors network activity
|
|
45
|
+
this.registerService('net-monitor', {
|
|
46
|
+
name: 'Network Monitor',
|
|
47
|
+
description: 'Monitors network connections and traffic',
|
|
48
|
+
interval: 120000, // 2 minutes
|
|
49
|
+
action: () => this.runNetworkMonitor()
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// File watcher service - watches for file changes
|
|
53
|
+
this.registerService('file-watch', {
|
|
54
|
+
name: 'File Watcher',
|
|
55
|
+
description: 'Watches project files for suspicious changes',
|
|
56
|
+
interval: 30000, // 30 seconds
|
|
57
|
+
action: () => this.runFileWatcher()
|
|
58
|
+
});
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
registerService(id, config) {
|
|
62
|
+
const service = {
|
|
63
|
+
id,
|
|
64
|
+
name: config.name,
|
|
65
|
+
description: config.description,
|
|
66
|
+
interval: config.interval,
|
|
67
|
+
action: config.action,
|
|
68
|
+
status: 'stopped',
|
|
69
|
+
timer: null,
|
|
70
|
+
lastRun: null,
|
|
71
|
+
runCount: 0,
|
|
72
|
+
errors: 0,
|
|
73
|
+
emitter: new EventEmitter()
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
this.services.set(id, service);
|
|
77
|
+
this.logs.set(id, []);
|
|
78
|
+
|
|
79
|
+
// Register with agent if available
|
|
80
|
+
if (this.agent) {
|
|
81
|
+
this.agent.registerService(id, {
|
|
82
|
+
start: () => this.startService([id]),
|
|
83
|
+
stop: () => this.stopService([id])
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
startService(args) {
|
|
89
|
+
const serviceId = args[0];
|
|
90
|
+
|
|
91
|
+
if (!serviceId) {
|
|
92
|
+
console.log(colorizer.error('Usage: service-start <service-id>'));
|
|
93
|
+
console.log(colorizer.info('Run "service-list" to see available services\n'));
|
|
94
|
+
return Promise.resolve();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const service = this.services.get(serviceId);
|
|
98
|
+
|
|
99
|
+
if (!service) {
|
|
100
|
+
console.log(colorizer.error(`Service "${serviceId}" not found\n`));
|
|
101
|
+
return Promise.resolve();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (service.status === 'running') {
|
|
105
|
+
console.log(colorizer.warning(`Service "${service.name}" is already running\n`));
|
|
106
|
+
return Promise.resolve();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
service.status = 'running';
|
|
110
|
+
|
|
111
|
+
// Run immediately
|
|
112
|
+
this.executeService(service);
|
|
113
|
+
|
|
114
|
+
// Schedule periodic execution
|
|
115
|
+
service.timer = setInterval(() => {
|
|
116
|
+
this.executeService(service);
|
|
117
|
+
}, service.interval);
|
|
118
|
+
|
|
119
|
+
this.log(serviceId, 'info', 'Service started');
|
|
120
|
+
console.log(colorizer.success(`✓ Started: ${service.name}`));
|
|
121
|
+
console.log(colorizer.dim(` Interval: ${this.formatInterval(service.interval)}\n`));
|
|
122
|
+
|
|
123
|
+
return Promise.resolve();
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
stopService(args) {
|
|
127
|
+
const serviceId = args[0];
|
|
128
|
+
|
|
129
|
+
if (!serviceId) {
|
|
130
|
+
console.log(colorizer.error('Usage: service-stop <service-id>'));
|
|
131
|
+
console.log(colorizer.info('Run "service-list" to see running services\n'));
|
|
132
|
+
return Promise.resolve();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const service = this.services.get(serviceId);
|
|
136
|
+
|
|
137
|
+
if (!service) {
|
|
138
|
+
console.log(colorizer.error(`Service "${serviceId}" not found\n`));
|
|
139
|
+
return Promise.resolve();
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (service.status === 'stopped') {
|
|
143
|
+
console.log(colorizer.warning(`Service "${service.name}" is not running\n`));
|
|
144
|
+
return Promise.resolve();
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (service.timer) {
|
|
148
|
+
clearInterval(service.timer);
|
|
149
|
+
service.timer = null;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
service.status = 'stopped';
|
|
153
|
+
this.log(serviceId, 'info', 'Service stopped');
|
|
154
|
+
|
|
155
|
+
console.log(colorizer.success(`✓ Stopped: ${service.name}\n`));
|
|
156
|
+
return Promise.resolve();
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
restartService(args) {
|
|
160
|
+
const serviceId = args[0];
|
|
161
|
+
|
|
162
|
+
if (!serviceId) {
|
|
163
|
+
console.log(colorizer.error('Usage: service-restart <service-id>\n'));
|
|
164
|
+
return Promise.resolve();
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
console.log(colorizer.info(`Restarting ${serviceId}...`));
|
|
168
|
+
|
|
169
|
+
return this.stopService([serviceId])
|
|
170
|
+
.then(() => new Promise(resolve => setTimeout(resolve, 1000)))
|
|
171
|
+
.then(() => this.startService([serviceId]));
|
|
172
|
+
},
|
|
173
|
+
|
|
174
|
+
executeService(service) {
|
|
175
|
+
try {
|
|
176
|
+
service.lastRun = new Date();
|
|
177
|
+
service.runCount++;
|
|
178
|
+
|
|
179
|
+
Promise.resolve(service.action())
|
|
180
|
+
.then(result => {
|
|
181
|
+
if (result && result.alert) {
|
|
182
|
+
this.log(service.id, 'alert', result.message);
|
|
183
|
+
console.log(colorizer.warning(`\n⚠️ ${service.name}: ${result.message}`));
|
|
184
|
+
} else if (result && result.info) {
|
|
185
|
+
this.log(service.id, 'info', result.message);
|
|
186
|
+
}
|
|
187
|
+
})
|
|
188
|
+
.catch(err => {
|
|
189
|
+
service.errors++;
|
|
190
|
+
this.log(service.id, 'error', err.message);
|
|
191
|
+
|
|
192
|
+
if (service.errors > 5) {
|
|
193
|
+
console.log(colorizer.error(`\n❌ ${service.name} encountered multiple errors. Stopping...`));
|
|
194
|
+
this.stopService([service.id]);
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
} catch (err) {
|
|
198
|
+
service.errors++;
|
|
199
|
+
this.log(service.id, 'error', err.message);
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
serviceStatus(args) {
|
|
204
|
+
const serviceId = args[0];
|
|
205
|
+
|
|
206
|
+
if (serviceId) {
|
|
207
|
+
return this.showServiceDetail(serviceId);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
console.log(colorizer.header('Background Services Status'));
|
|
211
|
+
console.log(colorizer.separator());
|
|
212
|
+
|
|
213
|
+
if (this.services.size === 0) {
|
|
214
|
+
console.log(colorizer.dim('No services registered\n'));
|
|
215
|
+
return Promise.resolve();
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
this.services.forEach(service => {
|
|
219
|
+
const status = service.status === 'running' ?
|
|
220
|
+
colorizer.green('● RUNNING') :
|
|
221
|
+
colorizer.dim('○ STOPPED');
|
|
222
|
+
|
|
223
|
+
console.log(`${status} ${colorizer.cyan(service.name)}`);
|
|
224
|
+
console.log(colorizer.dim(` ID: ${service.id}`));
|
|
225
|
+
console.log(colorizer.dim(` ${service.description}`));
|
|
226
|
+
|
|
227
|
+
if (service.status === 'running') {
|
|
228
|
+
console.log(colorizer.dim(` Interval: ${this.formatInterval(service.interval)}`));
|
|
229
|
+
console.log(colorizer.dim(` Runs: ${service.runCount}`));
|
|
230
|
+
if (service.lastRun) {
|
|
231
|
+
console.log(colorizer.dim(` Last run: ${service.lastRun.toLocaleString()}`));
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (service.errors > 0) {
|
|
236
|
+
console.log(colorizer.warning(` Errors: ${service.errors}`));
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
console.log();
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
return Promise.resolve();
|
|
243
|
+
},
|
|
244
|
+
|
|
245
|
+
showServiceDetail(serviceId) {
|
|
246
|
+
const service = this.services.get(serviceId);
|
|
247
|
+
|
|
248
|
+
if (!service) {
|
|
249
|
+
console.log(colorizer.error(`Service "${serviceId}" not found\n`));
|
|
250
|
+
return Promise.resolve();
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
console.log(colorizer.header(`Service: ${service.name}`));
|
|
254
|
+
console.log(colorizer.separator());
|
|
255
|
+
|
|
256
|
+
console.log(colorizer.cyan('ID: ') + service.id);
|
|
257
|
+
console.log(colorizer.cyan('Description: ') + service.description);
|
|
258
|
+
console.log(colorizer.cyan('Status: ') +
|
|
259
|
+
(service.status === 'running' ? colorizer.green('Running') : colorizer.dim('Stopped')));
|
|
260
|
+
console.log(colorizer.cyan('Interval: ') + this.formatInterval(service.interval));
|
|
261
|
+
console.log(colorizer.cyan('Total Runs: ') + service.runCount);
|
|
262
|
+
console.log(colorizer.cyan('Errors: ') +
|
|
263
|
+
(service.errors > 0 ? colorizer.warning(service.errors) : colorizer.green('0')));
|
|
264
|
+
|
|
265
|
+
if (service.lastRun) {
|
|
266
|
+
console.log(colorizer.cyan('Last Run: ') + service.lastRun.toLocaleString());
|
|
267
|
+
const nextRun = new Date(service.lastRun.getTime() + service.interval);
|
|
268
|
+
console.log(colorizer.cyan('Next Run: ') + nextRun.toLocaleString());
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
console.log();
|
|
272
|
+
|
|
273
|
+
// Show recent logs
|
|
274
|
+
const logs = this.logs.get(serviceId) || [];
|
|
275
|
+
if (logs.length > 0) {
|
|
276
|
+
console.log(colorizer.section('Recent Activity'));
|
|
277
|
+
logs.slice(-10).forEach(log => {
|
|
278
|
+
const icon = log.level === 'error' ? '❌' :
|
|
279
|
+
log.level === 'alert' ? '⚠️' :
|
|
280
|
+
'ℹ️';
|
|
281
|
+
const color = log.level === 'error' ? colorizer.error :
|
|
282
|
+
log.level === 'alert' ? colorizer.warning :
|
|
283
|
+
colorizer.dim;
|
|
284
|
+
console.log(`${icon} ${color(`[${log.time}] ${log.message}`)}`);
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
console.log();
|
|
289
|
+
return Promise.resolve();
|
|
290
|
+
},
|
|
291
|
+
|
|
292
|
+
listServices(args) {
|
|
293
|
+
console.log(colorizer.header('Available Background Services'));
|
|
294
|
+
console.log(colorizer.separator());
|
|
295
|
+
|
|
296
|
+
if (this.services.size === 0) {
|
|
297
|
+
console.log(colorizer.dim('No services available\n'));
|
|
298
|
+
return Promise.resolve();
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
console.log(colorizer.section('Service List'));
|
|
302
|
+
|
|
303
|
+
this.services.forEach(service => {
|
|
304
|
+
const status = service.status === 'running' ?
|
|
305
|
+
colorizer.green('[RUNNING]') :
|
|
306
|
+
colorizer.dim('[STOPPED]');
|
|
307
|
+
|
|
308
|
+
console.log(`${status} ${colorizer.cyan(service.id)}`);
|
|
309
|
+
console.log(colorizer.dim(` ${service.name} - ${service.description}`));
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
console.log();
|
|
313
|
+
console.log(colorizer.info('Commands:'));
|
|
314
|
+
console.log(colorizer.dim(' service-start <id> - Start a service'));
|
|
315
|
+
console.log(colorizer.dim(' service-stop <id> - Stop a service'));
|
|
316
|
+
console.log(colorizer.dim(' service-status <id> - View service details'));
|
|
317
|
+
console.log(colorizer.dim(' service-logs <id> - View service logs'));
|
|
318
|
+
console.log(colorizer.dim(' service-restart <id> - Restart a service'));
|
|
319
|
+
console.log();
|
|
320
|
+
|
|
321
|
+
return Promise.resolve();
|
|
322
|
+
},
|
|
323
|
+
|
|
324
|
+
showLogs(args) {
|
|
325
|
+
const serviceId = args[0];
|
|
326
|
+
const limit = parseInt(args[1]) || 50;
|
|
327
|
+
|
|
328
|
+
if (!serviceId) {
|
|
329
|
+
console.log(colorizer.error('Usage: service-logs <service-id> [limit]'));
|
|
330
|
+
console.log(colorizer.info('Example: service-logs auto-scan 100\n'));
|
|
331
|
+
return Promise.resolve();
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const service = this.services.get(serviceId);
|
|
335
|
+
const logs = this.logs.get(serviceId);
|
|
336
|
+
|
|
337
|
+
if (!service) {
|
|
338
|
+
console.log(colorizer.error(`Service "${serviceId}" not found\n`));
|
|
339
|
+
return Promise.resolve();
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
console.log(colorizer.header(`Logs: ${service.name}`));
|
|
343
|
+
console.log(colorizer.separator());
|
|
344
|
+
|
|
345
|
+
if (!logs || logs.length === 0) {
|
|
346
|
+
console.log(colorizer.dim('No logs available\n'));
|
|
347
|
+
return Promise.resolve();
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const recentLogs = logs.slice(-limit);
|
|
351
|
+
|
|
352
|
+
console.log(colorizer.cyan(`Showing last ${recentLogs.length} entries:\n`));
|
|
353
|
+
|
|
354
|
+
recentLogs.forEach(log => {
|
|
355
|
+
const icon = log.level === 'error' ? '❌' :
|
|
356
|
+
log.level === 'alert' ? '⚠️' :
|
|
357
|
+
log.level === 'info' ? 'ℹ️' :
|
|
358
|
+
'📝';
|
|
359
|
+
|
|
360
|
+
const color = log.level === 'error' ? colorizer.error :
|
|
361
|
+
log.level === 'alert' ? colorizer.warning :
|
|
362
|
+
colorizer.dim;
|
|
363
|
+
|
|
364
|
+
console.log(`${icon} ${color(`[${log.timestamp}] ${log.message}`)}`);
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
console.log();
|
|
368
|
+
return Promise.resolve();
|
|
369
|
+
},
|
|
370
|
+
|
|
371
|
+
log(serviceId, level, message) {
|
|
372
|
+
const logs = this.logs.get(serviceId);
|
|
373
|
+
if (!logs) return;
|
|
374
|
+
|
|
375
|
+
const entry = {
|
|
376
|
+
timestamp: new Date().toISOString(),
|
|
377
|
+
time: new Date().toLocaleTimeString(),
|
|
378
|
+
level,
|
|
379
|
+
message
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
logs.push(entry);
|
|
383
|
+
|
|
384
|
+
// Trim logs if too many
|
|
385
|
+
if (logs.length > this.maxLogEntries) {
|
|
386
|
+
logs.splice(0, logs.length - this.maxLogEntries);
|
|
387
|
+
}
|
|
388
|
+
},
|
|
389
|
+
|
|
390
|
+
formatInterval(ms) {
|
|
391
|
+
const seconds = Math.floor(ms / 1000);
|
|
392
|
+
const minutes = Math.floor(seconds / 60);
|
|
393
|
+
const hours = Math.floor(minutes / 60);
|
|
394
|
+
|
|
395
|
+
if (hours > 0) {
|
|
396
|
+
return `${hours}h ${minutes % 60}m`;
|
|
397
|
+
} else if (minutes > 0) {
|
|
398
|
+
return `${minutes}m ${seconds % 60}s`;
|
|
399
|
+
} else {
|
|
400
|
+
return `${seconds}s`;
|
|
401
|
+
}
|
|
402
|
+
},
|
|
403
|
+
|
|
404
|
+
// Built-in service actions
|
|
405
|
+
|
|
406
|
+
runAutoScan() {
|
|
407
|
+
// Scan project files for security issues
|
|
408
|
+
const scanResults = {
|
|
409
|
+
filesScanned: 0,
|
|
410
|
+
issues: []
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
// This would integrate with the scanner module
|
|
414
|
+
if (this.agent && this.agent.modules.scanner) {
|
|
415
|
+
// Perform quick security audit
|
|
416
|
+
return Promise.resolve({
|
|
417
|
+
info: true,
|
|
418
|
+
message: `Scanned ${scanResults.filesScanned} files`
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
return Promise.resolve();
|
|
423
|
+
},
|
|
424
|
+
|
|
425
|
+
runLogMonitor() {
|
|
426
|
+
// Monitor logs for suspicious patterns
|
|
427
|
+
const logsDir = './logs';
|
|
428
|
+
|
|
429
|
+
return fs.readdir(logsDir)
|
|
430
|
+
.then(files => {
|
|
431
|
+
const logFiles = files.filter(f => f.endsWith('.log'));
|
|
432
|
+
|
|
433
|
+
if (logFiles.length > 0) {
|
|
434
|
+
return {
|
|
435
|
+
info: true,
|
|
436
|
+
message: `Monitored ${logFiles.length} log files`
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
})
|
|
440
|
+
.catch(() => null);
|
|
441
|
+
},
|
|
442
|
+
|
|
443
|
+
runCVECheck() {
|
|
444
|
+
// Check for CVE updates
|
|
445
|
+
if (this.agent && this.agent.modules.cveDatabase) {
|
|
446
|
+
return Promise.resolve({
|
|
447
|
+
info: true,
|
|
448
|
+
message: 'CVE database check completed'
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
return Promise.resolve();
|
|
452
|
+
},
|
|
453
|
+
|
|
454
|
+
runNetworkMonitor() {
|
|
455
|
+
// Monitor network connections
|
|
456
|
+
return Promise.resolve({
|
|
457
|
+
info: true,
|
|
458
|
+
message: 'Network monitoring active'
|
|
459
|
+
});
|
|
460
|
+
},
|
|
461
|
+
|
|
462
|
+
runFileWatcher() {
|
|
463
|
+
// Watch for file changes
|
|
464
|
+
const watchDir = process.cwd();
|
|
465
|
+
|
|
466
|
+
return fs.readdir(watchDir)
|
|
467
|
+
.then(files => {
|
|
468
|
+
// This would track file modifications
|
|
469
|
+
return {
|
|
470
|
+
info: true,
|
|
471
|
+
message: `Watching ${files.length} files`
|
|
472
|
+
};
|
|
473
|
+
})
|
|
474
|
+
.catch(() => null);
|
|
475
|
+
}
|
|
476
|
+
};
|
|
477
|
+
|
|
478
|
+
module.exports = ServiceManager;
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
const cloudinary = require("cloudinary").v2;
|
|
2
|
+
const multer = require("multer");
|
|
3
|
+
const sharp = require("sharp");
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
require("dotenv").config();
|
|
7
|
+
|
|
8
|
+
// Initialize Cloudinary configuration
|
|
9
|
+
cloudinary.config({
|
|
10
|
+
cloud_name: process.env.CLOUDINARY_NAME,
|
|
11
|
+
api_key: process.env.API_KEY,
|
|
12
|
+
api_secret: process.env.API_SECRET,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Converts file buffer to base64 data URL
|
|
17
|
+
* @param {Object} file - File object with mimetype and buffer
|
|
18
|
+
* @returns {string} Base64 data URL
|
|
19
|
+
*/
|
|
20
|
+
const convertToBase64 = (file) => {
|
|
21
|
+
return `data:${file.mimetype};base64,${file.buffer.toString("base64")}`;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Uploads avatar to Cloudinary
|
|
26
|
+
* @param {string} file - Base64 encoded file or file path
|
|
27
|
+
* @returns {Promise<Object>} Avatar object with public_id and url
|
|
28
|
+
*/
|
|
29
|
+
const uploadAvatar = async (file) => {
|
|
30
|
+
try {
|
|
31
|
+
const result = await cloudinary.uploader.upload(file, {
|
|
32
|
+
folder: "avatar",
|
|
33
|
+
width: 150,
|
|
34
|
+
crop: "scale",
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
public_id: result.public_id,
|
|
39
|
+
url: result.secure_url,
|
|
40
|
+
};
|
|
41
|
+
} catch (error) {
|
|
42
|
+
throw new Error(`Avatar upload failed: ${error.message}`);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Uploads multiple images to Cloudinary in chunks to avoid rate limiting
|
|
48
|
+
* @param {string|string[]} files - Single file or array of files
|
|
49
|
+
* @param {number} chunkSize - Number of files to upload concurrently
|
|
50
|
+
* @returns {Promise<Object[]>} Array of image objects with public_id and url
|
|
51
|
+
*/
|
|
52
|
+
const uploadImages = async (files, chunkSize = 3) => {
|
|
53
|
+
try {
|
|
54
|
+
// Normalize input to array
|
|
55
|
+
const imageArray = Array.isArray(files) ? [...files] : [files];
|
|
56
|
+
|
|
57
|
+
if (imageArray.length === 0) {
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const imageLinks = [];
|
|
62
|
+
|
|
63
|
+
// Process images in chunks to avoid overwhelming the API
|
|
64
|
+
for (let i = 0; i < imageArray.length; i += chunkSize) {
|
|
65
|
+
const chunk = imageArray.slice(i, i + chunkSize);
|
|
66
|
+
|
|
67
|
+
const uploadPromises = chunk.map((image) =>
|
|
68
|
+
cloudinary.uploader.upload(image, {
|
|
69
|
+
folder: "products",
|
|
70
|
+
})
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const results = await Promise.all(uploadPromises);
|
|
74
|
+
|
|
75
|
+
const chunkResults = results.map((result) => ({
|
|
76
|
+
product_id: result.public_id,
|
|
77
|
+
url: result.secure_url,
|
|
78
|
+
}));
|
|
79
|
+
|
|
80
|
+
imageLinks.push(...chunkResults);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return imageLinks;
|
|
84
|
+
} catch (error) {
|
|
85
|
+
throw new Error(`Image upload failed: ${error.message}`);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Multer disk storage configuration
|
|
91
|
+
*/
|
|
92
|
+
const storage = multer.diskStorage({
|
|
93
|
+
destination: (req, file, cb) => {
|
|
94
|
+
const uploadPath = path.join(__dirname, "../public/images/");
|
|
95
|
+
cb(null, uploadPath);
|
|
96
|
+
},
|
|
97
|
+
filename: (req, file, cb) => {
|
|
98
|
+
const uniqueSuffix = `${Date.now()}-${Math.round(Math.random() * 1e9)}`;
|
|
99
|
+
const extension = path.extname(file.originalname) || '.jpeg';
|
|
100
|
+
cb(null, `${file.fieldname}-${uniqueSuffix}${extension}`);
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Multer file filter for images only
|
|
106
|
+
*/
|
|
107
|
+
const multerFilter = (req, file, cb) => {
|
|
108
|
+
if (file.mimetype.startsWith("image/")) {
|
|
109
|
+
cb(null, true);
|
|
110
|
+
} else {
|
|
111
|
+
cb(new Error("Unsupported file format. Only images are allowed."), false);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Multer upload configuration
|
|
117
|
+
*/
|
|
118
|
+
const uploadPhoto = multer({
|
|
119
|
+
storage,
|
|
120
|
+
fileFilter: multerFilter,
|
|
121
|
+
limits: {
|
|
122
|
+
fileSize: 1000000, // 1MB
|
|
123
|
+
files: 10 // Maximum 10 files
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Middleware to resize uploaded images
|
|
129
|
+
* @param {Object} req - Express request object
|
|
130
|
+
* @param {Object} res - Express response object
|
|
131
|
+
* @param {Function} next - Express next function
|
|
132
|
+
*/
|
|
133
|
+
const resizeImages = async (req, res, next) => {
|
|
134
|
+
try {
|
|
135
|
+
// Skip if no files uploaded or no directory specified
|
|
136
|
+
if (!req.files || !req.directory) {
|
|
137
|
+
return next();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Ensure directory exists
|
|
141
|
+
const targetDir = path.join("public/images", req.directory);
|
|
142
|
+
if (!fs.existsSync(targetDir)) {
|
|
143
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Process all files concurrently
|
|
147
|
+
await Promise.all(
|
|
148
|
+
req.files.map(async (file) => {
|
|
149
|
+
const originalPath = file.path;
|
|
150
|
+
const targetPath = path.join(targetDir, file.filename);
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
// Resize and convert image
|
|
154
|
+
await sharp(originalPath)
|
|
155
|
+
.resize(300, 300, {
|
|
156
|
+
fit: 'cover',
|
|
157
|
+
position: 'center'
|
|
158
|
+
})
|
|
159
|
+
.jpeg({ quality: 90 })
|
|
160
|
+
.toFile(targetPath);
|
|
161
|
+
|
|
162
|
+
// Clean up original file
|
|
163
|
+
if (fs.existsSync(originalPath)) {
|
|
164
|
+
fs.unlinkSync(originalPath);
|
|
165
|
+
}
|
|
166
|
+
} catch (imageError) {
|
|
167
|
+
console.error(`Failed to process image ${file.filename}:`, imageError);
|
|
168
|
+
// Clean up files on error
|
|
169
|
+
[originalPath, targetPath].forEach(filePath => {
|
|
170
|
+
if (fs.existsSync(filePath)) {
|
|
171
|
+
fs.unlinkSync(filePath);
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
throw imageError;
|
|
175
|
+
}
|
|
176
|
+
})
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
next();
|
|
180
|
+
} catch (error) {
|
|
181
|
+
next(new Error(`Image resize failed: ${error.message}`));
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Delete image from Cloudinary
|
|
187
|
+
* @param {string} publicId - Public ID of the image to delete
|
|
188
|
+
* @returns {Promise<Object>} Deletion result
|
|
189
|
+
*/
|
|
190
|
+
const deleteImage = async (publicId) => {
|
|
191
|
+
try {
|
|
192
|
+
const result = await cloudinary.uploader.destroy(publicId);
|
|
193
|
+
return result;
|
|
194
|
+
} catch (error) {
|
|
195
|
+
throw new Error(`Image deletion failed: ${error.message}`);
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Get Cloudinary image transformation URL
|
|
201
|
+
* @param {string} publicId - Public ID of the image
|
|
202
|
+
* @param {Object} transformations - Transformation options
|
|
203
|
+
* @returns {string} Transformed image URL
|
|
204
|
+
*/
|
|
205
|
+
const getTransformedImageUrl = (publicId, transformations = {}) => {
|
|
206
|
+
return cloudinary.url(publicId, {
|
|
207
|
+
secure: true,
|
|
208
|
+
...transformations,
|
|
209
|
+
});
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
module.exports = {
|
|
213
|
+
// Core functions
|
|
214
|
+
convertToBase64,
|
|
215
|
+
uploadAvatar,
|
|
216
|
+
uploadImages,
|
|
217
|
+
deleteImage,
|
|
218
|
+
getTransformedImageUrl,
|
|
219
|
+
|
|
220
|
+
// Multer configuration
|
|
221
|
+
storage,
|
|
222
|
+
multerFilter,
|
|
223
|
+
uploadPhoto,
|
|
224
|
+
resizeImages,
|
|
225
|
+
|
|
226
|
+
// Cloudinary instance (if needed for direct access)
|
|
227
|
+
cloudinary,
|
|
228
|
+
};
|