aetherframework-cluster 1.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.
@@ -0,0 +1,925 @@
1
+ // packages/cluster/src/middleware/process-monitor.js
2
+ import { EventEmitter } from 'events';
3
+ import os from 'os';
4
+
5
+ /**
6
+ * Process Monitor Middleware - Monitors system and process metrics
7
+ * Provides real-time monitoring of CPU, memory, and process statistics
8
+ */
9
+ class ProcessMonitorMiddleware extends EventEmitter {
10
+ constructor(options = {}) {
11
+ super();
12
+
13
+ this.options = {
14
+ path: options.path || '/cluster/monitor',
15
+ auth: options.auth || null,
16
+ updateInterval: options.updateInterval || 5000, // 5 seconds
17
+ historySize: options.historySize || 100,
18
+ enableMetrics: options.enableMetrics !== false,
19
+ enableAlerts: options.enableAlerts !== false,
20
+ ...options
21
+ };
22
+
23
+ this.metrics = {
24
+ cpu: [],
25
+ memory: [],
26
+ process: [],
27
+ system: []
28
+ };
29
+
30
+ this.alerts = [];
31
+ this.monitoringInterval = null;
32
+ this.lastUpdate = null;
33
+
34
+ // Alert thresholds
35
+ this.thresholds = {
36
+ cpu: options.cpuThreshold || 0.8, // 80%
37
+ memory: options.memoryThreshold || 0.8, // 80%
38
+ heapUsed: options.heapThreshold || 0.9, // 90%
39
+ eventLoopDelay: options.eventLoopThreshold || 100, // 100ms
40
+ ...options.thresholds
41
+ };
42
+
43
+ // Initialize monitoring
44
+ if (this.options.enableMetrics) {
45
+ this.startMonitoring();
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Get middleware function for HTTP framework integration
51
+ * @returns {Function} Middleware function
52
+ */
53
+ middleware() {
54
+ return async (ctx, next) => {
55
+ // Only handle monitor routes
56
+ if (!ctx.path.startsWith(this.options.path)) {
57
+ return next();
58
+ }
59
+
60
+ // Check authentication if required
61
+ if (this.options.auth && !this.checkAuth(ctx)) {
62
+ ctx.status = 401;
63
+ ctx.body = {
64
+ status: 'error',
65
+ message: 'Unauthorized',
66
+ timestamp: new Date().toISOString()
67
+ };
68
+ return;
69
+ }
70
+
71
+ try {
72
+ // Handle different monitor endpoints
73
+ if (ctx.path === this.options.path) {
74
+ await this.handleMonitorOverview(ctx);
75
+ } else if (ctx.path === `${this.options.path}/metrics`) {
76
+ await this.handleMetrics(ctx);
77
+ } else if (ctx.path === `${this.options.path}/metrics/:metric`) {
78
+ await this.handleMetricDetail(ctx);
79
+ } else if (ctx.path === `${this.options.path}/alerts`) {
80
+ await this.handleAlerts(ctx);
81
+ } else if (ctx.path === `${this.options.path}/system`) {
82
+ await this.handleSystemInfo(ctx);
83
+ } else if (ctx.path === `${this.options.path}/process`) {
84
+ await this.handleProcessInfo(ctx);
85
+ } else if (ctx.path === `${this.options.path}/history`) {
86
+ await this.handleHistory(ctx);
87
+ } else if (ctx.path === `${this.options.path}/reset`) {
88
+ await this.handleReset(ctx);
89
+ } else {
90
+ ctx.status = 404;
91
+ ctx.body = {
92
+ status: 'error',
93
+ message: 'Monitor endpoint not found',
94
+ timestamp: new Date().toISOString()
95
+ };
96
+ }
97
+ } catch (error) {
98
+ console.error('Process monitor middleware error:', error);
99
+
100
+ ctx.status = 500;
101
+ ctx.body = {
102
+ status: 'error',
103
+ timestamp: new Date().toISOString(),
104
+ message: 'Failed to get monitor data',
105
+ error: error.message,
106
+ stack: process.env.NODE_ENV === 'development' ? error.stack : undefined
107
+ };
108
+ }
109
+ };
110
+ }
111
+
112
+ /**
113
+ * Handle monitor overview request
114
+ * @param {Object} ctx - Context object
115
+ */
116
+ async handleMonitorOverview(ctx) {
117
+ const metrics = this.collectMetrics();
118
+ const alerts = this.getActiveAlerts();
119
+
120
+ ctx.status = 200;
121
+ ctx.body = {
122
+ status: 'success',
123
+ timestamp: new Date().toISOString(),
124
+ overview: {
125
+ cpu: {
126
+ usage: metrics.cpu.usage,
127
+ cores: metrics.cpu.cores,
128
+ loadAverage: metrics.cpu.loadAverage,
129
+ threshold: this.thresholds.cpu,
130
+ status: metrics.cpu.usage > this.thresholds.cpu ? 'warning' : 'normal'
131
+ },
132
+ memory: {
133
+ usage: metrics.memory.usage,
134
+ total: metrics.memory.total,
135
+ free: metrics.memory.free,
136
+ threshold: this.thresholds.memory,
137
+ status: metrics.memory.usage > this.thresholds.memory ? 'warning' : 'normal'
138
+ },
139
+ process: {
140
+ uptime: metrics.process.uptime,
141
+ memory: metrics.process.memory,
142
+ cpu: metrics.process.cpu,
143
+ eventLoop: metrics.process.eventLoop,
144
+ status: this.getProcessStatus(metrics.process)
145
+ },
146
+ system: {
147
+ platform: metrics.system.platform,
148
+ arch: metrics.system.arch,
149
+ uptime: metrics.system.uptime,
150
+ hostname: metrics.system.hostname
151
+ }
152
+ },
153
+ alerts: {
154
+ active: alerts.length,
155
+ critical: alerts.filter(a => a.severity === 'critical').length,
156
+ warning: alerts.filter(a => a.severity === 'warning').length,
157
+ info: alerts.filter(a => a.severity === 'info').length
158
+ },
159
+ history: {
160
+ cpu: this.metrics.cpu.length,
161
+ memory: this.metrics.memory.length,
162
+ process: this.metrics.process.length,
163
+ system: this.metrics.system.length
164
+ }
165
+ };
166
+ }
167
+
168
+ /**
169
+ * Handle metrics request
170
+ * @param {Object} ctx - Context object
171
+ */
172
+ async handleMetrics(ctx) {
173
+ const query = ctx.query || {};
174
+ const limit = parseInt(query.limit) || 50;
175
+ const offset = parseInt(query.offset) || 0;
176
+
177
+ const metrics = {
178
+ cpu: this.metrics.cpu.slice(-limit - offset).slice(0, limit),
179
+ memory: this.metrics.memory.slice(-limit - offset).slice(0, limit),
180
+ process: this.metrics.process.slice(-limit - offset).slice(0, limit),
181
+ system: this.metrics.system.slice(-limit - offset).slice(0, limit)
182
+ };
183
+
184
+ ctx.status = 200;
185
+ ctx.body = {
186
+ status: 'success',
187
+ timestamp: new Date().toISOString(),
188
+ metrics,
189
+ pagination: {
190
+ limit,
191
+ offset,
192
+ total: {
193
+ cpu: this.metrics.cpu.length,
194
+ memory: this.metrics.memory.length,
195
+ process: this.metrics.process.length,
196
+ system: this.metrics.system.length
197
+ }
198
+ }
199
+ };
200
+ }
201
+
202
+ /**
203
+ * Handle metric detail request
204
+ * @param {Object} ctx - Context object
205
+ */
206
+ async handleMetricDetail(ctx) {
207
+ const metric = ctx.params.metric;
208
+ const query = ctx.query || {};
209
+ const limit = parseInt(query.limit) || 100;
210
+ const offset = parseInt(query.offset) || 0;
211
+
212
+ if (!['cpu', 'memory', 'process', 'system'].includes(metric)) {
213
+ ctx.status = 400;
214
+ ctx.body = {
215
+ status: 'error',
216
+ message: 'Invalid metric type',
217
+ timestamp: new Date().toISOString(),
218
+ validMetrics: ['cpu', 'memory', 'process', 'system']
219
+ };
220
+ return;
221
+ }
222
+
223
+ const metricData = this.metrics[metric];
224
+ const data = metricData.slice(-limit - offset).slice(0, limit);
225
+
226
+ ctx.status = 200;
227
+ ctx.body = {
228
+ status: 'success',
229
+ timestamp: new Date().toISOString(),
230
+ metric,
231
+ data,
232
+ summary: this.getMetricSummary(metric, data),
233
+ pagination: {
234
+ limit,
235
+ offset,
236
+ total: metricData.length
237
+ }
238
+ };
239
+ }
240
+
241
+ /**
242
+ * Handle alerts request
243
+ * @param {Object} ctx - Context object
244
+ */
245
+ async handleAlerts(ctx) {
246
+ const query = ctx.query || {};
247
+ const severity = query.severity;
248
+ const resolved = query.resolved === 'true';
249
+ const limit = parseInt(query.limit) || 50;
250
+ const offset = parseInt(query.offset) || 0;
251
+
252
+ let alerts = this.alerts;
253
+
254
+ // Filter by severity
255
+ if (severity) {
256
+ alerts = alerts.filter(alert => alert.severity === severity);
257
+ }
258
+
259
+ // Filter by resolved status
260
+ alerts = alerts.filter(alert => alert.resolved === resolved);
261
+
262
+ // Apply pagination
263
+ const paginatedAlerts = alerts.slice(-limit - offset).slice(0, limit);
264
+
265
+ ctx.status = 200;
266
+ ctx.body = {
267
+ status: 'success',
268
+ timestamp: new Date().toISOString(),
269
+ alerts: paginatedAlerts,
270
+ summary: {
271
+ total: this.alerts.length,
272
+ active: this.alerts.filter(a => !a.resolved).length,
273
+ critical: this.alerts.filter(a => a.severity === 'critical' && !a.resolved).length,
274
+ warning: this.alerts.filter(a => a.severity === 'warning' && !a.resolved).length,
275
+ info: this.alerts.filter(a => a.severity === 'info' && !a.resolved).length
276
+ },
277
+ pagination: {
278
+ limit,
279
+ offset,
280
+ total: alerts.length
281
+ }
282
+ };
283
+ }
284
+
285
+ /**
286
+ * Handle system info request
287
+ * @param {Object} ctx - Context object
288
+ */
289
+ async handleSystemInfo(ctx) {
290
+ const systemInfo = this.getSystemInfo();
291
+
292
+ ctx.status = 200;
293
+ ctx.body = {
294
+ status: 'success',
295
+ timestamp: new Date().toISOString(),
296
+ system: systemInfo
297
+ };
298
+ }
299
+
300
+ /**
301
+ * Handle process info request
302
+ * @param {Object} ctx - Context object
303
+ */
304
+ async handleProcessInfo(ctx) {
305
+ const processInfo = this.getProcessInfo();
306
+
307
+ ctx.status = 200;
308
+ ctx.body = {
309
+ status: 'success',
310
+ timestamp: new Date().toISOString(),
311
+ process: processInfo
312
+ };
313
+ }
314
+
315
+ /**
316
+ * Handle history request
317
+ * @param {Object} ctx - Context object
318
+ */
319
+ async handleHistory(ctx) {
320
+ const query = ctx.query || {};
321
+ const duration = parseInt(query.duration) || 3600000; // 1 hour in milliseconds
322
+ const now = Date.now();
323
+
324
+ const filteredMetrics = {
325
+ cpu: this.metrics.cpu.filter(m => now - m.timestamp <= duration),
326
+ memory: this.metrics.memory.filter(m => now - m.timestamp <= duration),
327
+ process: this.metrics.process.filter(m => now - m.timestamp <= duration),
328
+ system: this.metrics.system.filter(m => now - m.timestamp <= duration)
329
+ };
330
+
331
+ ctx.status = 200;
332
+ ctx.body = {
333
+ status: 'success',
334
+ timestamp: new Date().toISOString(),
335
+ duration,
336
+ metrics: filteredMetrics,
337
+ summary: {
338
+ cpu: this.getMetricSummary('cpu', filteredMetrics.cpu),
339
+ memory: this.getMetricSummary('memory', filteredMetrics.memory),
340
+ process: this.getMetricSummary('process', filteredMetrics.process)
341
+ }
342
+ };
343
+ }
344
+
345
+ /**
346
+ * Handle reset request
347
+ * @param {Object} ctx - Context object
348
+ */
349
+ async handleReset(ctx) {
350
+ // Check authentication if required
351
+ if (this.options.auth && !this.checkAuth(ctx)) {
352
+ ctx.status = 401;
353
+ ctx.body = {
354
+ status: 'error',
355
+ message: 'Unauthorized',
356
+ timestamp: new Date().toISOString()
357
+ };
358
+ return;
359
+ }
360
+
361
+ this.resetMetrics();
362
+
363
+ ctx.status = 200;
364
+ ctx.body = {
365
+ status: 'success',
366
+ message: 'Metrics reset successfully',
367
+ timestamp: new Date().toISOString()
368
+ };
369
+ }
370
+
371
+ /**
372
+ * Start monitoring
373
+ */
374
+ startMonitoring() {
375
+ if (this.monitoringInterval) {
376
+ console.warn('Process monitor already started');
377
+ return;
378
+ }
379
+
380
+ console.log('🚀 Starting process monitor...');
381
+
382
+ // Collect initial metrics
383
+ this.collectAndStoreMetrics();
384
+
385
+ // Start periodic monitoring
386
+ this.monitoringInterval = setInterval(() => {
387
+ this.collectAndStoreMetrics();
388
+ }, this.options.updateInterval);
389
+
390
+ console.log(`✅ Process monitor started with ${this.options.updateInterval}ms interval`);
391
+ }
392
+
393
+ /**
394
+ * Stop monitoring
395
+ */
396
+ stopMonitoring() {
397
+ if (this.monitoringInterval) {
398
+ clearInterval(this.monitoringInterval);
399
+ this.monitoringInterval = null;
400
+ console.log('🛑 Process monitor stopped');
401
+ }
402
+ }
403
+
404
+ /**
405
+ * Collect and store metrics
406
+ */
407
+ collectAndStoreMetrics() {
408
+ try {
409
+ const metrics = this.collectMetrics();
410
+ const timestamp = Date.now();
411
+
412
+ // Store metrics
413
+ this.metrics.cpu.push({
414
+ timestamp,
415
+ usage: metrics.cpu.usage,
416
+ loadAverage: metrics.cpu.loadAverage,
417
+ cores: metrics.cpu.cores
418
+ });
419
+
420
+ this.metrics.memory.push({
421
+ timestamp,
422
+ usage: metrics.memory.usage,
423
+ total: metrics.memory.total,
424
+ free: metrics.memory.free,
425
+ used: metrics.memory.used
426
+ });
427
+
428
+ this.metrics.process.push({
429
+ timestamp,
430
+ memory: metrics.process.memory,
431
+ cpu: metrics.process.cpu,
432
+ eventLoop: metrics.process.eventLoop,
433
+ uptime: metrics.process.uptime
434
+ });
435
+
436
+ this.metrics.system.push({
437
+ timestamp,
438
+ platform: metrics.system.platform,
439
+ arch: metrics.system.arch,
440
+ uptime: metrics.system.uptime,
441
+ hostname: metrics.system.hostname
442
+ });
443
+
444
+ // Limit history size
445
+ this.limitHistory();
446
+
447
+ // Check for alerts
448
+ this.checkAlerts(metrics);
449
+
450
+ this.lastUpdate = timestamp;
451
+ this.emit('metrics:collected', { metrics, timestamp });
452
+
453
+ } catch (error) {
454
+ console.error('Error collecting metrics:', error);
455
+ this.emit('metrics:error', { error });
456
+ }
457
+ }
458
+
459
+ /**
460
+ * Collect current metrics
461
+ * @returns {Object} Collected metrics
462
+ */
463
+ collectMetrics() {
464
+ const cpuUsage = this.getCpuUsage();
465
+ const memoryUsage = this.getMemoryUsage();
466
+ const processInfo = this.getProcessInfo();
467
+ const systemInfo = this.getSystemInfo();
468
+
469
+ return {
470
+ cpu: {
471
+ usage: cpuUsage,
472
+ loadAverage: os.loadavg(),
473
+ cores: os.cpus().length,
474
+ timestamp: Date.now()
475
+ },
476
+ memory: {
477
+ usage: memoryUsage.usage,
478
+ total: memoryUsage.total,
479
+ free: memoryUsage.free,
480
+ used: memoryUsage.used,
481
+ timestamp: Date.now()
482
+ },
483
+ process: {
484
+ memory: processInfo.memory,
485
+ cpu: processInfo.cpu,
486
+ eventLoop: processInfo.eventLoop,
487
+ uptime: processInfo.uptime,
488
+ timestamp: Date.now()
489
+ },
490
+ system: {
491
+ platform: systemInfo.platform,
492
+ arch: systemInfo.arch,
493
+ uptime: systemInfo.uptime,
494
+ hostname: systemInfo.hostname,
495
+ timestamp: Date.now()
496
+ }
497
+ };
498
+ }
499
+
500
+ /**
501
+ * Get CPU usage
502
+ * @returns {number} CPU usage percentage
503
+ */
504
+ getCpuUsage() {
505
+ const cpus = os.cpus();
506
+ let totalIdle = 0;
507
+ let totalTick = 0;
508
+
509
+ cpus.forEach(cpu => {
510
+ for (const type in cpu.times) {
511
+ totalTick += cpu.times[type];
512
+ }
513
+ totalIdle += cpu.times.idle;
514
+ });
515
+
516
+ return 1 - (totalIdle / totalTick);
517
+ }
518
+
519
+ /**
520
+ * Get memory usage
521
+ * @returns {Object} Memory usage information
522
+ */
523
+ getMemoryUsage() {
524
+ const total = os.totalmem();
525
+ const free = os.freemem();
526
+ const used = total - free;
527
+ const usage = used / total;
528
+
529
+ return {
530
+ total,
531
+ free,
532
+ used,
533
+ usage,
534
+ totalGB: (total / 1024 / 1024 / 1024).toFixed(2),
535
+ freeGB: (free / 1024 / 1024 / 1024).toFixed(2),
536
+ usedGB: (used / 1024 / 1024 / 1024).toFixed(2),
537
+ usagePercent: (usage * 100).toFixed(2)
538
+ };
539
+ }
540
+
541
+ /**
542
+ * Get process information
543
+ * @returns {Object} Process information
544
+ */
545
+ getProcessInfo() {
546
+ const memoryUsage = process.memoryUsage();
547
+ const cpuUsage = process.cpuUsage();
548
+ const eventLoop = this.getEventLoopDelay();
549
+
550
+ return {
551
+ pid: process.pid,
552
+ uptime: process.uptime(),
553
+ memory: {
554
+ rss: memoryUsage.rss,
555
+ heapTotal: memoryUsage.heapTotal,
556
+ heapUsed: memoryUsage.heapUsed,
557
+ external: memoryUsage.external,
558
+ arrayBuffers: memoryUsage.arrayBuffers,
559
+ rssMB: (memoryUsage.rss / 1024 / 1024).toFixed(2),
560
+ heapUsedMB: (memoryUsage.heapUsed / 1024 / 1024).toFixed(2),
561
+ heapTotalMB: (memoryUsage.heapTotal / 1024 / 1024).toFixed(2)
562
+ },
563
+ cpu: {
564
+ user: cpuUsage.user,
565
+ system: cpuUsage.system,
566
+ userMS: (cpuUsage.user / 1000).toFixed(2),
567
+ systemMS: (cpuUsage.system / 1000).toFixed(2)
568
+ },
569
+ eventLoop: {
570
+ delay: eventLoop,
571
+ threshold: this.thresholds.eventLoopDelay,
572
+ status: eventLoop > this.thresholds.eventLoopDelay ? 'slow' : 'normal'
573
+ },
574
+ versions: process.versions,
575
+ argv: process.argv,
576
+ execArgv: process.execArgv,
577
+ env: process.env.NODE_ENV || 'development'
578
+ };
579
+ }
580
+
581
+ /**
582
+ * Get system information
583
+ * @returns {Object} System information
584
+ */
585
+ getSystemInfo() {
586
+ return {
587
+ platform: os.platform(),
588
+ arch: os.arch(),
589
+ release: os.release(),
590
+ type: os.type(),
591
+ uptime: os.uptime(),
592
+ hostname: os.hostname(),
593
+ cpus: os.cpus().length,
594
+ totalMemory: os.totalmem(),
595
+ freeMemory: os.freemem(),
596
+ loadAverage: os.loadavg(),
597
+ networkInterfaces: os.networkInterfaces()
598
+ };
599
+ }
600
+
601
+ /**
602
+ * Get event loop delay
603
+ * @returns {number} Event loop delay in milliseconds
604
+ */
605
+ getEventLoopDelay() {
606
+ const start = process.hrtime.bigint();
607
+ // Do a small synchronous operation
608
+ for (let i = 0; i < 1000000; i++) {}
609
+ const end = process.hrtime.bigint();
610
+
611
+ // Convert nanoseconds to milliseconds
612
+ return Number(end - start) / 1000000;
613
+ }
614
+
615
+ /**
616
+ * Check for alerts based on metrics
617
+ * @param {Object} metrics - Current metrics
618
+ */
619
+ checkAlerts(metrics) {
620
+ const alerts = [];
621
+
622
+ // Check CPU threshold
623
+ if (metrics.cpu.usage > this.thresholds.cpu) {
624
+ alerts.push({
625
+ type: 'cpu',
626
+ severity: 'warning',
627
+ message: `High CPU usage: ${(metrics.cpu.usage * 100).toFixed(2)}%`,
628
+ threshold: this.thresholds.cpu,
629
+ value: metrics.cpu.usage,
630
+ timestamp: Date.now(),
631
+ resolved: false
632
+ });
633
+ }
634
+
635
+ // Check memory threshold
636
+ if (metrics.memory.usage > this.thresholds.memory) {
637
+ alerts.push({
638
+ type: 'memory',
639
+ severity: 'warning',
640
+ message: `High memory usage: ${(metrics.memory.usage * 100).toFixed(2)}%`,
641
+ threshold: this.thresholds.memory,
642
+ value: metrics.memory.usage,
643
+ timestamp: Date.now(),
644
+ resolved: false
645
+ });
646
+ }
647
+
648
+ // Check heap usage threshold
649
+ const heapUsage = metrics.process.memory.heapUsed / metrics.process.memory.heapTotal;
650
+ if (heapUsage > this.thresholds.heapUsed) {
651
+ alerts.push({
652
+ type: 'heap',
653
+ severity: 'critical',
654
+ message: `High heap usage: ${(heapUsage * 100).toFixed(2)}%`,
655
+ threshold: this.thresholds.heapUsed,
656
+ value: heapUsage,
657
+ timestamp: Date.now(),
658
+ resolved: false
659
+ });
660
+ }
661
+
662
+ // Check event loop delay
663
+ if (metrics.process.eventLoop.delay > this.thresholds.eventLoopDelay) {
664
+ alerts.push({
665
+ type: 'eventLoop',
666
+ severity: 'warning',
667
+ message: `High event loop delay: ${metrics.process.eventLoop.delay.toFixed(2)}ms`,
668
+ threshold: this.thresholds.eventLoopDelay,
669
+ value: metrics.process.eventLoop.delay,
670
+ timestamp: Date.now(),
671
+ resolved: false
672
+ });
673
+ }
674
+
675
+ // Add new alerts
676
+ alerts.forEach(alert => {
677
+ this.alerts.push(alert);
678
+ this.emit('alert:created', alert);
679
+ });
680
+
681
+ // Check for resolved alerts
682
+ this.checkResolvedAlerts(metrics);
683
+ }
684
+
685
+ /**
686
+ * Check for resolved alerts
687
+ * @param {Object} metrics - Current metrics
688
+ */
689
+ checkResolvedAlerts(metrics) {
690
+ const now = Date.now();
691
+
692
+ this.alerts.forEach(alert => {
693
+ if (!alert.resolved) {
694
+ let isResolved = false;
695
+
696
+ switch (alert.type) {
697
+ case 'cpu':
698
+ isResolved = metrics.cpu.usage <= this.thresholds.cpu * 0.8; // Resolved when below 80% of threshold
699
+ break;
700
+ case 'memory':
701
+ isResolved = metrics.memory.usage <= this.thresholds.memory * 0.8;
702
+ break;
703
+ case 'heap':
704
+ const heapUsage = metrics.process.memory.heapUsed / metrics.process.memory.heapTotal;
705
+ isResolved = heapUsage <= this.thresholds.heapUsed * 0.8;
706
+ break;
707
+ case 'eventLoop':
708
+ isResolved = metrics.process.eventLoop.delay <= this.thresholds.eventLoopDelay * 0.8;
709
+ break;
710
+ }
711
+
712
+ if (isResolved) {
713
+ alert.resolved = true;
714
+ alert.resolvedAt = now;
715
+ alert.resolutionTime = now - alert.timestamp;
716
+ this.emit('alert:resolved', alert);
717
+ }
718
+ }
719
+ });
720
+ }
721
+
722
+ /**
723
+ * Get active alerts
724
+ * @returns {Array} Active alerts
725
+ */
726
+ getActiveAlerts() {
727
+ return this.alerts.filter(alert => !alert.resolved);
728
+ }
729
+
730
+ /**
731
+ * Get process status
732
+ * @param {Object} processInfo - Process information
733
+ * @returns {string} Process status
734
+ */
735
+ getProcessStatus(processInfo) {
736
+ const heapUsage = processInfo.memory.heapUsed / processInfo.memory.heapTotal;
737
+
738
+ if (heapUsage > this.thresholds.heapUsed) {
739
+ return 'critical';
740
+ }
741
+
742
+ if (processInfo.eventLoop.delay > this.thresholds.eventLoopDelay) {
743
+ return 'warning';
744
+ }
745
+
746
+ return 'normal';
747
+ }
748
+
749
+ /**
750
+ * Get metric summary
751
+ * @param {string} metric - Metric type
752
+ * @param {Array} data - Metric data
753
+ * @returns {Object} Metric summary
754
+ */
755
+ getMetricSummary(metric, data) {
756
+ if (data.length === 0) {
757
+ return {
758
+ count: 0,
759
+ average: 0,
760
+ min: 0,
761
+ max: 0,
762
+ latest: null
763
+ };
764
+ }
765
+
766
+ let values = [];
767
+
768
+ switch (metric) {
769
+ case 'cpu':
770
+ values = data.map(d => d.usage);
771
+ break;
772
+ case 'memory':
773
+ values = data.map(d => d.usage);
774
+ break;
775
+ case 'process':
776
+ values = data.map(d => d.memory.heapUsed / d.memory.heapTotal);
777
+ break;
778
+ default:
779
+ values = [];
780
+ }
781
+
782
+ const sum = values.reduce((a, b) => a + b, 0);
783
+ const average = sum / values.length;
784
+ const min = Math.min(...values);
785
+ const max = Math.max(...values);
786
+ const latest = data[data.length - 1];
787
+
788
+ return {
789
+ count: data.length,
790
+ average: average.toFixed(4),
791
+ min: min.toFixed(4),
792
+ max: max.toFixed(4),
793
+ latest: latest ? {
794
+ value: values[values.length - 1],
795
+ timestamp: latest.timestamp
796
+ } : null,
797
+ trend: this.getTrend(values)
798
+ };
799
+ }
800
+
801
+ /**
802
+ * Get trend from values
803
+ * @param {Array} values - Array of values
804
+ * @returns {string} Trend (up, down, stable)
805
+ */
806
+ getTrend(values) {
807
+ if (values.length < 2) {
808
+ return 'stable';
809
+ }
810
+
811
+ const recent = values.slice(-5); // Last 5 values
812
+ const first = recent[0];
813
+ const last = recent[recent.length - 1];
814
+
815
+ if (last > first * 1.1) {
816
+ return 'up';
817
+ } else if (last < first * 0.9) {
818
+ return 'down';
819
+ } else {
820
+ return 'stable';
821
+ }
822
+ }
823
+
824
+ /**
825
+ * Limit history size
826
+ */
827
+ limitHistory() {
828
+ if (this.metrics.cpu.length > this.options.historySize) {
829
+ this.metrics.cpu = this.metrics.cpu.slice(-this.options.historySize);
830
+ }
831
+
832
+ if (this.metrics.memory.length > this.options.historySize) {
833
+ this.metrics.memory = this.metrics.memory.slice(-this.options.historySize);
834
+ }
835
+
836
+ if (this.metrics.process.length > this.options.historySize) {
837
+ this.metrics.process = this.metrics.process.slice(-this.options.historySize);
838
+ }
839
+
840
+ if (this.metrics.system.length > this.options.historySize) {
841
+ this.metrics.system = this.metrics.system.slice(-this.options.historySize);
842
+ }
843
+
844
+ if (this.alerts.length > this.options.historySize * 2) {
845
+ this.alerts = this.alerts.slice(-this.options.historySize * 2);
846
+ }
847
+ }
848
+
849
+ /**
850
+ * Reset metrics
851
+ */
852
+ resetMetrics() {
853
+ this.metrics = {
854
+ cpu: [],
855
+ memory: [],
856
+ process: [],
857
+ system: []
858
+ };
859
+
860
+ this.alerts = [];
861
+ this.lastUpdate = null;
862
+
863
+ console.log('🔄 Process monitor metrics reset');
864
+ this.emit('metrics:reset');
865
+ }
866
+
867
+ /**
868
+ * Check authentication
869
+ * @param {Object} ctx - Context object
870
+ * @returns {boolean} Authentication status
871
+ */
872
+ checkAuth(ctx) {
873
+ if (typeof this.options.auth === 'function') {
874
+ return this.options.auth(ctx);
875
+ }
876
+
877
+ if (typeof this.options.auth === 'object') {
878
+ // Check API key
879
+ if (this.options.auth.apiKey) {
880
+ const apiKey = ctx.headers['x-api-key'] || ctx.query.apiKey;
881
+ return apiKey === this.options.auth.apiKey;
882
+ }
883
+
884
+ // Check basic auth
885
+ if (this.options.auth.username && this.options.auth.password) {
886
+ const authHeader = ctx.headers.authorization;
887
+ if (!authHeader || !authHeader.startsWith('Basic ')) {
888
+ return false;
889
+ }
890
+
891
+ const credentials = Buffer.from(authHeader.slice(6), 'base64').toString();
892
+ const [username, password] = credentials.split(':');
893
+ return username === this.options.auth.username && password === this.options.auth.password;
894
+ }
895
+ }
896
+
897
+ return true; // No auth required
898
+ }
899
+
900
+ /**
901
+ * Get monitoring configuration
902
+ * @returns {Object} Monitoring configuration
903
+ */
904
+ getConfig() {
905
+ return {
906
+ ...this.options,
907
+ thresholds: this.thresholds,
908
+ isMonitoring: !!this.monitoringInterval,
909
+ lastUpdate: this.lastUpdate,
910
+ metricsCount: {
911
+ cpu: this.metrics.cpu.length,
912
+ memory: this.metrics.memory.length,
913
+ process: this.metrics.process.length,
914
+ system: this.metrics.system.length
915
+ },
916
+ alertsCount: {
917
+ total: this.alerts.length,
918
+ active: this.getActiveAlerts().length,
919
+ resolved: this.alerts.filter(a => a.resolved).length
920
+ }
921
+ };
922
+ }
923
+ }
924
+
925
+ export default ProcessMonitorMiddleware;