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,248 @@
1
+ /**
2
+ * Email Notifier
3
+ * Sends email notifications using Nodemailer
4
+ */
5
+
6
+ const nodemailer = require('nodemailer');
7
+
8
+ class EmailNotifier {
9
+ constructor(config, logger) {
10
+ this.config = config.notifications.email;
11
+ this.appConfig = config.app;
12
+ this.logger = logger;
13
+ this.transporter = null;
14
+ this.lastSent = new Map(); // Track last sent time per alert type
15
+ this.cooldown = config.notifications.cooldown;
16
+
17
+ if (this.config.enabled) {
18
+ this.initialize();
19
+ }
20
+ }
21
+
22
+ /**
23
+ * Initialize email transporter
24
+ */
25
+ initialize() {
26
+ try {
27
+ this.transporter = nodemailer.createTransport({
28
+ host: this.config.host,
29
+ port: this.config.port,
30
+ secure: this.config.secure,
31
+ auth: {
32
+ user: this.config.auth.user,
33
+ pass: this.config.auth.pass
34
+ }
35
+ });
36
+
37
+ // Verify connection
38
+ this.transporter.verify((error) => {
39
+ if (error) {
40
+ this.logger.logWarning('email_config', 'Email transporter verification failed', { error: error.message });
41
+ } else {
42
+ this.logger.logInfo('Email notifier initialized successfully');
43
+ }
44
+ });
45
+ } catch (error) {
46
+ this.logger.logWarning('email_config', 'Failed to initialize email notifier', { error: error.message });
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Check if cooldown period has passed
52
+ */
53
+ canSend(alertType) {
54
+ const lastSentTime = this.lastSent.get(alertType);
55
+ if (!lastSentTime) return true;
56
+
57
+ return Date.now() - lastSentTime >= this.cooldown;
58
+ }
59
+
60
+ /**
61
+ * Send email notification
62
+ */
63
+ async send(alertType, subject, message, details = {}) {
64
+ if (!this.config.enabled || !this.transporter) {
65
+ return { success: false, reason: 'Email notifications not enabled or configured' };
66
+ }
67
+
68
+ if (!this.canSend(alertType)) {
69
+ return { success: false, reason: 'Cooldown period active' };
70
+ }
71
+
72
+ try {
73
+ const htmlContent = this.generateHtmlEmail(alertType, subject, message, details);
74
+ const textContent = this.generateTextEmail(alertType, subject, message, details);
75
+
76
+ const mailOptions = {
77
+ from: this.config.from || this.config.auth.user,
78
+ to: this.config.recipients.join(', '),
79
+ subject: `[${this.appConfig.name}] ${subject}`,
80
+ text: textContent,
81
+ html: htmlContent
82
+ };
83
+
84
+ const info = await this.transporter.sendMail(mailOptions);
85
+
86
+ this.lastSent.set(alertType, Date.now());
87
+ this.logger.logInfo('Email notification sent', { alertType, messageId: info.messageId });
88
+
89
+ return { success: true, messageId: info.messageId };
90
+ } catch (error) {
91
+ this.logger.logWarning('email_send_failed', 'Failed to send email notification', {
92
+ error: error.message,
93
+ alertType
94
+ });
95
+ return { success: false, reason: error.message };
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Generate HTML email content
101
+ */
102
+ generateHtmlEmail(alertType, subject, message, details) {
103
+ const severity = this.getSeverityColor(alertType);
104
+ const timestamp = new Date().toLocaleString();
105
+
106
+ let detailsHtml = '';
107
+ if (Object.keys(details).length > 0) {
108
+ detailsHtml = '<h3>Details:</h3><table style="border-collapse: collapse; width: 100%;">';
109
+ for (const [key, value] of Object.entries(details)) {
110
+ detailsHtml += `
111
+ <tr>
112
+ <td style="padding: 8px; border: 1px solid #ddd; font-weight: bold;">${this.formatKey(key)}</td>
113
+ <td style="padding: 8px; border: 1px solid #ddd;">${this.formatValue(value)}</td>
114
+ </tr>
115
+ `;
116
+ }
117
+ detailsHtml += '</table>';
118
+ }
119
+
120
+ return `
121
+ <!DOCTYPE html>
122
+ <html>
123
+ <head>
124
+ <style>
125
+ body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
126
+ .container { max-width: 600px; margin: 0 auto; padding: 20px; }
127
+ .header { background-color: ${severity}; color: white; padding: 20px; border-radius: 5px 5px 0 0; }
128
+ .content { background-color: #f9f9f9; padding: 20px; border: 1px solid #ddd; border-top: none; }
129
+ .footer { background-color: #333; color: white; padding: 10px; text-align: center; font-size: 12px; border-radius: 0 0 5px 5px; }
130
+ table { margin-top: 10px; }
131
+ </style>
132
+ </head>
133
+ <body>
134
+ <div class="container">
135
+ <div class="header">
136
+ <h2 style="margin: 0;">🚨 ${subject}</h2>
137
+ </div>
138
+ <div class="content">
139
+ <p><strong>Message:</strong> ${message}</p>
140
+ <p><strong>Application:</strong> ${this.appConfig.name} (v${this.appConfig.version})</p>
141
+ <p><strong>Environment:</strong> ${this.appConfig.environment}</p>
142
+ <p><strong>Hostname:</strong> ${this.appConfig.hostname}</p>
143
+ <p><strong>Time:</strong> ${timestamp}</p>
144
+ ${detailsHtml}
145
+ </div>
146
+ <div class="footer">
147
+ Node Monitor - Automated Alert System
148
+ </div>
149
+ </div>
150
+ </body>
151
+ </html>
152
+ `;
153
+ }
154
+
155
+ /**
156
+ * Generate plain text email content
157
+ */
158
+ generateTextEmail(alertType, subject, message, details) {
159
+ const timestamp = new Date().toLocaleString();
160
+ let text = `
161
+ 🚨 ${subject}
162
+
163
+ Message: ${message}
164
+ Application: ${this.appConfig.name} (v${this.appConfig.version})
165
+ Environment: ${this.appConfig.environment}
166
+ Hostname: ${this.appConfig.hostname}
167
+ Time: ${timestamp}
168
+ `;
169
+
170
+ if (Object.keys(details).length > 0) {
171
+ text += '\nDetails:\n';
172
+ for (const [key, value] of Object.entries(details)) {
173
+ text += ` ${this.formatKey(key)}: ${this.formatValue(value)}\n`;
174
+ }
175
+ }
176
+
177
+ text += '\n---\nNode Monitor - Automated Alert System';
178
+ return text;
179
+ }
180
+
181
+ /**
182
+ * Get severity color based on alert type
183
+ */
184
+ getSeverityColor(alertType) {
185
+ const type = alertType.toLowerCase();
186
+ if (type.includes('critical') || type.includes('down') || type.includes('failed')) {
187
+ return '#dc3545'; // Red
188
+ }
189
+ if (type.includes('warning') || type.includes('high')) {
190
+ return '#ffc107'; // Yellow
191
+ }
192
+ if (type.includes('recovery') || type.includes('success')) {
193
+ return '#28a745'; // Green
194
+ }
195
+ return '#007bff'; // Blue (info)
196
+ }
197
+
198
+ /**
199
+ * Format key for display
200
+ */
201
+ formatKey(key) {
202
+ return key
203
+ .replace(/([A-Z])/g, ' $1')
204
+ .replace(/^./, str => str.toUpperCase())
205
+ .trim();
206
+ }
207
+
208
+ /**
209
+ * Format value for display
210
+ */
211
+ formatValue(value) {
212
+ if (typeof value === 'object') {
213
+ return JSON.stringify(value, null, 2);
214
+ }
215
+ return String(value);
216
+ }
217
+
218
+ /**
219
+ * Send critical alert
220
+ */
221
+ async sendCritical(subject, message, details) {
222
+ return this.send('critical', `🔴 CRITICAL: ${subject}`, message, details);
223
+ }
224
+
225
+ /**
226
+ * Send warning alert
227
+ */
228
+ async sendWarning(subject, message, details) {
229
+ return this.send('warning', `⚠️ WARNING: ${subject}`, message, details);
230
+ }
231
+
232
+ /**
233
+ * Send info alert
234
+ */
235
+ async sendInfo(subject, message, details) {
236
+ return this.send('info', `ℹ️ INFO: ${subject}`, message, details);
237
+ }
238
+
239
+ /**
240
+ * Send recovery notification
241
+ */
242
+ async sendRecovery(subject, message, details) {
243
+ return this.send('recovery', `✅ RECOVERY: ${subject}`, message, details);
244
+ }
245
+ }
246
+
247
+ module.exports = EmailNotifier;
248
+
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Notification Manager
3
+ * Coordinates all notification channels
4
+ */
5
+
6
+ const EmailNotifier = require('./emailNotifier');
7
+ const SlackNotifier = require('./slackNotifier');
8
+
9
+ class NotificationManager {
10
+ constructor(config, logger) {
11
+ this.config = config;
12
+ this.logger = logger;
13
+ this.notifiers = [];
14
+
15
+ this.initialize();
16
+ }
17
+
18
+ /**
19
+ * Initialize all enabled notifiers
20
+ */
21
+ initialize() {
22
+ if (this.config.notifications.email.enabled) {
23
+ this.notifiers.push(new EmailNotifier(this.config, this.logger));
24
+ }
25
+
26
+ if (this.config.notifications.slack.enabled) {
27
+ this.notifiers.push(new SlackNotifier(this.config, this.logger));
28
+ }
29
+
30
+ // SMS notifier can be added here when implemented
31
+ // if (this.config.notifications.sms.enabled) {
32
+ // this.notifiers.push(new SmsNotifier(this.config, this.logger));
33
+ // }
34
+
35
+ this.logger.logInfo(`Notification manager initialized with ${this.notifiers.length} notifier(s)`);
36
+ }
37
+
38
+ /**
39
+ * Send notification through all enabled channels
40
+ */
41
+ async notify(alertType, subject, message, details = {}) {
42
+ if (!this.config.notifications.enabled || this.notifiers.length === 0) {
43
+ return { success: false, reason: 'No notifiers enabled' };
44
+ }
45
+
46
+ const results = await Promise.allSettled(
47
+ this.notifiers.map(notifier =>
48
+ notifier.send(alertType, subject, message, details)
49
+ )
50
+ );
51
+
52
+ const summary = {
53
+ total: results.length,
54
+ successful: results.filter(r => r.status === 'fulfilled' && r.value.success).length,
55
+ failed: results.filter(r => r.status === 'rejected' || !r.value.success).length,
56
+ results: results
57
+ };
58
+
59
+ return summary;
60
+ }
61
+
62
+ /**
63
+ * Send critical alert
64
+ */
65
+ async sendCritical(subject, message, details = {}) {
66
+ this.logger.logSystemError('critical_alert', subject, details);
67
+ return this.notify('critical', subject, message, details);
68
+ }
69
+
70
+ /**
71
+ * Send warning alert
72
+ */
73
+ async sendWarning(subject, message, details = {}) {
74
+ this.logger.logWarning('warning_alert', subject, details);
75
+ return this.notify('warning', subject, message, details);
76
+ }
77
+
78
+ /**
79
+ * Send info notification
80
+ */
81
+ async sendInfo(subject, message, details = {}) {
82
+ this.logger.logInfo(subject, details);
83
+ return this.notify('info', subject, message, details);
84
+ }
85
+
86
+ /**
87
+ * Send recovery notification
88
+ */
89
+ async sendRecovery(subject, message, details = {}) {
90
+ this.logger.logInfo(`Recovery: ${subject}`, details);
91
+ return this.notify('recovery', subject, message, details);
92
+ }
93
+ }
94
+
95
+ module.exports = NotificationManager;
96
+
@@ -0,0 +1,209 @@
1
+ /**
2
+ * Slack Notifier
3
+ * Sends notifications to Slack using Incoming Webhooks
4
+ */
5
+
6
+ const { IncomingWebhook } = require('@slack/webhook');
7
+
8
+ class SlackNotifier {
9
+ constructor(config, logger) {
10
+ this.config = config.notifications.slack;
11
+ this.appConfig = config.app;
12
+ this.logger = logger;
13
+ this.webhook = null;
14
+ this.lastSent = new Map();
15
+ this.cooldown = config.notifications.cooldown;
16
+
17
+ if (this.config.enabled) {
18
+ this.initialize();
19
+ }
20
+ }
21
+
22
+ /**
23
+ * Initialize Slack webhook
24
+ */
25
+ initialize() {
26
+ try {
27
+ this.webhook = new IncomingWebhook(this.config.webhook);
28
+ this.logger.logInfo('Slack notifier initialized successfully');
29
+ } catch (error) {
30
+ this.logger.logWarning('slack_config', 'Failed to initialize Slack notifier', {
31
+ error: error.message
32
+ });
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Check if cooldown period has passed
38
+ */
39
+ canSend(alertType) {
40
+ const lastSentTime = this.lastSent.get(alertType);
41
+ if (!lastSentTime) return true;
42
+
43
+ return Date.now() - lastSentTime >= this.cooldown;
44
+ }
45
+
46
+ /**
47
+ * Send Slack notification
48
+ */
49
+ async send(alertType, subject, message, details = {}) {
50
+ if (!this.config.enabled || !this.webhook) {
51
+ return { success: false, reason: 'Slack notifications not enabled or configured' };
52
+ }
53
+
54
+ if (!this.canSend(alertType)) {
55
+ return { success: false, reason: 'Cooldown period active' };
56
+ }
57
+
58
+ try {
59
+ const payload = this.buildPayload(alertType, subject, message, details);
60
+ await this.webhook.send(payload);
61
+
62
+ this.lastSent.set(alertType, Date.now());
63
+ this.logger.logInfo('Slack notification sent', { alertType });
64
+
65
+ return { success: true };
66
+ } catch (error) {
67
+ this.logger.logWarning('slack_send_failed', 'Failed to send Slack notification', {
68
+ error: error.message,
69
+ alertType
70
+ });
71
+ return { success: false, reason: error.message };
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Build Slack message payload
77
+ */
78
+ buildPayload(alertType, subject, message, details) {
79
+ const color = this.getSeverityColor(alertType);
80
+ const emoji = this.getSeverityEmoji(alertType);
81
+ const timestamp = Math.floor(Date.now() / 1000);
82
+
83
+ const fields = [
84
+ {
85
+ title: 'Application',
86
+ value: `${this.appConfig.name} (v${this.appConfig.version})`,
87
+ short: true
88
+ },
89
+ {
90
+ title: 'Environment',
91
+ value: this.appConfig.environment,
92
+ short: true
93
+ },
94
+ {
95
+ title: 'Hostname',
96
+ value: this.appConfig.hostname,
97
+ short: true
98
+ },
99
+ {
100
+ title: 'Alert Type',
101
+ value: alertType.toUpperCase(),
102
+ short: true
103
+ }
104
+ ];
105
+
106
+ // Add custom details as fields
107
+ for (const [key, value] of Object.entries(details)) {
108
+ if (typeof value !== 'object') {
109
+ fields.push({
110
+ title: this.formatKey(key),
111
+ value: String(value),
112
+ short: true
113
+ });
114
+ }
115
+ }
116
+
117
+ return {
118
+ username: this.config.username,
119
+ channel: this.config.channel,
120
+ icon_emoji: emoji,
121
+ attachments: [
122
+ {
123
+ color: color,
124
+ title: subject,
125
+ text: message,
126
+ fields: fields,
127
+ footer: 'Node Monitor',
128
+ footer_icon: 'https://platform.slack-edge.com/img/default_application_icon.png',
129
+ ts: timestamp
130
+ }
131
+ ]
132
+ };
133
+ }
134
+
135
+ /**
136
+ * Get severity color for Slack
137
+ */
138
+ getSeverityColor(alertType) {
139
+ const type = alertType.toLowerCase();
140
+ if (type.includes('critical') || type.includes('down') || type.includes('failed')) {
141
+ return 'danger'; // Red
142
+ }
143
+ if (type.includes('warning') || type.includes('high')) {
144
+ return 'warning'; // Yellow
145
+ }
146
+ if (type.includes('recovery') || type.includes('success')) {
147
+ return 'good'; // Green
148
+ }
149
+ return '#007bff'; // Blue (info)
150
+ }
151
+
152
+ /**
153
+ * Get severity emoji
154
+ */
155
+ getSeverityEmoji(alertType) {
156
+ const type = alertType.toLowerCase();
157
+ if (type.includes('critical') || type.includes('down') || type.includes('failed')) {
158
+ return ':rotating_light:';
159
+ }
160
+ if (type.includes('warning') || type.includes('high')) {
161
+ return ':warning:';
162
+ }
163
+ if (type.includes('recovery') || type.includes('success')) {
164
+ return ':white_check_mark:';
165
+ }
166
+ return ':information_source:';
167
+ }
168
+
169
+ /**
170
+ * Format key for display
171
+ */
172
+ formatKey(key) {
173
+ return key
174
+ .replace(/([A-Z])/g, ' $1')
175
+ .replace(/^./, str => str.toUpperCase())
176
+ .trim();
177
+ }
178
+
179
+ /**
180
+ * Send critical alert
181
+ */
182
+ async sendCritical(subject, message, details) {
183
+ return this.send('critical', `🔴 CRITICAL: ${subject}`, message, details);
184
+ }
185
+
186
+ /**
187
+ * Send warning alert
188
+ */
189
+ async sendWarning(subject, message, details) {
190
+ return this.send('warning', `⚠️ WARNING: ${subject}`, message, details);
191
+ }
192
+
193
+ /**
194
+ * Send info alert
195
+ */
196
+ async sendInfo(subject, message, details) {
197
+ return this.send('info', `ℹ️ INFO: ${subject}`, message, details);
198
+ }
199
+
200
+ /**
201
+ * Send recovery notification
202
+ */
203
+ async sendRecovery(subject, message, details) {
204
+ return this.send('recovery', `✅ RECOVERY: ${subject}`, message, details);
205
+ }
206
+ }
207
+
208
+ module.exports = SlackNotifier;
209
+