@tamyla/clodo-framework 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/CHANGELOG.md +564 -0
- package/LICENSE +21 -0
- package/README.md +1393 -0
- package/bin/README.md +71 -0
- package/bin/clodo-service.js +416 -0
- package/bin/security/security-cli.js +96 -0
- package/bin/service-management/README.md +74 -0
- package/bin/service-management/create-service.js +129 -0
- package/bin/service-management/init-service.js +102 -0
- package/bin/service-management/init-service.js.backup +889 -0
- package/bin/shared/config/customer-cli.js +293 -0
- package/dist/config/ConfigurationManager.js +159 -0
- package/dist/config/CustomerConfigCLI.js +220 -0
- package/dist/config/FeatureManager.js +426 -0
- package/dist/config/customers.js +441 -0
- package/dist/config/domains.js +180 -0
- package/dist/config/features.js +225 -0
- package/dist/config/index.js +6 -0
- package/dist/database/database-orchestrator.js +730 -0
- package/dist/database/index.js +4 -0
- package/dist/deployment/auditor.js +971 -0
- package/dist/deployment/index.js +10 -0
- package/dist/deployment/rollback-manager.js +523 -0
- package/dist/deployment/testers/api-tester.js +80 -0
- package/dist/deployment/testers/auth-tester.js +129 -0
- package/dist/deployment/testers/core.js +217 -0
- package/dist/deployment/testers/database-tester.js +105 -0
- package/dist/deployment/testers/index.js +74 -0
- package/dist/deployment/testers/load-tester.js +120 -0
- package/dist/deployment/testers/performance-tester.js +105 -0
- package/dist/deployment/validator.js +558 -0
- package/dist/deployment/wrangler-deployer.js +574 -0
- package/dist/handlers/GenericRouteHandler.js +532 -0
- package/dist/index.js +39 -0
- package/dist/migration/MigrationAdapters.js +562 -0
- package/dist/modules/ModuleManager.js +668 -0
- package/dist/modules/security.js +98 -0
- package/dist/orchestration/cross-domain-coordinator.js +1083 -0
- package/dist/orchestration/index.js +5 -0
- package/dist/orchestration/modules/DeploymentCoordinator.js +258 -0
- package/dist/orchestration/modules/DomainResolver.js +196 -0
- package/dist/orchestration/modules/StateManager.js +332 -0
- package/dist/orchestration/multi-domain-orchestrator.js +255 -0
- package/dist/routing/EnhancedRouter.js +158 -0
- package/dist/schema/SchemaManager.js +778 -0
- package/dist/security/ConfigurationValidator.js +490 -0
- package/dist/security/DeploymentManager.js +208 -0
- package/dist/security/SecretGenerator.js +142 -0
- package/dist/security/SecurityCLI.js +228 -0
- package/dist/security/index.js +51 -0
- package/dist/security/patterns/environment-rules.js +66 -0
- package/dist/security/patterns/insecure-patterns.js +21 -0
- package/dist/service-management/ConfirmationEngine.js +411 -0
- package/dist/service-management/ErrorTracker.js +294 -0
- package/dist/service-management/GenerationEngine.js +3109 -0
- package/dist/service-management/InputCollector.js +237 -0
- package/dist/service-management/ServiceCreator.js +229 -0
- package/dist/service-management/ServiceInitializer.js +448 -0
- package/dist/service-management/ServiceOrchestrator.js +638 -0
- package/dist/service-management/handlers/ConfigMutator.js +130 -0
- package/dist/service-management/handlers/ConfirmationHandler.js +71 -0
- package/dist/service-management/handlers/GenerationHandler.js +80 -0
- package/dist/service-management/handlers/InputHandler.js +59 -0
- package/dist/service-management/handlers/ValidationHandler.js +203 -0
- package/dist/service-management/index.js +7 -0
- package/dist/services/GenericDataService.js +488 -0
- package/dist/shared/cloudflare/domain-discovery.js +562 -0
- package/dist/shared/cloudflare/domain-manager.js +912 -0
- package/dist/shared/cloudflare/index.js +8 -0
- package/dist/shared/cloudflare/ops.js +387 -0
- package/dist/shared/config/cache.js +1167 -0
- package/dist/shared/config/command-config-manager.js +174 -0
- package/dist/shared/config/customer-cli.js +258 -0
- package/dist/shared/config/index.js +9 -0
- package/dist/shared/config/manager.js +289 -0
- package/dist/shared/database/connection-manager.js +338 -0
- package/dist/shared/database/index.js +7 -0
- package/dist/shared/database/orchestrator.js +632 -0
- package/dist/shared/deployment/auditor.js +971 -0
- package/dist/shared/deployment/index.js +10 -0
- package/dist/shared/deployment/rollback-manager.js +523 -0
- package/dist/shared/deployment/validator.js +558 -0
- package/dist/shared/index.js +32 -0
- package/dist/shared/monitoring/health-checker.js +250 -0
- package/dist/shared/monitoring/index.js +8 -0
- package/dist/shared/monitoring/memory-manager.js +382 -0
- package/dist/shared/monitoring/production-monitor.js +390 -0
- package/dist/shared/production-tester/api-tester.js +80 -0
- package/dist/shared/production-tester/auth-tester.js +129 -0
- package/dist/shared/production-tester/core.js +217 -0
- package/dist/shared/production-tester/database-tester.js +105 -0
- package/dist/shared/production-tester/index.js +74 -0
- package/dist/shared/production-tester/load-tester.js +120 -0
- package/dist/shared/production-tester/performance-tester.js +105 -0
- package/dist/shared/security/api-token-manager.js +296 -0
- package/dist/shared/security/index.js +8 -0
- package/dist/shared/security/secret-generator.js +918 -0
- package/dist/shared/security/secure-token-manager.js +379 -0
- package/dist/shared/utils/error-recovery.js +240 -0
- package/dist/shared/utils/graceful-shutdown-manager.js +380 -0
- package/dist/shared/utils/index.js +9 -0
- package/dist/shared/utils/interactive-prompts.js +134 -0
- package/dist/shared/utils/rate-limiter.js +249 -0
- package/dist/utils/ErrorHandler.js +173 -0
- package/dist/utils/deployment/config-cache.js +1160 -0
- package/dist/utils/deployment/index.js +6 -0
- package/dist/utils/deployment/interactive-prompts.js +97 -0
- package/dist/utils/deployment/secret-generator.js +896 -0
- package/dist/utils/dirname-helper.js +35 -0
- package/dist/utils/domain-config.js +159 -0
- package/dist/utils/error-recovery.js +240 -0
- package/dist/utils/esm-helper.js +52 -0
- package/dist/utils/framework-config.js +481 -0
- package/dist/utils/graceful-shutdown-manager.js +379 -0
- package/dist/utils/health-checker.js +114 -0
- package/dist/utils/index.js +36 -0
- package/dist/utils/prompt-handler.js +98 -0
- package/dist/utils/usage-tracker.js +252 -0
- package/dist/utils/validation.js +112 -0
- package/dist/version/VersionDetector.js +723 -0
- package/dist/worker/index.js +4 -0
- package/dist/worker/integration.js +332 -0
- package/docs/FRAMEWORK-ARCHITECTURE-OVERVIEW.md +206 -0
- package/docs/INTEGRATION_GUIDE.md +2045 -0
- package/docs/README.md +82 -0
- package/docs/SECURITY.md +242 -0
- package/docs/deployment/deployment-guide.md +540 -0
- package/docs/overview.md +280 -0
- package/package.json +176 -0
- package/types/index.d.ts +575 -0
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Production Monitoring Module
|
|
3
|
+
* Implements structured logging, metrics collection, and alerting
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { writeFile, appendFile, mkdir } from 'fs/promises';
|
|
7
|
+
import { join } from 'path';
|
|
8
|
+
export class ProductionMonitor {
|
|
9
|
+
constructor(options = {}) {
|
|
10
|
+
this.config = {
|
|
11
|
+
logLevel: options.logLevel || 'info',
|
|
12
|
+
logDir: options.logDir || 'logs',
|
|
13
|
+
metricsInterval: options.metricsInterval || 60000,
|
|
14
|
+
// 1 minute
|
|
15
|
+
alertThresholds: {
|
|
16
|
+
errorRate: options.errorRateThreshold || 0.05,
|
|
17
|
+
// 5% error rate
|
|
18
|
+
responseTime: options.responseTimeThreshold || 5000,
|
|
19
|
+
// 5 seconds
|
|
20
|
+
memoryUsage: options.memoryUsageThreshold || 0.8,
|
|
21
|
+
// 80% memory usage
|
|
22
|
+
...options.alertThresholds
|
|
23
|
+
},
|
|
24
|
+
enableMetrics: options.enableMetrics !== false,
|
|
25
|
+
enableAlerts: options.enableAlerts !== false,
|
|
26
|
+
alertWebhook: options.alertWebhook,
|
|
27
|
+
...options
|
|
28
|
+
};
|
|
29
|
+
this.metrics = {
|
|
30
|
+
startTime: new Date(),
|
|
31
|
+
requests: {
|
|
32
|
+
total: 0,
|
|
33
|
+
successful: 0,
|
|
34
|
+
failed: 0
|
|
35
|
+
},
|
|
36
|
+
responseTimes: [],
|
|
37
|
+
errors: [],
|
|
38
|
+
memoryUsage: [],
|
|
39
|
+
customMetrics: new Map()
|
|
40
|
+
};
|
|
41
|
+
this.logLevels = {
|
|
42
|
+
debug: 0,
|
|
43
|
+
info: 1,
|
|
44
|
+
warn: 2,
|
|
45
|
+
error: 3,
|
|
46
|
+
fatal: 4
|
|
47
|
+
};
|
|
48
|
+
this.alerts = [];
|
|
49
|
+
this.isMonitoring = false;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Start monitoring
|
|
54
|
+
*/
|
|
55
|
+
async startMonitoring() {
|
|
56
|
+
if (this.isMonitoring) return;
|
|
57
|
+
this.isMonitoring = true;
|
|
58
|
+
await this.ensureLogDirectory();
|
|
59
|
+
|
|
60
|
+
// Start metrics collection
|
|
61
|
+
if (this.config.enableMetrics) {
|
|
62
|
+
this.metricsInterval = setInterval(() => {
|
|
63
|
+
this.collectSystemMetrics();
|
|
64
|
+
this.checkAlertThresholds();
|
|
65
|
+
}, this.config.metricsInterval);
|
|
66
|
+
}
|
|
67
|
+
this.log('info', 'Production monitoring started', {
|
|
68
|
+
config: this.config,
|
|
69
|
+
startTime: this.metrics.startTime
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Stop monitoring
|
|
75
|
+
*/
|
|
76
|
+
async stopMonitoring() {
|
|
77
|
+
if (!this.isMonitoring) return;
|
|
78
|
+
this.isMonitoring = false;
|
|
79
|
+
if (this.metricsInterval) {
|
|
80
|
+
clearInterval(this.metricsInterval);
|
|
81
|
+
}
|
|
82
|
+
await this.saveMetrics();
|
|
83
|
+
this.log('info', 'Production monitoring stopped');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Log a message with structured data
|
|
88
|
+
*/
|
|
89
|
+
async log(level, message, data = {}) {
|
|
90
|
+
if (this.logLevels[level] < this.logLevels[this.config.logLevel]) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const logEntry = {
|
|
94
|
+
timestamp: new Date(),
|
|
95
|
+
level,
|
|
96
|
+
message,
|
|
97
|
+
data,
|
|
98
|
+
process: {
|
|
99
|
+
pid: process.pid,
|
|
100
|
+
memory: process.memoryUsage(),
|
|
101
|
+
uptime: process.uptime()
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
// Console output for development
|
|
106
|
+
const consoleMethod = level === 'error' || level === 'fatal' ? 'error' : level === 'warn' ? 'warn' : 'log';
|
|
107
|
+
console[consoleMethod](`[${level.toUpperCase()}] ${message}`, data);
|
|
108
|
+
|
|
109
|
+
// File logging
|
|
110
|
+
try {
|
|
111
|
+
const logFile = join(this.config.logDir, `${new Date().toISOString().split('T')[0]}.log`);
|
|
112
|
+
const logLine = JSON.stringify(logEntry) + '\n';
|
|
113
|
+
await appendFile(logFile, logLine);
|
|
114
|
+
} catch (error) {
|
|
115
|
+
console.error('Failed to write log file:', error);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Track errors for metrics
|
|
119
|
+
if (level === 'error' || level === 'fatal') {
|
|
120
|
+
this.metrics.errors.push(logEntry);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Record a request
|
|
126
|
+
*/
|
|
127
|
+
recordRequest(success = true, responseTime = 0, metadata = {}) {
|
|
128
|
+
this.metrics.requests.total++;
|
|
129
|
+
if (success) {
|
|
130
|
+
this.metrics.requests.successful++;
|
|
131
|
+
} else {
|
|
132
|
+
this.metrics.requests.failed++;
|
|
133
|
+
}
|
|
134
|
+
if (responseTime > 0) {
|
|
135
|
+
this.metrics.responseTimes.push({
|
|
136
|
+
time: responseTime,
|
|
137
|
+
timestamp: new Date(),
|
|
138
|
+
success,
|
|
139
|
+
...metadata
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// Keep only last 1000 response times
|
|
143
|
+
if (this.metrics.responseTimes.length > 1000) {
|
|
144
|
+
this.metrics.responseTimes.shift();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Record a custom metric
|
|
151
|
+
*/
|
|
152
|
+
recordMetric(name, value, tags = {}) {
|
|
153
|
+
if (!this.metrics.customMetrics.has(name)) {
|
|
154
|
+
this.metrics.customMetrics.set(name, []);
|
|
155
|
+
}
|
|
156
|
+
const metrics = this.metrics.customMetrics.get(name);
|
|
157
|
+
metrics.push({
|
|
158
|
+
value,
|
|
159
|
+
timestamp: new Date(),
|
|
160
|
+
tags
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// Keep only last 100 values per metric
|
|
164
|
+
if (metrics.length > 100) {
|
|
165
|
+
metrics.shift();
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Collect system metrics
|
|
171
|
+
*/
|
|
172
|
+
collectSystemMetrics() {
|
|
173
|
+
const memUsage = process.memoryUsage();
|
|
174
|
+
const memoryData = {
|
|
175
|
+
rss: memUsage.rss,
|
|
176
|
+
heapUsed: memUsage.heapUsed,
|
|
177
|
+
heapTotal: memUsage.heapTotal,
|
|
178
|
+
external: memUsage.external,
|
|
179
|
+
timestamp: new Date()
|
|
180
|
+
};
|
|
181
|
+
this.metrics.memoryUsage.push(memoryData);
|
|
182
|
+
|
|
183
|
+
// Keep only last 100 memory readings
|
|
184
|
+
if (this.metrics.memoryUsage.length > 100) {
|
|
185
|
+
this.metrics.memoryUsage.shift();
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Record memory usage as custom metric
|
|
189
|
+
this.recordMetric('memory_usage_percent', memUsage.heapUsed / memUsage.heapTotal * 100);
|
|
190
|
+
this.recordMetric('memory_heap_used_mb', memUsage.heapUsed / 1024 / 1024);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Check alert thresholds and trigger alerts
|
|
195
|
+
*/
|
|
196
|
+
checkAlertThresholds() {
|
|
197
|
+
const now = Date.now();
|
|
198
|
+
|
|
199
|
+
// Check error rate
|
|
200
|
+
const recentRequests = this.getRecentRequests(5 * 60 * 1000); // Last 5 minutes
|
|
201
|
+
if (recentRequests.total > 10) {
|
|
202
|
+
// Only check if we have enough data
|
|
203
|
+
const errorRate = recentRequests.failed / recentRequests.total;
|
|
204
|
+
if (errorRate > this.config.alertThresholds.errorRate) {
|
|
205
|
+
this.triggerAlert('HIGH_ERROR_RATE', {
|
|
206
|
+
errorRate: errorRate * 100,
|
|
207
|
+
threshold: this.config.alertThresholds.errorRate * 100,
|
|
208
|
+
recentRequests
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Check response time
|
|
214
|
+
const avgResponseTime = this.getAverageResponseTime(5 * 60 * 1000);
|
|
215
|
+
if (avgResponseTime > this.config.alertThresholds.responseTime) {
|
|
216
|
+
this.triggerAlert('HIGH_RESPONSE_TIME', {
|
|
217
|
+
averageResponseTime: avgResponseTime,
|
|
218
|
+
threshold: this.config.alertThresholds.responseTime
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Check memory usage
|
|
223
|
+
const currentMemory = this.metrics.memoryUsage[this.metrics.memoryUsage.length - 1];
|
|
224
|
+
if (currentMemory) {
|
|
225
|
+
const memoryUsagePercent = currentMemory.heapUsed / currentMemory.heapTotal;
|
|
226
|
+
if (memoryUsagePercent > this.config.alertThresholds.memoryUsage) {
|
|
227
|
+
this.triggerAlert('HIGH_MEMORY_USAGE', {
|
|
228
|
+
memoryUsagePercent: memoryUsagePercent * 100,
|
|
229
|
+
threshold: this.config.alertThresholds.memoryUsage * 100,
|
|
230
|
+
heapUsed: currentMemory.heapUsed,
|
|
231
|
+
heapTotal: currentMemory.heapTotal
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Trigger an alert
|
|
239
|
+
*/
|
|
240
|
+
async triggerAlert(type, data) {
|
|
241
|
+
const alert = {
|
|
242
|
+
id: `alert_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
243
|
+
type,
|
|
244
|
+
timestamp: new Date(),
|
|
245
|
+
data,
|
|
246
|
+
acknowledged: false
|
|
247
|
+
};
|
|
248
|
+
this.alerts.push(alert);
|
|
249
|
+
|
|
250
|
+
// Keep only last 100 alerts
|
|
251
|
+
if (this.alerts.length > 100) {
|
|
252
|
+
this.alerts.shift();
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Log the alert
|
|
256
|
+
await this.log('error', `Alert triggered: ${type}`, data);
|
|
257
|
+
|
|
258
|
+
// Send webhook if configured
|
|
259
|
+
if (this.config.alertWebhook) {
|
|
260
|
+
try {
|
|
261
|
+
await this.sendWebhookAlert(alert);
|
|
262
|
+
} catch (error) {
|
|
263
|
+
await this.log('error', 'Failed to send alert webhook', {
|
|
264
|
+
error: error.message
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Send webhook alert
|
|
272
|
+
*/
|
|
273
|
+
async sendWebhookAlert(alert) {
|
|
274
|
+
if (!this.config.alertWebhook) return;
|
|
275
|
+
|
|
276
|
+
// In a real implementation, you'd use fetch or axios to send the webhook
|
|
277
|
+
// For now, just log it
|
|
278
|
+
await this.log('info', 'Alert webhook would be sent', {
|
|
279
|
+
webhook: this.config.alertWebhook,
|
|
280
|
+
alert
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Get recent requests within time window
|
|
286
|
+
*/
|
|
287
|
+
getRecentRequests(timeWindowMs) {
|
|
288
|
+
const cutoff = Date.now() - timeWindowMs;
|
|
289
|
+
const recent = this.metrics.responseTimes.filter(r => r.timestamp.getTime() > cutoff);
|
|
290
|
+
return {
|
|
291
|
+
total: recent.length,
|
|
292
|
+
successful: recent.filter(r => r.success).length,
|
|
293
|
+
failed: recent.filter(r => !r.success).length
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Get average response time within time window
|
|
299
|
+
*/
|
|
300
|
+
getAverageResponseTime(timeWindowMs) {
|
|
301
|
+
const cutoff = Date.now() - timeWindowMs;
|
|
302
|
+
const recent = this.metrics.responseTimes.filter(r => r.timestamp.getTime() > cutoff);
|
|
303
|
+
if (recent.length === 0) return 0;
|
|
304
|
+
const totalTime = recent.reduce((sum, r) => sum + r.time, 0);
|
|
305
|
+
return totalTime / recent.length;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Get current metrics summary
|
|
310
|
+
*/
|
|
311
|
+
getMetricsSummary() {
|
|
312
|
+
const uptime = Date.now() - this.metrics.startTime.getTime();
|
|
313
|
+
return {
|
|
314
|
+
uptime,
|
|
315
|
+
requests: {
|
|
316
|
+
...this.metrics.requests
|
|
317
|
+
},
|
|
318
|
+
errorRate: this.metrics.requests.total > 0 ? this.metrics.requests.failed / this.metrics.requests.total * 100 : 0,
|
|
319
|
+
averageResponseTime: this.getAverageResponseTime(60 * 60 * 1000),
|
|
320
|
+
// Last hour
|
|
321
|
+
memoryUsage: this.metrics.memoryUsage[this.metrics.memoryUsage.length - 1],
|
|
322
|
+
activeAlerts: this.alerts.filter(a => !a.acknowledged).length,
|
|
323
|
+
totalAlerts: this.alerts.length
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Get detailed metrics
|
|
329
|
+
*/
|
|
330
|
+
getDetailedMetrics() {
|
|
331
|
+
return {
|
|
332
|
+
...this.getMetricsSummary(),
|
|
333
|
+
responseTimes: this.metrics.responseTimes.slice(-100),
|
|
334
|
+
// Last 100
|
|
335
|
+
memoryUsage: this.metrics.memoryUsage.slice(-20),
|
|
336
|
+
// Last 20 readings
|
|
337
|
+
errors: this.metrics.errors.slice(-50),
|
|
338
|
+
// Last 50 errors
|
|
339
|
+
alerts: this.alerts.slice(-20),
|
|
340
|
+
// Last 20 alerts
|
|
341
|
+
customMetrics: Object.fromEntries(this.metrics.customMetrics)
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Save metrics to file
|
|
347
|
+
*/
|
|
348
|
+
async saveMetrics() {
|
|
349
|
+
try {
|
|
350
|
+
const metricsFile = join(this.config.logDir, 'metrics.json');
|
|
351
|
+
const metricsData = this.getDetailedMetrics();
|
|
352
|
+
await writeFile(metricsFile, JSON.stringify(metricsData, null, 2));
|
|
353
|
+
} catch (error) {
|
|
354
|
+
console.error('Failed to save metrics:', error);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Ensure log directory exists
|
|
360
|
+
*/
|
|
361
|
+
async ensureLogDirectory() {
|
|
362
|
+
try {
|
|
363
|
+
await mkdir(this.config.logDir, {
|
|
364
|
+
recursive: true
|
|
365
|
+
});
|
|
366
|
+
} catch (error) {
|
|
367
|
+
if (error.code !== 'EEXIST') {
|
|
368
|
+
throw error;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Acknowledge an alert
|
|
375
|
+
*/
|
|
376
|
+
acknowledgeAlert(alertId) {
|
|
377
|
+
const alert = this.alerts.find(a => a.id === alertId);
|
|
378
|
+
if (alert) {
|
|
379
|
+
alert.acknowledged = true;
|
|
380
|
+
alert.acknowledgedAt = new Date();
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Get unacknowledged alerts
|
|
386
|
+
*/
|
|
387
|
+
getUnacknowledgedAlerts() {
|
|
388
|
+
return this.alerts.filter(a => !a.acknowledged);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Testing Module
|
|
3
|
+
* Specialized module for API endpoint testing
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import fetch from 'node-fetch';
|
|
7
|
+
export class ApiTester {
|
|
8
|
+
constructor(config) {
|
|
9
|
+
this.config = config;
|
|
10
|
+
}
|
|
11
|
+
async runApiTests(environment) {
|
|
12
|
+
const results = {
|
|
13
|
+
passed: 0,
|
|
14
|
+
failed: 0,
|
|
15
|
+
endpoints: []
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// Define API endpoints to test
|
|
19
|
+
const endpoints = [{
|
|
20
|
+
name: 'Health Check',
|
|
21
|
+
url: `https://${environment}.api.example.com/health`,
|
|
22
|
+
method: 'GET'
|
|
23
|
+
}, {
|
|
24
|
+
name: 'API Status',
|
|
25
|
+
url: `https://${environment}.api.example.com/status`,
|
|
26
|
+
method: 'GET'
|
|
27
|
+
}, {
|
|
28
|
+
name: 'User Profile',
|
|
29
|
+
url: `https://${environment}.api.example.com/api/v1/user/profile`,
|
|
30
|
+
method: 'GET',
|
|
31
|
+
auth: true
|
|
32
|
+
}];
|
|
33
|
+
for (const endpoint of endpoints) {
|
|
34
|
+
try {
|
|
35
|
+
const startTime = Date.now();
|
|
36
|
+
const response = await this.testEndpoint(endpoint, environment);
|
|
37
|
+
const responseTime = Date.now() - startTime;
|
|
38
|
+
const result = {
|
|
39
|
+
name: endpoint.name,
|
|
40
|
+
url: endpoint.url,
|
|
41
|
+
method: endpoint.method,
|
|
42
|
+
status: response.status,
|
|
43
|
+
responseTime,
|
|
44
|
+
success: response.ok && responseTime < this.config.responseTimeThreshold
|
|
45
|
+
};
|
|
46
|
+
results.endpoints.push(result);
|
|
47
|
+
if (result.success) {
|
|
48
|
+
results.passed++;
|
|
49
|
+
} else {
|
|
50
|
+
results.failed++;
|
|
51
|
+
}
|
|
52
|
+
} catch (error) {
|
|
53
|
+
results.endpoints.push({
|
|
54
|
+
name: endpoint.name,
|
|
55
|
+
url: endpoint.url,
|
|
56
|
+
method: endpoint.method,
|
|
57
|
+
error: error.message,
|
|
58
|
+
success: false
|
|
59
|
+
});
|
|
60
|
+
results.failed++;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return results;
|
|
64
|
+
}
|
|
65
|
+
async testEndpoint(endpoint, environment) {
|
|
66
|
+
const options = {
|
|
67
|
+
method: endpoint.method,
|
|
68
|
+
timeout: this.config.timeout,
|
|
69
|
+
headers: {
|
|
70
|
+
'User-Agent': 'ProductionTester/2.0',
|
|
71
|
+
'Accept': 'application/json'
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
if (endpoint.auth) {
|
|
75
|
+
// Add authentication headers if needed
|
|
76
|
+
options.headers.Authorization = `Bearer ${process.env.TEST_API_TOKEN || 'test-token'}`;
|
|
77
|
+
}
|
|
78
|
+
return await fetch(endpoint.url, options);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication Testing Module
|
|
3
|
+
* Specialized module for authentication flow validation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import fetch from 'node-fetch';
|
|
7
|
+
export class AuthTester {
|
|
8
|
+
constructor(config) {
|
|
9
|
+
this.config = config;
|
|
10
|
+
}
|
|
11
|
+
async runAuthTests(environment) {
|
|
12
|
+
const results = {
|
|
13
|
+
passed: 0,
|
|
14
|
+
failed: 0,
|
|
15
|
+
flows: []
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// Authentication flows to test
|
|
19
|
+
const flows = [{
|
|
20
|
+
name: 'Login Flow',
|
|
21
|
+
test: () => this.testLoginFlow(environment)
|
|
22
|
+
}, {
|
|
23
|
+
name: 'Token Validation',
|
|
24
|
+
test: () => this.testTokenValidation(environment)
|
|
25
|
+
}, {
|
|
26
|
+
name: 'Logout Flow',
|
|
27
|
+
test: () => this.testLogoutFlow(environment)
|
|
28
|
+
}];
|
|
29
|
+
for (const flow of flows) {
|
|
30
|
+
try {
|
|
31
|
+
const startTime = Date.now();
|
|
32
|
+
const result = await flow.test();
|
|
33
|
+
const duration = Date.now() - startTime;
|
|
34
|
+
const flowResult = {
|
|
35
|
+
name: flow.name,
|
|
36
|
+
duration,
|
|
37
|
+
success: result.success && duration < this.config.authFlowThreshold,
|
|
38
|
+
details: result
|
|
39
|
+
};
|
|
40
|
+
results.flows.push(flowResult);
|
|
41
|
+
if (flowResult.success) {
|
|
42
|
+
results.passed++;
|
|
43
|
+
} else {
|
|
44
|
+
results.failed++;
|
|
45
|
+
}
|
|
46
|
+
} catch (error) {
|
|
47
|
+
results.flows.push({
|
|
48
|
+
name: flow.name,
|
|
49
|
+
error: error.message,
|
|
50
|
+
success: false
|
|
51
|
+
});
|
|
52
|
+
results.failed++;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return results;
|
|
56
|
+
}
|
|
57
|
+
async testLoginFlow(environment) {
|
|
58
|
+
try {
|
|
59
|
+
// Simulate login request
|
|
60
|
+
const response = await fetch(`https://${environment}.api.example.com/auth/login`, {
|
|
61
|
+
method: 'POST',
|
|
62
|
+
headers: {
|
|
63
|
+
'Content-Type': 'application/json',
|
|
64
|
+
'User-Agent': 'AuthTester/2.0'
|
|
65
|
+
},
|
|
66
|
+
body: JSON.stringify({
|
|
67
|
+
username: 'test@example.com',
|
|
68
|
+
password: 'test-password'
|
|
69
|
+
}),
|
|
70
|
+
timeout: this.config.timeout
|
|
71
|
+
});
|
|
72
|
+
return {
|
|
73
|
+
success: response.ok,
|
|
74
|
+
status: response.status,
|
|
75
|
+
hasToken: response.headers.has('authorization') || response.headers.has('x-auth-token')
|
|
76
|
+
};
|
|
77
|
+
} catch (error) {
|
|
78
|
+
return {
|
|
79
|
+
success: false,
|
|
80
|
+
error: error.message
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
async testTokenValidation(environment) {
|
|
85
|
+
try {
|
|
86
|
+
const token = process.env.TEST_AUTH_TOKEN || 'test-jwt-token';
|
|
87
|
+
const response = await fetch(`https://${environment}.api.example.com/auth/validate`, {
|
|
88
|
+
method: 'GET',
|
|
89
|
+
headers: {
|
|
90
|
+
'Authorization': `Bearer ${token}`,
|
|
91
|
+
'User-Agent': 'AuthTester/2.0'
|
|
92
|
+
},
|
|
93
|
+
timeout: this.config.timeout
|
|
94
|
+
});
|
|
95
|
+
return {
|
|
96
|
+
success: response.ok,
|
|
97
|
+
status: response.status,
|
|
98
|
+
valid: response.status === 200
|
|
99
|
+
};
|
|
100
|
+
} catch (error) {
|
|
101
|
+
return {
|
|
102
|
+
success: false,
|
|
103
|
+
error: error.message
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
async testLogoutFlow(environment) {
|
|
108
|
+
try {
|
|
109
|
+
const token = process.env.TEST_AUTH_TOKEN || 'test-jwt-token';
|
|
110
|
+
const response = await fetch(`https://${environment}.api.example.com/auth/logout`, {
|
|
111
|
+
method: 'POST',
|
|
112
|
+
headers: {
|
|
113
|
+
'Authorization': `Bearer ${token}`,
|
|
114
|
+
'User-Agent': 'AuthTester/2.0'
|
|
115
|
+
},
|
|
116
|
+
timeout: this.config.timeout
|
|
117
|
+
});
|
|
118
|
+
return {
|
|
119
|
+
success: response.ok,
|
|
120
|
+
status: response.status
|
|
121
|
+
};
|
|
122
|
+
} catch (error) {
|
|
123
|
+
return {
|
|
124
|
+
success: false,
|
|
125
|
+
error: error.message
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|