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.
- package/README.md +93 -0
- package/node-monitor/ARCHITECTURE.md +341 -0
- package/node-monitor/CHANGELOG.md +105 -0
- package/node-monitor/CONTRIBUTING.md +96 -0
- package/node-monitor/DESIGN_IMPROVEMENTS.md +286 -0
- package/node-monitor/FILTER_BUTTONS_FIX.md +303 -0
- package/node-monitor/GETTING_STARTED.md +416 -0
- package/node-monitor/INSTALLATION.md +470 -0
- package/node-monitor/LICENSE +22 -0
- package/node-monitor/PUBLISHING_GUIDE.md +331 -0
- package/node-monitor/QUICK_REFERENCE.md +252 -0
- package/node-monitor/README.md +458 -0
- package/node-monitor/READY_TO_PUBLISH.md +272 -0
- package/node-monitor/SETUP_GUIDE.md +479 -0
- package/node-monitor/examples/EMAIL_SETUP_GUIDE.md +282 -0
- package/node-monitor/examples/ERROR_LOGGING_GUIDE.md +405 -0
- package/node-monitor/examples/GET_APP_PASSWORD.md +145 -0
- package/node-monitor/examples/LOG_FILES_REFERENCE.md +336 -0
- package/node-monitor/examples/QUICK_START_EMAIL.md +126 -0
- package/node-monitor/examples/express-app.js +499 -0
- package/node-monitor/examples/package-lock.json +1295 -0
- package/node-monitor/examples/package.json +18 -0
- package/node-monitor/examples/public/css/style.css +718 -0
- package/node-monitor/examples/public/js/dashboard.js +207 -0
- package/node-monitor/examples/public/js/health.js +114 -0
- package/node-monitor/examples/public/js/main.js +89 -0
- package/node-monitor/examples/public/js/metrics.js +225 -0
- package/node-monitor/examples/public/js/theme.js +138 -0
- package/node-monitor/examples/views/dashboard.ejs +20 -0
- package/node-monitor/examples/views/error-logs.ejs +1129 -0
- package/node-monitor/examples/views/health.ejs +21 -0
- package/node-monitor/examples/views/home.ejs +341 -0
- package/node-monitor/examples/views/layout.ejs +50 -0
- package/node-monitor/examples/views/metrics.ejs +16 -0
- package/node-monitor/examples/views/partials/footer.ejs +16 -0
- package/node-monitor/examples/views/partials/header.ejs +35 -0
- package/node-monitor/examples/views/partials/nav.ejs +23 -0
- package/node-monitor/examples/views/status.ejs +390 -0
- package/node-monitor/package-lock.json +4300 -0
- package/node-monitor/package.json +76 -0
- package/node-monitor/pre-publish-check.js +200 -0
- package/node-monitor/src/config/monitoringConfig.js +255 -0
- package/node-monitor/src/index.js +300 -0
- package/node-monitor/src/logger/errorLogger.js +297 -0
- package/node-monitor/src/monitors/apiErrorMonitor.js +156 -0
- package/node-monitor/src/monitors/dbConnectionMonitor.js +389 -0
- package/node-monitor/src/monitors/serverHealthMonitor.js +320 -0
- package/node-monitor/src/monitors/systemResourceMonitor.js +357 -0
- package/node-monitor/src/notifiers/emailNotifier.js +248 -0
- package/node-monitor/src/notifiers/notificationManager.js +96 -0
- package/node-monitor/src/notifiers/slackNotifier.js +209 -0
- package/node-monitor/src/views/dashboard.html +530 -0
- package/node-monitor/src/views/health.html +399 -0
- package/node-monitor/src/views/metrics.html +406 -0
- package/package.json +22 -0
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Node Monitor - Dashboard</title>
|
|
7
|
+
<style>
|
|
8
|
+
* {
|
|
9
|
+
margin: 0;
|
|
10
|
+
padding: 0;
|
|
11
|
+
box-sizing: border-box;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
body {
|
|
15
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
|
16
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
17
|
+
min-height: 100vh;
|
|
18
|
+
padding: 20px;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.container {
|
|
22
|
+
max-width: 1400px;
|
|
23
|
+
margin: 0 auto;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.header {
|
|
27
|
+
background: white;
|
|
28
|
+
border-radius: 15px;
|
|
29
|
+
padding: 30px;
|
|
30
|
+
margin-bottom: 30px;
|
|
31
|
+
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.header h1 {
|
|
35
|
+
color: #333;
|
|
36
|
+
font-size: 2.5em;
|
|
37
|
+
margin-bottom: 10px;
|
|
38
|
+
display: flex;
|
|
39
|
+
align-items: center;
|
|
40
|
+
gap: 15px;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.header .subtitle {
|
|
44
|
+
color: #666;
|
|
45
|
+
font-size: 1.1em;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.status-badge {
|
|
49
|
+
display: inline-block;
|
|
50
|
+
padding: 8px 20px;
|
|
51
|
+
border-radius: 25px;
|
|
52
|
+
font-weight: bold;
|
|
53
|
+
font-size: 0.9em;
|
|
54
|
+
margin-left: auto;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.status-healthy {
|
|
58
|
+
background: #10b981;
|
|
59
|
+
color: white;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.status-warning {
|
|
63
|
+
background: #f59e0b;
|
|
64
|
+
color: white;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.status-error {
|
|
68
|
+
background: #ef4444;
|
|
69
|
+
color: white;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.grid {
|
|
73
|
+
display: grid;
|
|
74
|
+
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
75
|
+
gap: 20px;
|
|
76
|
+
margin-bottom: 30px;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.card {
|
|
80
|
+
background: white;
|
|
81
|
+
border-radius: 15px;
|
|
82
|
+
padding: 25px;
|
|
83
|
+
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
|
|
84
|
+
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.card:hover {
|
|
88
|
+
transform: translateY(-5px);
|
|
89
|
+
box-shadow: 0 15px 40px rgba(0,0,0,0.3);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.card-header {
|
|
93
|
+
display: flex;
|
|
94
|
+
align-items: center;
|
|
95
|
+
gap: 15px;
|
|
96
|
+
margin-bottom: 20px;
|
|
97
|
+
padding-bottom: 15px;
|
|
98
|
+
border-bottom: 2px solid #f0f0f0;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.card-icon {
|
|
102
|
+
font-size: 2.5em;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.card-title {
|
|
106
|
+
font-size: 1.3em;
|
|
107
|
+
color: #333;
|
|
108
|
+
font-weight: 600;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.metric {
|
|
112
|
+
display: flex;
|
|
113
|
+
justify-content: space-between;
|
|
114
|
+
align-items: center;
|
|
115
|
+
padding: 12px 0;
|
|
116
|
+
border-bottom: 1px solid #f0f0f0;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.metric:last-child {
|
|
120
|
+
border-bottom: none;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.metric-label {
|
|
124
|
+
color: #666;
|
|
125
|
+
font-size: 0.95em;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.metric-value {
|
|
129
|
+
font-weight: bold;
|
|
130
|
+
font-size: 1.1em;
|
|
131
|
+
color: #333;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.metric-value.good {
|
|
135
|
+
color: #10b981;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.metric-value.warning {
|
|
139
|
+
color: #f59e0b;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.metric-value.danger {
|
|
143
|
+
color: #ef4444;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.progress-bar {
|
|
147
|
+
width: 100%;
|
|
148
|
+
height: 10px;
|
|
149
|
+
background: #e5e7eb;
|
|
150
|
+
border-radius: 10px;
|
|
151
|
+
overflow: hidden;
|
|
152
|
+
margin-top: 8px;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.progress-fill {
|
|
156
|
+
height: 100%;
|
|
157
|
+
background: linear-gradient(90deg, #10b981, #059669);
|
|
158
|
+
border-radius: 10px;
|
|
159
|
+
transition: width 0.3s ease;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.progress-fill.warning {
|
|
163
|
+
background: linear-gradient(90deg, #f59e0b, #d97706);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.progress-fill.danger {
|
|
167
|
+
background: linear-gradient(90deg, #ef4444, #dc2626);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.chart-container {
|
|
171
|
+
margin-top: 20px;
|
|
172
|
+
height: 200px;
|
|
173
|
+
position: relative;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.refresh-btn {
|
|
177
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
178
|
+
color: white;
|
|
179
|
+
border: none;
|
|
180
|
+
padding: 12px 30px;
|
|
181
|
+
border-radius: 25px;
|
|
182
|
+
font-size: 1em;
|
|
183
|
+
font-weight: bold;
|
|
184
|
+
cursor: pointer;
|
|
185
|
+
transition: transform 0.2s ease;
|
|
186
|
+
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.refresh-btn:hover {
|
|
190
|
+
transform: scale(1.05);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.refresh-btn:active {
|
|
194
|
+
transform: scale(0.95);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.timestamp {
|
|
198
|
+
color: #999;
|
|
199
|
+
font-size: 0.9em;
|
|
200
|
+
text-align: center;
|
|
201
|
+
margin-top: 20px;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.health-checks {
|
|
205
|
+
list-style: none;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.health-check-item {
|
|
209
|
+
display: flex;
|
|
210
|
+
justify-content: space-between;
|
|
211
|
+
align-items: center;
|
|
212
|
+
padding: 12px;
|
|
213
|
+
margin-bottom: 10px;
|
|
214
|
+
background: #f9fafb;
|
|
215
|
+
border-radius: 8px;
|
|
216
|
+
border-left: 4px solid #10b981;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.health-check-item.fail {
|
|
220
|
+
border-left-color: #ef4444;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.check-status {
|
|
224
|
+
padding: 4px 12px;
|
|
225
|
+
border-radius: 12px;
|
|
226
|
+
font-size: 0.85em;
|
|
227
|
+
font-weight: bold;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.check-status.pass {
|
|
231
|
+
background: #d1fae5;
|
|
232
|
+
color: #065f46;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.check-status.fail {
|
|
236
|
+
background: #fee2e2;
|
|
237
|
+
color: #991b1b;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.error-list {
|
|
241
|
+
max-height: 300px;
|
|
242
|
+
overflow-y: auto;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.error-item {
|
|
246
|
+
background: #fef2f2;
|
|
247
|
+
border-left: 4px solid #ef4444;
|
|
248
|
+
padding: 15px;
|
|
249
|
+
margin-bottom: 10px;
|
|
250
|
+
border-radius: 8px;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.error-time {
|
|
254
|
+
color: #991b1b;
|
|
255
|
+
font-size: 0.85em;
|
|
256
|
+
font-weight: bold;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
.error-message {
|
|
260
|
+
color: #333;
|
|
261
|
+
margin-top: 5px;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
.loading {
|
|
265
|
+
text-align: center;
|
|
266
|
+
padding: 40px;
|
|
267
|
+
color: #666;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.spinner {
|
|
271
|
+
border: 4px solid #f3f4f6;
|
|
272
|
+
border-top: 4px solid #667eea;
|
|
273
|
+
border-radius: 50%;
|
|
274
|
+
width: 40px;
|
|
275
|
+
height: 40px;
|
|
276
|
+
animation: spin 1s linear infinite;
|
|
277
|
+
margin: 0 auto 20px;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
@keyframes spin {
|
|
281
|
+
0% { transform: rotate(0deg); }
|
|
282
|
+
100% { transform: rotate(360deg); }
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.footer {
|
|
286
|
+
text-align: center;
|
|
287
|
+
color: white;
|
|
288
|
+
margin-top: 30px;
|
|
289
|
+
padding: 20px;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.nav-links {
|
|
293
|
+
display: flex;
|
|
294
|
+
gap: 15px;
|
|
295
|
+
margin-top: 15px;
|
|
296
|
+
flex-wrap: wrap;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
.nav-link {
|
|
300
|
+
color: #667eea;
|
|
301
|
+
text-decoration: none;
|
|
302
|
+
padding: 8px 16px;
|
|
303
|
+
background: #f0f0f0;
|
|
304
|
+
border-radius: 8px;
|
|
305
|
+
transition: all 0.3s ease;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.nav-link:hover {
|
|
309
|
+
background: #667eea;
|
|
310
|
+
color: white;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
@media (max-width: 768px) {
|
|
314
|
+
.grid {
|
|
315
|
+
grid-template-columns: 1fr;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.header h1 {
|
|
319
|
+
font-size: 1.8em;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
</style>
|
|
323
|
+
</head>
|
|
324
|
+
<body>
|
|
325
|
+
<div class="container">
|
|
326
|
+
<div class="header">
|
|
327
|
+
<h1>
|
|
328
|
+
📊 Node Monitor Dashboard
|
|
329
|
+
<span class="status-badge status-healthy" id="overallStatus">● Healthy</span>
|
|
330
|
+
</h1>
|
|
331
|
+
<p class="subtitle">Real-time application monitoring and health checks</p>
|
|
332
|
+
<div class="nav-links">
|
|
333
|
+
<a href="/monitor/dashboard" class="nav-link">Dashboard</a>
|
|
334
|
+
<a href="/health" class="nav-link">Health Check</a>
|
|
335
|
+
<a href="/monitor/status" class="nav-link">Status API</a>
|
|
336
|
+
<a href="/monitor/metrics" class="nav-link">Metrics</a>
|
|
337
|
+
</div>
|
|
338
|
+
</div>
|
|
339
|
+
|
|
340
|
+
<div id="dashboardContent">
|
|
341
|
+
<div class="loading">
|
|
342
|
+
<div class="spinner"></div>
|
|
343
|
+
<p>Loading monitoring data...</p>
|
|
344
|
+
</div>
|
|
345
|
+
</div>
|
|
346
|
+
|
|
347
|
+
<div style="text-align: center; margin-top: 20px;">
|
|
348
|
+
<button class="refresh-btn" onclick="loadDashboard()">🔄 Refresh Data</button>
|
|
349
|
+
</div>
|
|
350
|
+
|
|
351
|
+
<div class="timestamp" id="lastUpdate"></div>
|
|
352
|
+
|
|
353
|
+
<div class="footer">
|
|
354
|
+
<p>Node Monitor v1.0.0 | Built with ❤️ By Manish Desai</p>
|
|
355
|
+
</div>
|
|
356
|
+
</div>
|
|
357
|
+
|
|
358
|
+
<script>
|
|
359
|
+
async function loadDashboard() {
|
|
360
|
+
try {
|
|
361
|
+
const response = await fetch('/monitor/status');
|
|
362
|
+
const data = await response.json();
|
|
363
|
+
renderDashboard(data);
|
|
364
|
+
updateTimestamp();
|
|
365
|
+
} catch (error) {
|
|
366
|
+
document.getElementById('dashboardContent').innerHTML = `
|
|
367
|
+
<div class="card">
|
|
368
|
+
<h3 style="color: #ef4444;">❌ Error Loading Dashboard</h3>
|
|
369
|
+
<p>${error.message}</p>
|
|
370
|
+
</div>
|
|
371
|
+
`;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function renderDashboard(data) {
|
|
376
|
+
const isHealthy = data.health?.isHealthy ?? true;
|
|
377
|
+
document.getElementById('overallStatus').className = `status-badge ${isHealthy ? 'status-healthy' : 'status-error'}`;
|
|
378
|
+
document.getElementById('overallStatus').textContent = isHealthy ? '● Healthy' : '● Unhealthy';
|
|
379
|
+
|
|
380
|
+
const content = `
|
|
381
|
+
<div class="grid">
|
|
382
|
+
${renderApplicationCard(data)}
|
|
383
|
+
${renderHealthCard(data.health)}
|
|
384
|
+
${renderSystemCard(data.system)}
|
|
385
|
+
${renderErrorCard(data.errors)}
|
|
386
|
+
</div>
|
|
387
|
+
`;
|
|
388
|
+
|
|
389
|
+
document.getElementById('dashboardContent').innerHTML = content;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function renderApplicationCard(data) {
|
|
393
|
+
return `
|
|
394
|
+
<div class="card">
|
|
395
|
+
<div class="card-header">
|
|
396
|
+
<span class="card-icon">🚀</span>
|
|
397
|
+
<h2 class="card-title">Application</h2>
|
|
398
|
+
</div>
|
|
399
|
+
<div class="metric">
|
|
400
|
+
<span class="metric-label">Status</span>
|
|
401
|
+
<span class="metric-value good">${data.isRunning ? 'Running' : 'Stopped'}</span>
|
|
402
|
+
</div>
|
|
403
|
+
<div class="metric">
|
|
404
|
+
<span class="metric-label">Uptime</span>
|
|
405
|
+
<span class="metric-value">${data.health?.uptime || 'N/A'}</span>
|
|
406
|
+
</div>
|
|
407
|
+
<div class="metric">
|
|
408
|
+
<span class="metric-label">Environment</span>
|
|
409
|
+
<span class="metric-value">${process.env.NODE_ENV || 'development'}</span>
|
|
410
|
+
</div>
|
|
411
|
+
<div class="metric">
|
|
412
|
+
<span class="metric-label">Monitoring</span>
|
|
413
|
+
<span class="metric-value good">Active</span>
|
|
414
|
+
</div>
|
|
415
|
+
</div>
|
|
416
|
+
`;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function renderHealthCard(health) {
|
|
420
|
+
if (!health) return '';
|
|
421
|
+
|
|
422
|
+
return `
|
|
423
|
+
<div class="card">
|
|
424
|
+
<div class="card-header">
|
|
425
|
+
<span class="card-icon">💚</span>
|
|
426
|
+
<h2 class="card-title">Health Checks</h2>
|
|
427
|
+
</div>
|
|
428
|
+
<div class="metric">
|
|
429
|
+
<span class="metric-label">Overall Status</span>
|
|
430
|
+
<span class="metric-value ${health.isHealthy ? 'good' : 'danger'}">
|
|
431
|
+
${health.isHealthy ? '✓ Healthy' : '✗ Unhealthy'}
|
|
432
|
+
</span>
|
|
433
|
+
</div>
|
|
434
|
+
<div class="metric">
|
|
435
|
+
<span class="metric-label">Registered Checks</span>
|
|
436
|
+
<span class="metric-value">${health.registeredChecks || 0}</span>
|
|
437
|
+
</div>
|
|
438
|
+
<div class="metric">
|
|
439
|
+
<span class="metric-label">Consecutive Failures</span>
|
|
440
|
+
<span class="metric-value ${health.consecutiveFailures > 0 ? 'warning' : 'good'}">
|
|
441
|
+
${health.consecutiveFailures || 0}
|
|
442
|
+
</span>
|
|
443
|
+
</div>
|
|
444
|
+
</div>
|
|
445
|
+
`;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
function renderSystemCard(system) {
|
|
449
|
+
if (!system) return '';
|
|
450
|
+
|
|
451
|
+
const cpuPercent = system.cpu?.process || 0;
|
|
452
|
+
const memoryPercent = system.memory?.percentUsed || 0;
|
|
453
|
+
|
|
454
|
+
return `
|
|
455
|
+
<div class="card">
|
|
456
|
+
<div class="card-header">
|
|
457
|
+
<span class="card-icon">💻</span>
|
|
458
|
+
<h2 class="card-title">System Resources</h2>
|
|
459
|
+
</div>
|
|
460
|
+
<div class="metric">
|
|
461
|
+
<span class="metric-label">CPU Usage</span>
|
|
462
|
+
<span class="metric-value ${getCpuClass(cpuPercent)}">${cpuPercent.toFixed(1)}%</span>
|
|
463
|
+
</div>
|
|
464
|
+
<div class="progress-bar">
|
|
465
|
+
<div class="progress-fill ${getCpuClass(cpuPercent)}" style="width: ${cpuPercent}%"></div>
|
|
466
|
+
</div>
|
|
467
|
+
<div class="metric" style="margin-top: 15px;">
|
|
468
|
+
<span class="metric-label">Memory Usage</span>
|
|
469
|
+
<span class="metric-value ${getMemoryClass(memoryPercent)}">${memoryPercent.toFixed(1)}%</span>
|
|
470
|
+
</div>
|
|
471
|
+
<div class="progress-bar">
|
|
472
|
+
<div class="progress-fill ${getMemoryClass(memoryPercent)}" style="width: ${memoryPercent}%"></div>
|
|
473
|
+
</div>
|
|
474
|
+
</div>
|
|
475
|
+
`;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function renderErrorCard(errors) {
|
|
479
|
+
if (!errors) return '';
|
|
480
|
+
|
|
481
|
+
return `
|
|
482
|
+
<div class="card">
|
|
483
|
+
<div class="card-header">
|
|
484
|
+
<span class="card-icon">⚠️</span>
|
|
485
|
+
<h2 class="card-title">Error Tracking</h2>
|
|
486
|
+
</div>
|
|
487
|
+
<div class="metric">
|
|
488
|
+
<span class="metric-label">Current Error Rate</span>
|
|
489
|
+
<span class="metric-value ${errors.currentErrorRate > 10 ? 'danger' : 'good'}">
|
|
490
|
+
${errors.currentErrorRate || 0} /min
|
|
491
|
+
</span>
|
|
492
|
+
</div>
|
|
493
|
+
<div class="metric">
|
|
494
|
+
<span class="metric-label">Threshold</span>
|
|
495
|
+
<span class="metric-value">${errors.threshold || 0} /min</span>
|
|
496
|
+
</div>
|
|
497
|
+
<div class="metric">
|
|
498
|
+
<span class="metric-label">API Error Rate</span>
|
|
499
|
+
<span class="metric-value">${errors.apiErrorRate || 0} /min</span>
|
|
500
|
+
</div>
|
|
501
|
+
</div>
|
|
502
|
+
`;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
function getCpuClass(percent) {
|
|
506
|
+
if (percent > 80) return 'danger';
|
|
507
|
+
if (percent > 60) return 'warning';
|
|
508
|
+
return '';
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
function getMemoryClass(percent) {
|
|
512
|
+
if (percent > 90) return 'danger';
|
|
513
|
+
if (percent > 75) return 'warning';
|
|
514
|
+
return '';
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
function updateTimestamp() {
|
|
518
|
+
const now = new Date().toLocaleString();
|
|
519
|
+
document.getElementById('lastUpdate').textContent = `Last updated: ${now}`;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// Load dashboard on page load
|
|
523
|
+
loadDashboard();
|
|
524
|
+
|
|
525
|
+
// Auto-refresh every 30 seconds
|
|
526
|
+
setInterval(loadDashboard, 30000);
|
|
527
|
+
</script>
|
|
528
|
+
</body>
|
|
529
|
+
</html>
|
|
530
|
+
|