pms_md 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.
Files changed (55) hide show
  1. package/README.md +93 -0
  2. package/node-monitor/ARCHITECTURE.md +341 -0
  3. package/node-monitor/CHANGELOG.md +105 -0
  4. package/node-monitor/CONTRIBUTING.md +96 -0
  5. package/node-monitor/DESIGN_IMPROVEMENTS.md +286 -0
  6. package/node-monitor/FILTER_BUTTONS_FIX.md +303 -0
  7. package/node-monitor/GETTING_STARTED.md +416 -0
  8. package/node-monitor/INSTALLATION.md +470 -0
  9. package/node-monitor/LICENSE +22 -0
  10. package/node-monitor/PUBLISHING_GUIDE.md +331 -0
  11. package/node-monitor/QUICK_REFERENCE.md +252 -0
  12. package/node-monitor/README.md +458 -0
  13. package/node-monitor/READY_TO_PUBLISH.md +272 -0
  14. package/node-monitor/SETUP_GUIDE.md +479 -0
  15. package/node-monitor/examples/EMAIL_SETUP_GUIDE.md +282 -0
  16. package/node-monitor/examples/ERROR_LOGGING_GUIDE.md +405 -0
  17. package/node-monitor/examples/GET_APP_PASSWORD.md +145 -0
  18. package/node-monitor/examples/LOG_FILES_REFERENCE.md +336 -0
  19. package/node-monitor/examples/QUICK_START_EMAIL.md +126 -0
  20. package/node-monitor/examples/express-app.js +499 -0
  21. package/node-monitor/examples/package-lock.json +1295 -0
  22. package/node-monitor/examples/package.json +18 -0
  23. package/node-monitor/examples/public/css/style.css +718 -0
  24. package/node-monitor/examples/public/js/dashboard.js +207 -0
  25. package/node-monitor/examples/public/js/health.js +114 -0
  26. package/node-monitor/examples/public/js/main.js +89 -0
  27. package/node-monitor/examples/public/js/metrics.js +225 -0
  28. package/node-monitor/examples/public/js/theme.js +138 -0
  29. package/node-monitor/examples/views/dashboard.ejs +20 -0
  30. package/node-monitor/examples/views/error-logs.ejs +1129 -0
  31. package/node-monitor/examples/views/health.ejs +21 -0
  32. package/node-monitor/examples/views/home.ejs +341 -0
  33. package/node-monitor/examples/views/layout.ejs +50 -0
  34. package/node-monitor/examples/views/metrics.ejs +16 -0
  35. package/node-monitor/examples/views/partials/footer.ejs +16 -0
  36. package/node-monitor/examples/views/partials/header.ejs +35 -0
  37. package/node-monitor/examples/views/partials/nav.ejs +23 -0
  38. package/node-monitor/examples/views/status.ejs +390 -0
  39. package/node-monitor/package-lock.json +4300 -0
  40. package/node-monitor/package.json +76 -0
  41. package/node-monitor/pre-publish-check.js +200 -0
  42. package/node-monitor/src/config/monitoringConfig.js +255 -0
  43. package/node-monitor/src/index.js +300 -0
  44. package/node-monitor/src/logger/errorLogger.js +297 -0
  45. package/node-monitor/src/monitors/apiErrorMonitor.js +156 -0
  46. package/node-monitor/src/monitors/dbConnectionMonitor.js +389 -0
  47. package/node-monitor/src/monitors/serverHealthMonitor.js +320 -0
  48. package/node-monitor/src/monitors/systemResourceMonitor.js +357 -0
  49. package/node-monitor/src/notifiers/emailNotifier.js +248 -0
  50. package/node-monitor/src/notifiers/notificationManager.js +96 -0
  51. package/node-monitor/src/notifiers/slackNotifier.js +209 -0
  52. package/node-monitor/src/views/dashboard.html +530 -0
  53. package/node-monitor/src/views/health.html +399 -0
  54. package/node-monitor/src/views/metrics.html +406 -0
  55. package/package.json +22 -0
@@ -0,0 +1,207 @@
1
+ // ===================================
2
+ // DASHBOARD FUNCTIONS
3
+ // ===================================
4
+
5
+ async function loadDashboard() {
6
+ try {
7
+ const response = await fetch('/monitor/status?format=json');
8
+ const data = await response.json();
9
+ renderDashboard(data);
10
+ updateTimestamp();
11
+ } catch (error) {
12
+ showError('dashboardContent', error.message);
13
+ }
14
+ }
15
+
16
+ function renderDashboard(data) {
17
+ const isHealthy = data.health?.isHealthy ?? true;
18
+
19
+ // Update status badge
20
+ const statusBadge = document.getElementById('overallStatus');
21
+ const statusText = document.getElementById('statusText');
22
+
23
+ if (statusBadge && statusText) {
24
+ statusBadge.className = `status-badge ${isHealthy ? 'healthy' : 'unhealthy'}`;
25
+ statusText.textContent = isHealthy ? 'Healthy' : 'Unhealthy';
26
+ }
27
+
28
+ // Render content
29
+ const content = `
30
+ <div class="grid">
31
+ ${renderApplicationCard(data)}
32
+ ${renderHealthCard(data.health)}
33
+ ${renderSystemCard(data.system)}
34
+ ${renderErrorCard(data.errors)}
35
+ </div>
36
+ `;
37
+
38
+ document.getElementById('dashboardContent').innerHTML = content;
39
+
40
+ // Render stats summary
41
+ renderStatsSummary(data);
42
+ }
43
+
44
+ function renderApplicationCard(data) {
45
+ const uptime = data.health?.uptime || 0;
46
+
47
+ return `
48
+ <div class="card">
49
+ <div class="card-header">
50
+ <span class="card-icon">🚀</span>
51
+ <h2 class="card-title">Application</h2>
52
+ </div>
53
+ <div class="metric">
54
+ <span class="metric-label">Status</span>
55
+ <span class="metric-value good">${data.isRunning ? 'Running' : 'Stopped'}</span>
56
+ </div>
57
+ <div class="metric">
58
+ <span class="metric-label">Uptime</span>
59
+ <span class="metric-value">${formatUptime(uptime)}</span>
60
+ </div>
61
+ <div class="metric">
62
+ <span class="metric-label">Environment</span>
63
+ <span class="metric-value">${data.environment || 'development'}</span>
64
+ </div>
65
+ <div class="metric">
66
+ <span class="metric-label">Monitoring</span>
67
+ <span class="metric-value good">Active</span>
68
+ </div>
69
+ </div>
70
+ `;
71
+ }
72
+
73
+ function renderHealthCard(health) {
74
+ if (!health) return '';
75
+
76
+ return `
77
+ <div class="card">
78
+ <div class="card-header">
79
+ <span class="card-icon">💚</span>
80
+ <h2 class="card-title">Health Checks</h2>
81
+ </div>
82
+ <div class="metric">
83
+ <span class="metric-label">Overall Status</span>
84
+ <span class="metric-value ${health.isHealthy ? 'good' : 'danger'}">
85
+ ${health.isHealthy ? '✓ Healthy' : '✗ Unhealthy'}
86
+ </span>
87
+ </div>
88
+ <div class="metric">
89
+ <span class="metric-label">Registered Checks</span>
90
+ <span class="metric-value">${health.registeredChecks || 0}</span>
91
+ </div>
92
+ <div class="metric">
93
+ <span class="metric-label">Consecutive Failures</span>
94
+ <span class="metric-value ${health.consecutiveFailures > 0 ? 'warning' : 'good'}">
95
+ ${health.consecutiveFailures || 0}
96
+ </span>
97
+ </div>
98
+ </div>
99
+ `;
100
+ }
101
+
102
+ function renderSystemCard(system) {
103
+ if (!system) return '';
104
+
105
+ const cpuPercent = system.cpu?.system || 0;
106
+ const memoryPercent = system.memory?.system?.usagePercent || 0;
107
+
108
+ return `
109
+ <div class="card">
110
+ <div class="card-header">
111
+ <span class="card-icon">💻</span>
112
+ <h2 class="card-title">System Resources</h2>
113
+ </div>
114
+ <div class="metric">
115
+ <span class="metric-label">CPU Usage</span>
116
+ <span class="metric-value ${getCpuClass(cpuPercent)}">${cpuPercent.toFixed(1)}%</span>
117
+ </div>
118
+ <div class="progress-bar">
119
+ <div class="progress-fill ${getCpuClass(cpuPercent)}" style="width: ${cpuPercent}%"></div>
120
+ </div>
121
+ <div class="metric" style="margin-top: 15px;">
122
+ <span class="metric-label">Memory Usage</span>
123
+ <span class="metric-value ${getMemoryClass(memoryPercent)}">${memoryPercent.toFixed(1)}%</span>
124
+ </div>
125
+ <div class="progress-bar">
126
+ <div class="progress-fill ${getMemoryClass(memoryPercent)}" style="width: ${memoryPercent}%"></div>
127
+ </div>
128
+ <div class="metric" style="margin-top: 15px;">
129
+ <span class="metric-label">Heap Used</span>
130
+ <span class="metric-value">${formatBytes(system.memory?.heapUsed || 0)}</span>
131
+ </div>
132
+ </div>
133
+ `;
134
+ }
135
+
136
+ function renderErrorCard(errors) {
137
+ if (!errors) return '';
138
+
139
+ return `
140
+ <div class="card">
141
+ <div class="card-header">
142
+ <span class="card-icon">⚠️</span>
143
+ <h2 class="card-title">Error Tracking</h2>
144
+ </div>
145
+ <div class="metric">
146
+ <span class="metric-label">Current Error Rate</span>
147
+ <span class="metric-value ${errors.currentErrorRate > 10 ? 'danger' : 'good'}">
148
+ ${errors.currentErrorRate || 0} /min
149
+ </span>
150
+ </div>
151
+ <div class="metric">
152
+ <span class="metric-label">Threshold</span>
153
+ <span class="metric-value">${errors.threshold || 0} /min</span>
154
+ </div>
155
+ <div class="metric">
156
+ <span class="metric-label">API Error Rate</span>
157
+ <span class="metric-value">${errors.apiErrorRate || 0} /min</span>
158
+ </div>
159
+ </div>
160
+ `;
161
+ }
162
+
163
+ function renderStatsSummary(data) {
164
+ const summaryEl = document.getElementById('statsSummary');
165
+ if (!summaryEl) return;
166
+
167
+ const uptime = data.health?.uptime || 0;
168
+ const cpuPercent = data.system?.cpu?.system || 0;
169
+ const memoryPercent = data.system?.memory?.system?.usagePercent || 0;
170
+
171
+ summaryEl.innerHTML = `
172
+ <div class="stats-grid">
173
+ <div class="card" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white;">
174
+ <div style="text-align: center;">
175
+ <div style="font-size: 0.9em; opacity: 0.9;">Uptime</div>
176
+ <div style="font-size: 2em; font-weight: bold; margin: 10px 0;">${formatUptime(uptime)}</div>
177
+ </div>
178
+ </div>
179
+ <div class="card" style="background: linear-gradient(135deg, #10b981 0%, #059669 100%); color: white;">
180
+ <div style="text-align: center;">
181
+ <div style="font-size: 0.9em; opacity: 0.9;">CPU Usage</div>
182
+ <div style="font-size: 2em; font-weight: bold; margin: 10px 0;">${cpuPercent.toFixed(1)}%</div>
183
+ </div>
184
+ </div>
185
+ <div class="card" style="background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); color: white;">
186
+ <div style="text-align: center;">
187
+ <div style="font-size: 0.9em; opacity: 0.9;">Memory Usage</div>
188
+ <div style="font-size: 2em; font-weight: bold; margin: 10px 0;">${memoryPercent.toFixed(1)}%</div>
189
+ </div>
190
+ </div>
191
+ <div class="card" style="background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); color: white;">
192
+ <div style="text-align: center;">
193
+ <div style="font-size: 0.9em; opacity: 0.9;">Error Rate</div>
194
+ <div style="font-size: 2em; font-weight: bold; margin: 10px 0;">${data.errors?.currentErrorRate || 0}/min</div>
195
+ </div>
196
+ </div>
197
+ </div>
198
+ `;
199
+ }
200
+
201
+ // Load dashboard on page load
202
+ document.addEventListener('DOMContentLoaded', function() {
203
+ loadDashboard();
204
+ // Auto-refresh every 30 seconds
205
+ setInterval(loadDashboard, 30000);
206
+ });
207
+
@@ -0,0 +1,114 @@
1
+ // ===================================
2
+ // HEALTH CHECK FUNCTIONS
3
+ // ===================================
4
+
5
+ async function loadHealth() {
6
+ try {
7
+ const response = await fetch('/health?format=json');
8
+ const data = await response.json();
9
+ renderHealth(data);
10
+ updateTimestamp();
11
+ } catch (error) {
12
+ showError('healthContent', error.message);
13
+ }
14
+ }
15
+
16
+ function renderHealth(data) {
17
+ const isHealthy = data.status === 'healthy';
18
+
19
+ // Update status indicator
20
+ const indicator = document.getElementById('statusIndicator');
21
+ const statusText = document.getElementById('statusText');
22
+
23
+ if (indicator && statusText) {
24
+ if (isHealthy) {
25
+ indicator.className = 'status-indicator healthy';
26
+ statusText.textContent = 'All Systems Operational';
27
+ } else {
28
+ indicator.className = 'status-indicator unhealthy';
29
+ statusText.textContent = 'System Issues Detected';
30
+ }
31
+ }
32
+
33
+ // Render content
34
+ const content = `
35
+ <div class="card">
36
+ <h2 style="display: flex; align-items: center; gap: 10px; margin-bottom: 20px;">
37
+ 📊 Overview
38
+ </h2>
39
+ <div class="stats-grid">
40
+ <div class="card" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white;">
41
+ <div style="text-align: center;">
42
+ <div style="font-size: 0.9em; opacity: 0.9;">Status</div>
43
+ <div style="font-size: 3em; margin: 10px 0;">${isHealthy ? '✓' : '✗'}</div>
44
+ <div style="font-size: 1.1em; font-weight: bold;">${data.status}</div>
45
+ </div>
46
+ </div>
47
+ <div class="card" style="background: linear-gradient(135deg, #10b981 0%, #059669 100%); color: white;">
48
+ <div style="text-align: center;">
49
+ <div style="font-size: 0.9em; opacity: 0.9;">Uptime</div>
50
+ <div style="font-size: 2.5em; font-weight: bold; margin: 10px 0;">${formatUptime(data.uptime)}</div>
51
+ </div>
52
+ </div>
53
+ <div class="card" style="background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); color: white;">
54
+ <div style="text-align: center;">
55
+ <div style="font-size: 0.9em; opacity: 0.9;">Checks</div>
56
+ <div style="font-size: 2.5em; font-weight: bold; margin: 10px 0;">${Object.keys(data.checks || {}).length}</div>
57
+ <div style="font-size: 0.9em;">registered</div>
58
+ </div>
59
+ </div>
60
+ </div>
61
+ </div>
62
+
63
+ ${renderChecks(data.checks)}
64
+ `;
65
+
66
+ document.getElementById('healthContent').innerHTML = content;
67
+ }
68
+
69
+ function renderChecks(checks) {
70
+ if (!checks || Object.keys(checks).length === 0) {
71
+ return `
72
+ <div class="card">
73
+ <h2 style="display: flex; align-items: center; gap: 10px;">
74
+ ✅ Health Checks
75
+ </h2>
76
+ <p style="text-align: center; color: #666; padding: 20px;">
77
+ No custom health checks registered
78
+ </p>
79
+ </div>
80
+ `;
81
+ }
82
+
83
+ const checkItems = Object.entries(checks).map(([name, check]) => {
84
+ const isPassing = check.status === 'pass';
85
+ return `
86
+ <div class="card" style="border-left: 5px solid ${isPassing ? '#10b981' : '#ef4444'};">
87
+ <div style="display: flex; justify-content: space-between; align-items: center;">
88
+ <div>
89
+ <h3 style="color: #333; margin-bottom: 5px;">${formatCheckName(name)}</h3>
90
+ <p style="color: #666; font-size: 0.9em;">Health check</p>
91
+ </div>
92
+ <div style="padding: 8px 20px; border-radius: 25px; font-weight: bold; background: ${isPassing ? '#d1fae5' : '#fee2e2'}; color: ${isPassing ? '#065f46' : '#991b1b'};">
93
+ ${isPassing ? '✓ Pass' : '✗ Fail'}
94
+ </div>
95
+ </div>
96
+ </div>
97
+ `;
98
+ }).join('');
99
+
100
+ return `
101
+ <div>
102
+ <h2 style="color: white; margin: 30px 0 20px 0; font-size: 1.5em;">✅ Health Checks</h2>
103
+ ${checkItems}
104
+ </div>
105
+ `;
106
+ }
107
+
108
+ // Load health on page load
109
+ document.addEventListener('DOMContentLoaded', function() {
110
+ loadHealth();
111
+ // Auto-refresh every 10 seconds
112
+ setInterval(loadHealth, 10000);
113
+ });
114
+
@@ -0,0 +1,89 @@
1
+ // ===================================
2
+ // UTILITY FUNCTIONS
3
+ // ===================================
4
+
5
+ function updateTimestamp() {
6
+ const now = new Date().toLocaleString();
7
+ const timestampEl = document.getElementById('lastUpdate');
8
+ if (timestampEl) {
9
+ timestampEl.textContent = `Last updated: ${now}`;
10
+ }
11
+ }
12
+
13
+ function formatBytes(bytes) {
14
+ if (bytes === 0) return '0 B';
15
+ const k = 1024;
16
+ const sizes = ['B', 'KB', 'MB', 'GB'];
17
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
18
+ return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
19
+ }
20
+
21
+ function formatUptime(seconds) {
22
+ if (!seconds) return '0s';
23
+
24
+ const days = Math.floor(seconds / 86400);
25
+ const hours = Math.floor((seconds % 86400) / 3600);
26
+ const minutes = Math.floor((seconds % 3600) / 60);
27
+ const secs = Math.floor(seconds % 60);
28
+
29
+ const parts = [];
30
+ if (days > 0) parts.push(`${days}d`);
31
+ if (hours > 0) parts.push(`${hours}h`);
32
+ if (minutes > 0) parts.push(`${minutes}m`);
33
+ if (secs > 0 || parts.length === 0) parts.push(`${secs}s`);
34
+
35
+ return parts.join(' ');
36
+ }
37
+
38
+ function getCpuClass(percent) {
39
+ if (percent > 80) return 'danger';
40
+ if (percent > 60) return 'warning';
41
+ return 'good';
42
+ }
43
+
44
+ function getMemoryClass(percent) {
45
+ if (percent > 90) return 'danger';
46
+ if (percent > 75) return 'warning';
47
+ return 'good';
48
+ }
49
+
50
+ function formatCheckName(name) {
51
+ return name
52
+ .split('-')
53
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1))
54
+ .join(' ');
55
+ }
56
+
57
+ // ===================================
58
+ // ERROR HANDLING
59
+ // ===================================
60
+
61
+ function showError(containerId, message) {
62
+ const container = document.getElementById(containerId);
63
+ if (container) {
64
+ container.innerHTML = `
65
+ <div class="card" style="border-left: 5px solid #ef4444;">
66
+ <h3 style="color: #ef4444; display: flex; align-items: center; gap: 10px;">
67
+ <span>❌</span>
68
+ <span>Error Loading Data</span>
69
+ </h3>
70
+ <p style="color: #666; margin-top: 10px;">${escapeHtml(message)}</p>
71
+ <button class="refresh-btn" onclick="location.reload()" style="margin-top: 20px;">
72
+ 🔄 Reload Page
73
+ </button>
74
+ </div>
75
+ `;
76
+ }
77
+ }
78
+
79
+ function escapeHtml(text) {
80
+ const div = document.createElement('div');
81
+ div.textContent = text;
82
+ return div.innerHTML;
83
+ }
84
+
85
+ // Initialize timestamp on page load
86
+ document.addEventListener('DOMContentLoaded', function() {
87
+ updateTimestamp();
88
+ });
89
+
@@ -0,0 +1,225 @@
1
+ // ===================================
2
+ // METRICS FUNCTIONS
3
+ // ===================================
4
+
5
+ let cpuChart = null;
6
+ let memoryChart = null;
7
+
8
+ async function loadMetrics() {
9
+ try {
10
+ const response = await fetch('/monitor/metrics?format=json');
11
+ const data = await response.json();
12
+ renderMetrics(data);
13
+ updateTimestamp();
14
+ } catch (error) {
15
+ showError('statsContent', error.message);
16
+ }
17
+ }
18
+
19
+ function renderMetrics(data) {
20
+ renderStats(data);
21
+ renderCharts(data);
22
+ }
23
+
24
+ function renderStats(data) {
25
+ // Get the latest values from the history arrays
26
+ const cpuHistory = data.history?.cpu || [];
27
+ const memoryHistory = data.history?.memory || [];
28
+
29
+ const latestCpu = cpuHistory.length > 0 ? cpuHistory[cpuHistory.length - 1].value : { process: 0, system: 0 };
30
+ const latestMemory = memoryHistory.length > 0 ? memoryHistory[memoryHistory.length - 1].value : { system: {}, process: {} };
31
+
32
+ const html = `
33
+ <div class="stats-grid">
34
+ <div class="card" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white;">
35
+ <div style="text-align: center;">
36
+ <div style="font-size: 0.9em; opacity: 0.9;">CPU Usage (Process)</div>
37
+ <div style="font-size: 2.5em; font-weight: bold; margin: 10px 0;">${(latestCpu.process || 0).toFixed(1)}%</div>
38
+ <div style="font-size: 0.9em; opacity: 0.9;">Current load</div>
39
+ </div>
40
+ </div>
41
+ <div class="card" style="background: linear-gradient(135deg, #764ba2 0%, #667eea 100%); color: white;">
42
+ <div style="text-align: center;">
43
+ <div style="font-size: 0.9em; opacity: 0.9;">CPU Usage (System)</div>
44
+ <div style="font-size: 2.5em; font-weight: bold; margin: 10px 0;">${(latestCpu.system || 0).toFixed(1)}%</div>
45
+ <div style="font-size: 0.9em; opacity: 0.9;">Overall system</div>
46
+ </div>
47
+ </div>
48
+ <div class="card" style="background: linear-gradient(135deg, #10b981 0%, #059669 100%); color: white;">
49
+ <div style="text-align: center;">
50
+ <div style="font-size: 0.9em; opacity: 0.9;">Memory Usage (System)</div>
51
+ <div style="font-size: 2.5em; font-weight: bold; margin: 10px 0;">${(latestMemory.system?.usagePercent || 0).toFixed(1)}%</div>
52
+ <div style="font-size: 0.9em; opacity: 0.9;">${latestMemory.system?.used || 'N/A'} used</div>
53
+ </div>
54
+ </div>
55
+ <div class="card" style="background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); color: white;">
56
+ <div style="text-align: center;">
57
+ <div style="font-size: 0.9em; opacity: 0.9;">Data Points</div>
58
+ <div style="font-size: 2.5em; font-weight: bold; margin: 10px 0;">${cpuHistory.length || 0}</div>
59
+ <div style="font-size: 0.9em; opacity: 0.9;">Collected samples</div>
60
+ </div>
61
+ </div>
62
+ </div>
63
+ `;
64
+
65
+ document.getElementById('statsContent').innerHTML = html;
66
+ }
67
+
68
+ function renderCharts(data) {
69
+ const cpuHistory = data.history?.cpu || [];
70
+ const memoryHistory = data.history?.memory || [];
71
+
72
+ if (cpuHistory.length === 0 && memoryHistory.length === 0) {
73
+ document.getElementById('chartsContent').innerHTML = `
74
+ <div class="card">
75
+ <p style="text-align: center; color: #666;">No metrics history available yet. Data will appear as the system collects metrics.</p>
76
+ </div>
77
+ `;
78
+ return;
79
+ }
80
+
81
+ // Use CPU history for labels (both should have same length)
82
+ const labels = cpuHistory.map((_, index) => `${index + 1}`);
83
+ const cpuProcessData = cpuHistory.map(m => m.value?.process || 0);
84
+ const cpuSystemData = cpuHistory.map(m => m.value?.system || 0);
85
+ const memoryData = memoryHistory.map(m => m.value?.system?.usagePercent || 0);
86
+
87
+ document.getElementById('chartsContent').innerHTML = `
88
+ <div class="card">
89
+ <h2 style="display: flex; align-items: center; gap: 10px; margin-bottom: 20px;">
90
+ 💻 CPU Usage Over Time
91
+ </h2>
92
+ <div style="position: relative; height: 300px;">
93
+ <canvas id="cpuChart"></canvas>
94
+ </div>
95
+ </div>
96
+
97
+ <div class="card">
98
+ <h2 style="display: flex; align-items: center; gap: 10px; margin-bottom: 20px;">
99
+ 🧠 Memory Usage Over Time
100
+ </h2>
101
+ <div style="position: relative; height: 300px;">
102
+ <canvas id="memoryChart"></canvas>
103
+ </div>
104
+ </div>
105
+ `;
106
+
107
+ // Destroy existing charts
108
+ if (cpuChart) cpuChart.destroy();
109
+ if (memoryChart) memoryChart.destroy();
110
+
111
+ // Create CPU chart
112
+ const cpuCtx = document.getElementById('cpuChart').getContext('2d');
113
+ cpuChart = new Chart(cpuCtx, {
114
+ type: 'line',
115
+ data: {
116
+ labels: labels,
117
+ datasets: [
118
+ {
119
+ label: 'Process CPU %',
120
+ data: cpuProcessData,
121
+ borderColor: '#667eea',
122
+ backgroundColor: 'rgba(102, 126, 234, 0.1)',
123
+ tension: 0.4,
124
+ fill: true,
125
+ borderWidth: 2
126
+ },
127
+ {
128
+ label: 'System CPU %',
129
+ data: cpuSystemData,
130
+ borderColor: '#764ba2',
131
+ backgroundColor: 'rgba(118, 75, 162, 0.1)',
132
+ tension: 0.4,
133
+ fill: true,
134
+ borderWidth: 2
135
+ }
136
+ ]
137
+ },
138
+ options: {
139
+ responsive: true,
140
+ maintainAspectRatio: false,
141
+ plugins: {
142
+ legend: {
143
+ display: true,
144
+ position: 'top'
145
+ },
146
+ tooltip: {
147
+ mode: 'index',
148
+ intersect: false
149
+ }
150
+ },
151
+ scales: {
152
+ y: {
153
+ beginAtZero: true,
154
+ max: 100,
155
+ ticks: {
156
+ callback: function(value) {
157
+ return value + '%';
158
+ }
159
+ }
160
+ }
161
+ },
162
+ interaction: {
163
+ mode: 'nearest',
164
+ axis: 'x',
165
+ intersect: false
166
+ }
167
+ }
168
+ });
169
+
170
+ // Create Memory chart
171
+ const memoryCtx = document.getElementById('memoryChart').getContext('2d');
172
+ memoryChart = new Chart(memoryCtx, {
173
+ type: 'line',
174
+ data: {
175
+ labels: labels,
176
+ datasets: [{
177
+ label: 'Memory Usage %',
178
+ data: memoryData,
179
+ borderColor: '#10b981',
180
+ backgroundColor: 'rgba(16, 185, 129, 0.1)',
181
+ tension: 0.4,
182
+ fill: true,
183
+ borderWidth: 2
184
+ }]
185
+ },
186
+ options: {
187
+ responsive: true,
188
+ maintainAspectRatio: false,
189
+ plugins: {
190
+ legend: {
191
+ display: true,
192
+ position: 'top'
193
+ },
194
+ tooltip: {
195
+ mode: 'index',
196
+ intersect: false
197
+ }
198
+ },
199
+ scales: {
200
+ y: {
201
+ beginAtZero: true,
202
+ max: 100,
203
+ ticks: {
204
+ callback: function(value) {
205
+ return value + '%';
206
+ }
207
+ }
208
+ }
209
+ },
210
+ interaction: {
211
+ mode: 'nearest',
212
+ axis: 'x',
213
+ intersect: false
214
+ }
215
+ }
216
+ });
217
+ }
218
+
219
+ // Load metrics on page load
220
+ document.addEventListener('DOMContentLoaded', function() {
221
+ loadMetrics();
222
+ // Auto-refresh every 30 seconds
223
+ setInterval(loadMetrics, 30000);
224
+ });
225
+