@bernierllc/retry-suite 0.1.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/LICENSE +5 -0
- package/README.md +654 -0
- package/dist/admin/admin-server.d.ts +30 -0
- package/dist/admin/admin-server.js +348 -0
- package/dist/components/retry-dashboard.d.ts +17 -0
- package/dist/components/retry-dashboard.js +406 -0
- package/dist/config/configuration-manager.d.ts +20 -0
- package/dist/config/configuration-manager.js +309 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +32 -0
- package/dist/integrations/email-integration.d.ts +12 -0
- package/dist/integrations/email-integration.js +215 -0
- package/dist/integrations/integration-manager.d.ts +15 -0
- package/dist/integrations/integration-manager.js +108 -0
- package/dist/integrations/slack-integration.d.ts +10 -0
- package/dist/integrations/slack-integration.js +94 -0
- package/dist/integrations/webhook-integration.d.ts +10 -0
- package/dist/integrations/webhook-integration.js +82 -0
- package/dist/retry-suite.d.ts +34 -0
- package/dist/retry-suite.js +328 -0
- package/dist/types.d.ts +205 -0
- package/dist/types.js +9 -0
- package/package.json +71 -0
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Copyright (c) 2025 Bernier LLC
|
|
4
|
+
|
|
5
|
+
This file is licensed to the client under a limited-use license.
|
|
6
|
+
The client may use and modify this code *only within the scope of the project it was delivered for*.
|
|
7
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.ConfigurationManager = void 0;
|
|
11
|
+
class ConfigurationManager {
|
|
12
|
+
constructor(initialConfig) {
|
|
13
|
+
this.configValidators = new Map();
|
|
14
|
+
this.config = { ...initialConfig };
|
|
15
|
+
this.setupValidators();
|
|
16
|
+
}
|
|
17
|
+
setupValidators() {
|
|
18
|
+
this.configValidators.set('retryManager', this.validateRetryManager.bind(this));
|
|
19
|
+
this.configValidators.set('retryStorage', this.validateRetryStorage.bind(this));
|
|
20
|
+
this.configValidators.set('retryMonitoring', this.validateRetryMonitoring.bind(this));
|
|
21
|
+
this.configValidators.set('admin', this.validateAdmin.bind(this));
|
|
22
|
+
this.configValidators.set('integrations', this.validateIntegrations.bind(this));
|
|
23
|
+
this.configValidators.set('logging', this.validateLogging.bind(this));
|
|
24
|
+
}
|
|
25
|
+
async updateConfiguration(updates) {
|
|
26
|
+
const newConfig = { ...this.config, ...updates };
|
|
27
|
+
const validation = this.validateConfiguration(newConfig);
|
|
28
|
+
if (!validation.isValid) {
|
|
29
|
+
return {
|
|
30
|
+
success: false,
|
|
31
|
+
errors: validation.errors
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
const oldConfig = { ...this.config };
|
|
36
|
+
this.config = newConfig;
|
|
37
|
+
await this.notifyConfigurationChange(oldConfig, newConfig);
|
|
38
|
+
return {
|
|
39
|
+
success: true,
|
|
40
|
+
message: 'Configuration updated successfully'
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
this.config = { ...this.config };
|
|
45
|
+
return {
|
|
46
|
+
success: false,
|
|
47
|
+
errors: [`Failed to apply configuration: ${error.message}`]
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
getConfiguration() {
|
|
52
|
+
return JSON.parse(JSON.stringify(this.config));
|
|
53
|
+
}
|
|
54
|
+
validateConfiguration(config) {
|
|
55
|
+
const errors = [];
|
|
56
|
+
for (const [key, validator] of this.configValidators) {
|
|
57
|
+
if (config[key]) {
|
|
58
|
+
const validationErrors = validator(config[key]);
|
|
59
|
+
errors.push(...validationErrors);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
errors.push(`${key} configuration is required`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
isValid: errors.length === 0,
|
|
67
|
+
errors
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
validateRetryManager(config) {
|
|
71
|
+
const errors = [];
|
|
72
|
+
if (!config.defaultOptions) {
|
|
73
|
+
errors.push('retryManager.defaultOptions is required');
|
|
74
|
+
return errors;
|
|
75
|
+
}
|
|
76
|
+
const opts = config.defaultOptions;
|
|
77
|
+
if (typeof opts.maxRetries !== 'number' || opts.maxRetries < 0) {
|
|
78
|
+
errors.push('retryManager.defaultOptions.maxRetries must be a non-negative number');
|
|
79
|
+
}
|
|
80
|
+
if (typeof opts.initialDelayMs !== 'number' || opts.initialDelayMs < 0) {
|
|
81
|
+
errors.push('retryManager.defaultOptions.initialDelayMs must be a non-negative number');
|
|
82
|
+
}
|
|
83
|
+
if (typeof opts.backoffFactor !== 'number' || opts.backoffFactor < 1) {
|
|
84
|
+
errors.push('retryManager.defaultOptions.backoffFactor must be >= 1');
|
|
85
|
+
}
|
|
86
|
+
if (opts.maxDelayMs !== undefined) {
|
|
87
|
+
if (typeof opts.maxDelayMs !== 'number' || opts.maxDelayMs < opts.initialDelayMs) {
|
|
88
|
+
errors.push('retryManager.defaultOptions.maxDelayMs must be >= initialDelayMs');
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return errors;
|
|
92
|
+
}
|
|
93
|
+
validateRetryStorage(config) {
|
|
94
|
+
const errors = [];
|
|
95
|
+
if (!config.type) {
|
|
96
|
+
errors.push('retryStorage.type is required');
|
|
97
|
+
}
|
|
98
|
+
else if (!['memory', 'redis', 'file'].includes(config.type)) {
|
|
99
|
+
errors.push('retryStorage.type must be one of: memory, redis, file');
|
|
100
|
+
}
|
|
101
|
+
if (!config.options || typeof config.options !== 'object') {
|
|
102
|
+
errors.push('retryStorage.options must be an object');
|
|
103
|
+
}
|
|
104
|
+
if (config.type === 'redis' && config.options) {
|
|
105
|
+
if (!config.options.url && !config.options.host) {
|
|
106
|
+
errors.push('retryStorage.options must include url or host for redis type');
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (config.type === 'file' && config.options) {
|
|
110
|
+
if (!config.options.path) {
|
|
111
|
+
errors.push('retryStorage.options.path is required for file type');
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return errors;
|
|
115
|
+
}
|
|
116
|
+
validateRetryMonitoring(config) {
|
|
117
|
+
const errors = [];
|
|
118
|
+
if (typeof config.enabled !== 'boolean') {
|
|
119
|
+
errors.push('retryMonitoring.enabled must be a boolean');
|
|
120
|
+
}
|
|
121
|
+
if (typeof config.metricsInterval !== 'number' || config.metricsInterval < 1000) {
|
|
122
|
+
errors.push('retryMonitoring.metricsInterval must be >= 1000ms');
|
|
123
|
+
}
|
|
124
|
+
if (!config.alertThresholds) {
|
|
125
|
+
errors.push('retryMonitoring.alertThresholds is required');
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
const thresholds = config.alertThresholds;
|
|
129
|
+
if (typeof thresholds.failureRate !== 'number' ||
|
|
130
|
+
thresholds.failureRate < 0 ||
|
|
131
|
+
thresholds.failureRate > 1) {
|
|
132
|
+
errors.push('retryMonitoring.alertThresholds.failureRate must be between 0 and 1');
|
|
133
|
+
}
|
|
134
|
+
if (typeof thresholds.averageRetryTime !== 'number' ||
|
|
135
|
+
thresholds.averageRetryTime < 0) {
|
|
136
|
+
errors.push('retryMonitoring.alertThresholds.averageRetryTime must be >= 0');
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return errors;
|
|
140
|
+
}
|
|
141
|
+
validateAdmin(config) {
|
|
142
|
+
const errors = [];
|
|
143
|
+
if (typeof config.port !== 'number' ||
|
|
144
|
+
config.port < 1 ||
|
|
145
|
+
config.port > 65535) {
|
|
146
|
+
errors.push('admin.port must be between 1 and 65535');
|
|
147
|
+
}
|
|
148
|
+
if (typeof config.host !== 'string' || !config.host) {
|
|
149
|
+
errors.push('admin.host must be a non-empty string');
|
|
150
|
+
}
|
|
151
|
+
if (!config.auth || typeof config.auth !== 'object') {
|
|
152
|
+
errors.push('admin.auth configuration is required');
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
if (typeof config.auth.enabled !== 'boolean') {
|
|
156
|
+
errors.push('admin.auth.enabled must be a boolean');
|
|
157
|
+
}
|
|
158
|
+
if (config.auth.enabled) {
|
|
159
|
+
if (!config.auth.username || typeof config.auth.username !== 'string') {
|
|
160
|
+
errors.push('admin.auth.username is required when auth is enabled');
|
|
161
|
+
}
|
|
162
|
+
if (!config.auth.password || typeof config.auth.password !== 'string') {
|
|
163
|
+
errors.push('admin.auth.password is required when auth is enabled');
|
|
164
|
+
}
|
|
165
|
+
if (config.auth.password && config.auth.password.length < 8) {
|
|
166
|
+
errors.push('admin.auth.password must be at least 8 characters long');
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return errors;
|
|
171
|
+
}
|
|
172
|
+
validateIntegrations(config) {
|
|
173
|
+
const errors = [];
|
|
174
|
+
if (!Array.isArray(config)) {
|
|
175
|
+
errors.push('integrations must be an array');
|
|
176
|
+
return errors;
|
|
177
|
+
}
|
|
178
|
+
for (let i = 0; i < config.length; i++) {
|
|
179
|
+
const integration = config[i];
|
|
180
|
+
const prefix = `integrations[${i}]`;
|
|
181
|
+
if (!integration.type) {
|
|
182
|
+
errors.push(`${prefix}.type is required`);
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
if (!['slack', 'pagerduty', 'email', 'webhook'].includes(integration.type)) {
|
|
186
|
+
errors.push(`${prefix}.type must be one of: slack, pagerduty, email, webhook`);
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
if (!integration.config || typeof integration.config !== 'object') {
|
|
190
|
+
errors.push(`${prefix}.config is required and must be an object`);
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
// Validate specific integration configs
|
|
194
|
+
switch (integration.type) {
|
|
195
|
+
case 'slack':
|
|
196
|
+
if (!integration.config.token) {
|
|
197
|
+
errors.push(`${prefix}.config.token is required for Slack integration`);
|
|
198
|
+
}
|
|
199
|
+
if (!integration.config.channel) {
|
|
200
|
+
errors.push(`${prefix}.config.channel is required for Slack integration`);
|
|
201
|
+
}
|
|
202
|
+
break;
|
|
203
|
+
case 'email':
|
|
204
|
+
if (!integration.config.provider) {
|
|
205
|
+
errors.push(`${prefix}.config.provider is required for email integration`);
|
|
206
|
+
}
|
|
207
|
+
if (!integration.config.from) {
|
|
208
|
+
errors.push(`${prefix}.config.from is required for email integration`);
|
|
209
|
+
}
|
|
210
|
+
if (!Array.isArray(integration.config.to) || integration.config.to.length === 0) {
|
|
211
|
+
errors.push(`${prefix}.config.to must be a non-empty array for email integration`);
|
|
212
|
+
}
|
|
213
|
+
break;
|
|
214
|
+
case 'webhook':
|
|
215
|
+
if (!integration.config.url) {
|
|
216
|
+
errors.push(`${prefix}.config.url is required for webhook integration`);
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
try {
|
|
220
|
+
new URL(integration.config.url);
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
errors.push(`${prefix}.config.url must be a valid URL for webhook integration`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
break;
|
|
227
|
+
case 'pagerduty':
|
|
228
|
+
if (!integration.config.apiKey) {
|
|
229
|
+
errors.push(`${prefix}.config.apiKey is required for PagerDuty integration`);
|
|
230
|
+
}
|
|
231
|
+
if (!integration.config.serviceId) {
|
|
232
|
+
errors.push(`${prefix}.config.serviceId is required for PagerDuty integration`);
|
|
233
|
+
}
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return errors;
|
|
238
|
+
}
|
|
239
|
+
validateLogging(config) {
|
|
240
|
+
const errors = [];
|
|
241
|
+
if (!['debug', 'info', 'warn', 'error'].includes(config.level)) {
|
|
242
|
+
errors.push('logging.level must be one of: debug, info, warn, error');
|
|
243
|
+
}
|
|
244
|
+
if (!['json', 'text'].includes(config.format)) {
|
|
245
|
+
errors.push('logging.format must be one of: json, text');
|
|
246
|
+
}
|
|
247
|
+
return errors;
|
|
248
|
+
}
|
|
249
|
+
async notifyConfigurationChange(oldConfig, newConfig) {
|
|
250
|
+
console.log('Configuration updated:', {
|
|
251
|
+
timestamp: new Date().toISOString(),
|
|
252
|
+
changes: this.getConfigChanges(oldConfig, newConfig)
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
getConfigChanges(oldConfig, newConfig) {
|
|
256
|
+
const changes = {};
|
|
257
|
+
for (const key of Object.keys(newConfig)) {
|
|
258
|
+
if (JSON.stringify(oldConfig[key]) !== JSON.stringify(newConfig[key])) {
|
|
259
|
+
changes[key] = {
|
|
260
|
+
old: oldConfig[key],
|
|
261
|
+
new: newConfig[key]
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return changes;
|
|
266
|
+
}
|
|
267
|
+
resetToDefaults() {
|
|
268
|
+
this.config = this.getDefaultConfiguration();
|
|
269
|
+
return this.getConfiguration();
|
|
270
|
+
}
|
|
271
|
+
getDefaultConfiguration() {
|
|
272
|
+
return {
|
|
273
|
+
retryManager: {
|
|
274
|
+
defaultOptions: {
|
|
275
|
+
maxRetries: 5,
|
|
276
|
+
initialDelayMs: 1000,
|
|
277
|
+
backoffFactor: 2,
|
|
278
|
+
maxDelayMs: 30000,
|
|
279
|
+
jitter: true
|
|
280
|
+
}
|
|
281
|
+
},
|
|
282
|
+
retryStorage: {
|
|
283
|
+
type: 'memory',
|
|
284
|
+
options: {}
|
|
285
|
+
},
|
|
286
|
+
retryMonitoring: {
|
|
287
|
+
enabled: true,
|
|
288
|
+
metricsInterval: 60000,
|
|
289
|
+
alertThresholds: {
|
|
290
|
+
failureRate: 0.1,
|
|
291
|
+
averageRetryTime: 5000
|
|
292
|
+
}
|
|
293
|
+
},
|
|
294
|
+
admin: {
|
|
295
|
+
port: 3000,
|
|
296
|
+
host: 'localhost',
|
|
297
|
+
auth: {
|
|
298
|
+
enabled: false
|
|
299
|
+
}
|
|
300
|
+
},
|
|
301
|
+
integrations: [],
|
|
302
|
+
logging: {
|
|
303
|
+
level: 'info',
|
|
304
|
+
format: 'json'
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
exports.ConfigurationManager = ConfigurationManager;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { RetrySuite } from './retry-suite';
|
|
2
|
+
export { AdminServer } from './admin/admin-server';
|
|
3
|
+
export { IntegrationManager } from './integrations/integration-manager';
|
|
4
|
+
export { ConfigurationManager } from './config/configuration-manager';
|
|
5
|
+
export { SlackIntegration } from './integrations/slack-integration';
|
|
6
|
+
export { EmailIntegration } from './integrations/email-integration';
|
|
7
|
+
export { WebhookIntegration } from './integrations/webhook-integration';
|
|
8
|
+
export { RetryDashboard, MetricsOverview, AlertsList, RetriesList } from './components/retry-dashboard';
|
|
9
|
+
export type { RetrySuiteConfig, RetryManagerConfig, RetryStorageConfig, RetryMonitoringConfig, AdminConfig, IntegrationConfig, LoggingConfig, RetryStatus, RetryOptions, RetryResult, RetryMetrics, FailureReason, HourlyMetrics, StorageMetrics, Alert, PerformanceReport, TrendData, Recommendation, MetricsOptions, AlertOptions, ReportOptions, ScheduleResult, CancelResult, PauseResult, ResumeResult, ConfigResult, ValidationResult, Integration, SlackConfig, PagerDutyConfig, EmailConfig, WebhookConfig } from './types';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Copyright (c) 2025 Bernier LLC
|
|
4
|
+
|
|
5
|
+
This file is licensed to the client under a limited-use license.
|
|
6
|
+
The client may use and modify this code *only within the scope of the project it was delivered for*.
|
|
7
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.RetriesList = exports.AlertsList = exports.MetricsOverview = exports.RetryDashboard = exports.WebhookIntegration = exports.EmailIntegration = exports.SlackIntegration = exports.ConfigurationManager = exports.IntegrationManager = exports.AdminServer = exports.RetrySuite = void 0;
|
|
11
|
+
// Main classes
|
|
12
|
+
var retry_suite_1 = require("./retry-suite");
|
|
13
|
+
Object.defineProperty(exports, "RetrySuite", { enumerable: true, get: function () { return retry_suite_1.RetrySuite; } });
|
|
14
|
+
var admin_server_1 = require("./admin/admin-server");
|
|
15
|
+
Object.defineProperty(exports, "AdminServer", { enumerable: true, get: function () { return admin_server_1.AdminServer; } });
|
|
16
|
+
var integration_manager_1 = require("./integrations/integration-manager");
|
|
17
|
+
Object.defineProperty(exports, "IntegrationManager", { enumerable: true, get: function () { return integration_manager_1.IntegrationManager; } });
|
|
18
|
+
var configuration_manager_1 = require("./config/configuration-manager");
|
|
19
|
+
Object.defineProperty(exports, "ConfigurationManager", { enumerable: true, get: function () { return configuration_manager_1.ConfigurationManager; } });
|
|
20
|
+
// Integration classes
|
|
21
|
+
var slack_integration_1 = require("./integrations/slack-integration");
|
|
22
|
+
Object.defineProperty(exports, "SlackIntegration", { enumerable: true, get: function () { return slack_integration_1.SlackIntegration; } });
|
|
23
|
+
var email_integration_1 = require("./integrations/email-integration");
|
|
24
|
+
Object.defineProperty(exports, "EmailIntegration", { enumerable: true, get: function () { return email_integration_1.EmailIntegration; } });
|
|
25
|
+
var webhook_integration_1 = require("./integrations/webhook-integration");
|
|
26
|
+
Object.defineProperty(exports, "WebhookIntegration", { enumerable: true, get: function () { return webhook_integration_1.WebhookIntegration; } });
|
|
27
|
+
// React components
|
|
28
|
+
var retry_dashboard_1 = require("./components/retry-dashboard");
|
|
29
|
+
Object.defineProperty(exports, "RetryDashboard", { enumerable: true, get: function () { return retry_dashboard_1.RetryDashboard; } });
|
|
30
|
+
Object.defineProperty(exports, "MetricsOverview", { enumerable: true, get: function () { return retry_dashboard_1.MetricsOverview; } });
|
|
31
|
+
Object.defineProperty(exports, "AlertsList", { enumerable: true, get: function () { return retry_dashboard_1.AlertsList; } });
|
|
32
|
+
Object.defineProperty(exports, "RetriesList", { enumerable: true, get: function () { return retry_dashboard_1.RetriesList; } });
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Alert, Integration, EmailConfig } from '../types';
|
|
2
|
+
export declare class EmailIntegration implements Integration {
|
|
3
|
+
readonly type = "email";
|
|
4
|
+
private config;
|
|
5
|
+
constructor(config: EmailConfig);
|
|
6
|
+
sendAlert(alert: Alert): Promise<void>;
|
|
7
|
+
sendCriticalAlert(alert: Alert): Promise<void>;
|
|
8
|
+
private generateEmailTemplate;
|
|
9
|
+
private generateCriticalEmailTemplate;
|
|
10
|
+
private getSeverityColor;
|
|
11
|
+
private sendEmail;
|
|
12
|
+
}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Copyright (c) 2025 Bernier LLC
|
|
4
|
+
|
|
5
|
+
This file is licensed to the client under a limited-use license.
|
|
6
|
+
The client may use and modify this code *only within the scope of the project it was delivered for*.
|
|
7
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.EmailIntegration = void 0;
|
|
11
|
+
class EmailIntegration {
|
|
12
|
+
constructor(config) {
|
|
13
|
+
this.type = 'email';
|
|
14
|
+
this.config = config;
|
|
15
|
+
}
|
|
16
|
+
async sendAlert(alert) {
|
|
17
|
+
if (alert.severity === 'low') {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const subject = `[${alert.severity.toUpperCase()}] ${alert.message}`;
|
|
21
|
+
const htmlContent = this.generateEmailTemplate(alert);
|
|
22
|
+
await this.sendEmail({
|
|
23
|
+
to: this.config.to,
|
|
24
|
+
subject,
|
|
25
|
+
html: htmlContent
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
async sendCriticalAlert(alert) {
|
|
29
|
+
const subject = `[CRITICAL ALERT] ${alert.message}`;
|
|
30
|
+
const htmlContent = this.generateCriticalEmailTemplate(alert);
|
|
31
|
+
await this.sendEmail({
|
|
32
|
+
to: this.config.to,
|
|
33
|
+
subject,
|
|
34
|
+
html: htmlContent,
|
|
35
|
+
priority: 'high'
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
generateEmailTemplate(alert) {
|
|
39
|
+
const severityColor = this.getSeverityColor(alert.severity);
|
|
40
|
+
return `
|
|
41
|
+
<!DOCTYPE html>
|
|
42
|
+
<html>
|
|
43
|
+
<head>
|
|
44
|
+
<meta charset="utf-8">
|
|
45
|
+
<title>Retry Suite Alert</title>
|
|
46
|
+
<style>
|
|
47
|
+
body { font-family: Arial, sans-serif; margin: 0; padding: 20px; }
|
|
48
|
+
.alert-container {
|
|
49
|
+
max-width: 600px;
|
|
50
|
+
margin: 0 auto;
|
|
51
|
+
border: 1px solid #ddd;
|
|
52
|
+
border-radius: 8px;
|
|
53
|
+
overflow: hidden;
|
|
54
|
+
}
|
|
55
|
+
.alert-header {
|
|
56
|
+
background-color: ${severityColor};
|
|
57
|
+
color: white;
|
|
58
|
+
padding: 20px;
|
|
59
|
+
text-align: center;
|
|
60
|
+
}
|
|
61
|
+
.alert-body { padding: 20px; }
|
|
62
|
+
.alert-field { margin: 10px 0; }
|
|
63
|
+
.alert-field strong { color: #333; }
|
|
64
|
+
.footer {
|
|
65
|
+
background-color: #f5f5f5;
|
|
66
|
+
padding: 15px 20px;
|
|
67
|
+
font-size: 12px;
|
|
68
|
+
color: #666;
|
|
69
|
+
}
|
|
70
|
+
</style>
|
|
71
|
+
</head>
|
|
72
|
+
<body>
|
|
73
|
+
<div class="alert-container">
|
|
74
|
+
<div class="alert-header">
|
|
75
|
+
<h1>Retry Suite Alert</h1>
|
|
76
|
+
<h2>${alert.message}</h2>
|
|
77
|
+
</div>
|
|
78
|
+
<div class="alert-body">
|
|
79
|
+
<div class="alert-field">
|
|
80
|
+
<strong>Type:</strong> ${alert.type}
|
|
81
|
+
</div>
|
|
82
|
+
<div class="alert-field">
|
|
83
|
+
<strong>Severity:</strong> ${alert.severity}
|
|
84
|
+
</div>
|
|
85
|
+
<div class="alert-field">
|
|
86
|
+
<strong>Time:</strong> ${alert.timestamp.toISOString()}
|
|
87
|
+
</div>
|
|
88
|
+
<div class="alert-field">
|
|
89
|
+
<strong>Alert ID:</strong> ${alert.id}
|
|
90
|
+
</div>
|
|
91
|
+
${Object.keys(alert.metadata).length > 0 ? `
|
|
92
|
+
<div class="alert-field">
|
|
93
|
+
<strong>Details:</strong>
|
|
94
|
+
<pre>${JSON.stringify(alert.metadata, null, 2)}</pre>
|
|
95
|
+
</div>
|
|
96
|
+
` : ''}
|
|
97
|
+
</div>
|
|
98
|
+
<div class="footer">
|
|
99
|
+
This alert was generated by Retry Suite monitoring system.
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
</body>
|
|
103
|
+
</html>
|
|
104
|
+
`;
|
|
105
|
+
}
|
|
106
|
+
generateCriticalEmailTemplate(alert) {
|
|
107
|
+
return `
|
|
108
|
+
<!DOCTYPE html>
|
|
109
|
+
<html>
|
|
110
|
+
<head>
|
|
111
|
+
<meta charset="utf-8">
|
|
112
|
+
<title>CRITICAL ALERT - Retry Suite</title>
|
|
113
|
+
<style>
|
|
114
|
+
body { font-family: Arial, sans-serif; margin: 0; padding: 20px; background-color: #ffe6e6; }
|
|
115
|
+
.alert-container {
|
|
116
|
+
max-width: 600px;
|
|
117
|
+
margin: 0 auto;
|
|
118
|
+
border: 3px solid #ff0000;
|
|
119
|
+
border-radius: 8px;
|
|
120
|
+
overflow: hidden;
|
|
121
|
+
background-color: white;
|
|
122
|
+
}
|
|
123
|
+
.alert-header {
|
|
124
|
+
background-color: #ff0000;
|
|
125
|
+
color: white;
|
|
126
|
+
padding: 20px;
|
|
127
|
+
text-align: center;
|
|
128
|
+
}
|
|
129
|
+
.critical-banner {
|
|
130
|
+
background-color: #8b0000;
|
|
131
|
+
color: white;
|
|
132
|
+
padding: 10px;
|
|
133
|
+
text-align: center;
|
|
134
|
+
font-weight: bold;
|
|
135
|
+
font-size: 18px;
|
|
136
|
+
}
|
|
137
|
+
.alert-body { padding: 20px; }
|
|
138
|
+
.alert-field { margin: 15px 0; }
|
|
139
|
+
.alert-field strong { color: #333; }
|
|
140
|
+
.footer {
|
|
141
|
+
background-color: #f5f5f5;
|
|
142
|
+
padding: 15px 20px;
|
|
143
|
+
font-size: 12px;
|
|
144
|
+
color: #666;
|
|
145
|
+
}
|
|
146
|
+
</style>
|
|
147
|
+
</head>
|
|
148
|
+
<body>
|
|
149
|
+
<div class="alert-container">
|
|
150
|
+
<div class="critical-banner">
|
|
151
|
+
🚨 CRITICAL SYSTEM ALERT 🚨
|
|
152
|
+
</div>
|
|
153
|
+
<div class="alert-header">
|
|
154
|
+
<h1>IMMEDIATE ATTENTION REQUIRED</h1>
|
|
155
|
+
<h2>${alert.message}</h2>
|
|
156
|
+
</div>
|
|
157
|
+
<div class="alert-body">
|
|
158
|
+
<div class="alert-field">
|
|
159
|
+
<strong>Type:</strong> ${alert.type}
|
|
160
|
+
</div>
|
|
161
|
+
<div class="alert-field">
|
|
162
|
+
<strong>Severity:</strong> <span style="color: red; font-weight: bold;">${alert.severity.toUpperCase()}</span>
|
|
163
|
+
</div>
|
|
164
|
+
<div class="alert-field">
|
|
165
|
+
<strong>Time:</strong> ${alert.timestamp.toISOString()}
|
|
166
|
+
</div>
|
|
167
|
+
<div class="alert-field">
|
|
168
|
+
<strong>Alert ID:</strong> ${alert.id}
|
|
169
|
+
</div>
|
|
170
|
+
<div class="alert-field">
|
|
171
|
+
<strong>Details:</strong>
|
|
172
|
+
<pre style="background-color: #f5f5f5; padding: 10px; border-radius: 4px;">${JSON.stringify(alert.metadata, null, 2)}</pre>
|
|
173
|
+
</div>
|
|
174
|
+
</div>
|
|
175
|
+
<div class="footer">
|
|
176
|
+
<strong>This is a critical alert that requires immediate attention.</strong><br>
|
|
177
|
+
Generated by Retry Suite monitoring system at ${alert.timestamp.toISOString()}.
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
</body>
|
|
181
|
+
</html>
|
|
182
|
+
`;
|
|
183
|
+
}
|
|
184
|
+
getSeverityColor(severity) {
|
|
185
|
+
switch (severity) {
|
|
186
|
+
case 'low': return '#36a64f';
|
|
187
|
+
case 'medium': return '#ffa500';
|
|
188
|
+
case 'high': return '#ff0000';
|
|
189
|
+
case 'critical': return '#8b0000';
|
|
190
|
+
default: return '#36a64f';
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
async sendEmail(params) {
|
|
194
|
+
try {
|
|
195
|
+
// This would integrate with an actual email service like SendGrid, SES, etc.
|
|
196
|
+
// For now, we'll simulate the email sending
|
|
197
|
+
console.log(`[EMAIL] Sending email to: ${params.to.join(', ')}`);
|
|
198
|
+
console.log(`[EMAIL] Subject: ${params.subject}`);
|
|
199
|
+
console.log(`[EMAIL] Priority: ${params.priority || 'normal'}`);
|
|
200
|
+
// In a real implementation, you would use a service like:
|
|
201
|
+
// - SendGrid
|
|
202
|
+
// - Amazon SES
|
|
203
|
+
// - Mailgun
|
|
204
|
+
// - SMTP server
|
|
205
|
+
// Simulate network delay
|
|
206
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
207
|
+
console.log('[EMAIL] Email sent successfully');
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
console.error('Failed to send email:', error);
|
|
211
|
+
throw error;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
exports.EmailIntegration = EmailIntegration;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { IntegrationConfig, Alert } from '../types';
|
|
2
|
+
export declare class IntegrationManager {
|
|
3
|
+
private integrations;
|
|
4
|
+
private config;
|
|
5
|
+
constructor(config: IntegrationConfig[]);
|
|
6
|
+
private setupIntegrations;
|
|
7
|
+
private createIntegration;
|
|
8
|
+
sendAlert(alert: Alert): Promise<void>;
|
|
9
|
+
sendCriticalAlert(alert: Alert): Promise<void>;
|
|
10
|
+
getIntegrations(): string[];
|
|
11
|
+
hasIntegration(type: string): boolean;
|
|
12
|
+
testIntegration(type: string): Promise<boolean>;
|
|
13
|
+
testAllIntegrations(): Promise<Record<string, boolean>>;
|
|
14
|
+
updateConfiguration(config: IntegrationConfig[]): void;
|
|
15
|
+
}
|