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,399 @@
|
|
|
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>Health Check - Node Monitor</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: 900px;
|
|
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
|
+
text-align: center;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.header h1 {
|
|
36
|
+
color: #333;
|
|
37
|
+
font-size: 2.5em;
|
|
38
|
+
margin-bottom: 10px;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.status-indicator {
|
|
42
|
+
display: inline-flex;
|
|
43
|
+
align-items: center;
|
|
44
|
+
gap: 10px;
|
|
45
|
+
padding: 15px 30px;
|
|
46
|
+
border-radius: 50px;
|
|
47
|
+
font-size: 1.3em;
|
|
48
|
+
font-weight: bold;
|
|
49
|
+
margin: 20px 0;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.status-healthy {
|
|
53
|
+
background: linear-gradient(135deg, #10b981, #059669);
|
|
54
|
+
color: white;
|
|
55
|
+
box-shadow: 0 5px 20px rgba(16, 185, 129, 0.4);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.status-unhealthy {
|
|
59
|
+
background: linear-gradient(135deg, #ef4444, #dc2626);
|
|
60
|
+
color: white;
|
|
61
|
+
box-shadow: 0 5px 20px rgba(239, 68, 68, 0.4);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.pulse {
|
|
65
|
+
width: 20px;
|
|
66
|
+
height: 20px;
|
|
67
|
+
border-radius: 50%;
|
|
68
|
+
background: white;
|
|
69
|
+
animation: pulse 2s infinite;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
@keyframes pulse {
|
|
73
|
+
0%, 100% { opacity: 1; transform: scale(1); }
|
|
74
|
+
50% { opacity: 0.5; transform: scale(1.1); }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.card {
|
|
78
|
+
background: white;
|
|
79
|
+
border-radius: 15px;
|
|
80
|
+
padding: 30px;
|
|
81
|
+
margin-bottom: 20px;
|
|
82
|
+
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.card h2 {
|
|
86
|
+
color: #333;
|
|
87
|
+
margin-bottom: 20px;
|
|
88
|
+
padding-bottom: 15px;
|
|
89
|
+
border-bottom: 2px solid #f0f0f0;
|
|
90
|
+
display: flex;
|
|
91
|
+
align-items: center;
|
|
92
|
+
gap: 10px;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.checks-grid {
|
|
96
|
+
display: grid;
|
|
97
|
+
gap: 15px;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.check-item {
|
|
101
|
+
display: flex;
|
|
102
|
+
justify-content: space-between;
|
|
103
|
+
align-items: center;
|
|
104
|
+
padding: 20px;
|
|
105
|
+
background: #f9fafb;
|
|
106
|
+
border-radius: 12px;
|
|
107
|
+
border-left: 5px solid #10b981;
|
|
108
|
+
transition: all 0.3s ease;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.check-item:hover {
|
|
112
|
+
transform: translateX(5px);
|
|
113
|
+
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.check-item.fail {
|
|
117
|
+
border-left-color: #ef4444;
|
|
118
|
+
background: #fef2f2;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.check-name {
|
|
122
|
+
font-weight: 600;
|
|
123
|
+
color: #333;
|
|
124
|
+
font-size: 1.1em;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.check-status {
|
|
128
|
+
display: flex;
|
|
129
|
+
align-items: center;
|
|
130
|
+
gap: 10px;
|
|
131
|
+
padding: 8px 20px;
|
|
132
|
+
border-radius: 25px;
|
|
133
|
+
font-weight: bold;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.check-status.pass {
|
|
137
|
+
background: #d1fae5;
|
|
138
|
+
color: #065f46;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.check-status.fail {
|
|
142
|
+
background: #fee2e2;
|
|
143
|
+
color: #991b1b;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.metrics-grid {
|
|
147
|
+
display: grid;
|
|
148
|
+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
149
|
+
gap: 20px;
|
|
150
|
+
margin-top: 20px;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.metric-box {
|
|
154
|
+
text-align: center;
|
|
155
|
+
padding: 20px;
|
|
156
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
157
|
+
border-radius: 12px;
|
|
158
|
+
color: white;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.metric-value {
|
|
162
|
+
font-size: 2.5em;
|
|
163
|
+
font-weight: bold;
|
|
164
|
+
margin: 10px 0;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.metric-label {
|
|
168
|
+
font-size: 0.9em;
|
|
169
|
+
opacity: 0.9;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.refresh-btn {
|
|
173
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
174
|
+
color: white;
|
|
175
|
+
border: none;
|
|
176
|
+
padding: 15px 40px;
|
|
177
|
+
border-radius: 30px;
|
|
178
|
+
font-size: 1.1em;
|
|
179
|
+
font-weight: bold;
|
|
180
|
+
cursor: pointer;
|
|
181
|
+
transition: transform 0.2s ease;
|
|
182
|
+
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
|
|
183
|
+
margin: 20px auto;
|
|
184
|
+
display: block;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.refresh-btn:hover {
|
|
188
|
+
transform: scale(1.05);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.timestamp {
|
|
192
|
+
text-align: center;
|
|
193
|
+
color: white;
|
|
194
|
+
margin-top: 20px;
|
|
195
|
+
font-size: 0.95em;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.loading {
|
|
199
|
+
text-align: center;
|
|
200
|
+
padding: 40px;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.spinner {
|
|
204
|
+
border: 4px solid #f3f4f6;
|
|
205
|
+
border-top: 4px solid #667eea;
|
|
206
|
+
border-radius: 50%;
|
|
207
|
+
width: 50px;
|
|
208
|
+
height: 50px;
|
|
209
|
+
animation: spin 1s linear infinite;
|
|
210
|
+
margin: 0 auto 20px;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
@keyframes spin {
|
|
214
|
+
0% { transform: rotate(0deg); }
|
|
215
|
+
100% { transform: rotate(360deg); }
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.nav-links {
|
|
219
|
+
display: flex;
|
|
220
|
+
gap: 15px;
|
|
221
|
+
justify-content: center;
|
|
222
|
+
margin-top: 20px;
|
|
223
|
+
flex-wrap: wrap;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.nav-link {
|
|
227
|
+
color: #667eea;
|
|
228
|
+
text-decoration: none;
|
|
229
|
+
padding: 10px 20px;
|
|
230
|
+
background: #f0f0f0;
|
|
231
|
+
border-radius: 8px;
|
|
232
|
+
transition: all 0.3s ease;
|
|
233
|
+
font-weight: 500;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.nav-link:hover {
|
|
237
|
+
background: #667eea;
|
|
238
|
+
color: white;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
@media (max-width: 768px) {
|
|
242
|
+
.header h1 {
|
|
243
|
+
font-size: 1.8em;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.metrics-grid {
|
|
247
|
+
grid-template-columns: 1fr;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
</style>
|
|
251
|
+
</head>
|
|
252
|
+
<body>
|
|
253
|
+
<div class="container">
|
|
254
|
+
<div class="header">
|
|
255
|
+
<h1>💚 Health Check</h1>
|
|
256
|
+
<div class="status-indicator status-healthy" id="statusIndicator">
|
|
257
|
+
<div class="pulse"></div>
|
|
258
|
+
<span id="statusText">All Systems Operational</span>
|
|
259
|
+
</div>
|
|
260
|
+
<div class="nav-links">
|
|
261
|
+
<a href="/monitor/dashboard" class="nav-link">📊 Dashboard</a>
|
|
262
|
+
<a href="/health" class="nav-link">💚 Health</a>
|
|
263
|
+
<a href="/monitor/status" class="nav-link">📈 Status</a>
|
|
264
|
+
</div>
|
|
265
|
+
</div>
|
|
266
|
+
|
|
267
|
+
<div id="healthContent">
|
|
268
|
+
<div class="loading">
|
|
269
|
+
<div class="spinner"></div>
|
|
270
|
+
<p>Loading health data...</p>
|
|
271
|
+
</div>
|
|
272
|
+
</div>
|
|
273
|
+
|
|
274
|
+
<button class="refresh-btn" onclick="loadHealth()">🔄 Refresh Health Check</button>
|
|
275
|
+
|
|
276
|
+
<div class="timestamp" id="lastUpdate"></div>
|
|
277
|
+
</div>
|
|
278
|
+
|
|
279
|
+
<script>
|
|
280
|
+
async function loadHealth() {
|
|
281
|
+
try {
|
|
282
|
+
const response = await fetch('/health');
|
|
283
|
+
const data = await response.json();
|
|
284
|
+
renderHealth(data);
|
|
285
|
+
updateTimestamp();
|
|
286
|
+
} catch (error) {
|
|
287
|
+
document.getElementById('healthContent').innerHTML = `
|
|
288
|
+
<div class="card">
|
|
289
|
+
<h2 style="color: #ef4444;">❌ Error Loading Health Data</h2>
|
|
290
|
+
<p>${error.message}</p>
|
|
291
|
+
</div>
|
|
292
|
+
`;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function renderHealth(data) {
|
|
297
|
+
const isHealthy = data.status === 'healthy';
|
|
298
|
+
|
|
299
|
+
// Update status indicator
|
|
300
|
+
const indicator = document.getElementById('statusIndicator');
|
|
301
|
+
const statusText = document.getElementById('statusText');
|
|
302
|
+
|
|
303
|
+
if (isHealthy) {
|
|
304
|
+
indicator.className = 'status-indicator status-healthy';
|
|
305
|
+
statusText.textContent = 'All Systems Operational';
|
|
306
|
+
} else {
|
|
307
|
+
indicator.className = 'status-indicator status-unhealthy';
|
|
308
|
+
statusText.textContent = 'System Issues Detected';
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Render content
|
|
312
|
+
const content = `
|
|
313
|
+
<div class="card">
|
|
314
|
+
<h2>📊 Overview</h2>
|
|
315
|
+
<div class="metrics-grid">
|
|
316
|
+
<div class="metric-box">
|
|
317
|
+
<div class="metric-label">Status</div>
|
|
318
|
+
<div class="metric-value">${isHealthy ? '✓' : '✗'}</div>
|
|
319
|
+
<div class="metric-label">${data.status}</div>
|
|
320
|
+
</div>
|
|
321
|
+
<div class="metric-box">
|
|
322
|
+
<div class="metric-label">Uptime</div>
|
|
323
|
+
<div class="metric-value">${formatUptime(data.uptime)}</div>
|
|
324
|
+
<div class="metric-label">seconds</div>
|
|
325
|
+
</div>
|
|
326
|
+
<div class="metric-box">
|
|
327
|
+
<div class="metric-label">Checks</div>
|
|
328
|
+
<div class="metric-value">${Object.keys(data.checks || {}).length}</div>
|
|
329
|
+
<div class="metric-label">registered</div>
|
|
330
|
+
</div>
|
|
331
|
+
</div>
|
|
332
|
+
</div>
|
|
333
|
+
|
|
334
|
+
${renderChecks(data.checks)}
|
|
335
|
+
`;
|
|
336
|
+
|
|
337
|
+
document.getElementById('healthContent').innerHTML = content;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function renderChecks(checks) {
|
|
341
|
+
if (!checks || Object.keys(checks).length === 0) {
|
|
342
|
+
return `
|
|
343
|
+
<div class="card">
|
|
344
|
+
<h2>✅ Health Checks</h2>
|
|
345
|
+
<p style="text-align: center; color: #666; padding: 20px;">
|
|
346
|
+
No custom health checks registered
|
|
347
|
+
</p>
|
|
348
|
+
</div>
|
|
349
|
+
`;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const checkItems = Object.entries(checks).map(([name, check]) => {
|
|
353
|
+
const isPassing = check.status === 'pass';
|
|
354
|
+
return `
|
|
355
|
+
<div class="check-item ${isPassing ? '' : 'fail'}">
|
|
356
|
+
<div class="check-name">${formatCheckName(name)}</div>
|
|
357
|
+
<div class="check-status ${isPassing ? 'pass' : 'fail'}">
|
|
358
|
+
${isPassing ? '✓ Pass' : '✗ Fail'}
|
|
359
|
+
</div>
|
|
360
|
+
</div>
|
|
361
|
+
`;
|
|
362
|
+
}).join('');
|
|
363
|
+
|
|
364
|
+
return `
|
|
365
|
+
<div class="card">
|
|
366
|
+
<h2>✅ Health Checks</h2>
|
|
367
|
+
<div class="checks-grid">
|
|
368
|
+
${checkItems}
|
|
369
|
+
</div>
|
|
370
|
+
</div>
|
|
371
|
+
`;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function formatCheckName(name) {
|
|
375
|
+
return name
|
|
376
|
+
.split('-')
|
|
377
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
378
|
+
.join(' ');
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function formatUptime(seconds) {
|
|
382
|
+
if (!seconds) return '0';
|
|
383
|
+
return Math.floor(seconds);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function updateTimestamp() {
|
|
387
|
+
const now = new Date().toLocaleString();
|
|
388
|
+
document.getElementById('lastUpdate').textContent = `Last checked: ${now}`;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Load health on page load
|
|
392
|
+
loadHealth();
|
|
393
|
+
|
|
394
|
+
// Auto-refresh every 10 seconds
|
|
395
|
+
setInterval(loadHealth, 10000);
|
|
396
|
+
</script>
|
|
397
|
+
</body>
|
|
398
|
+
</html>
|
|
399
|
+
|