UNPKG

fortify2-js

Version:

MOST POWERFUL JavaScript Security Library! Military-grade cryptography + 19 enhanced object methods + quantum-resistant algorithms + perfect TypeScript support. More powerful than Lodash with built-in security.

511 lines (505 loc) 17.9 kB
'use strict'; var events = require('events'); var fs = require('fs'); var path = require('path'); function _interopNamespaceDefault(e) { var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n.default = e; return Object.freeze(n); } var fs__namespace = /*#__PURE__*/_interopNamespaceDefault(fs); var path__namespace = /*#__PURE__*/_interopNamespaceDefault(path); /** * Server Maintenance Plugin * * Automatically detects issues and performs server maintenance in the background * Features: * - Error detection and analysis * - Memory leak detection * - Log cleanup and rotation * - Performance degradation detection * - Automatic health checks * - Resource optimization */ class ServerMaintenancePlugin extends events.EventEmitter { constructor(config = {}) { super(); this.issues = []; this.healthHistory = []; this.startTime = Date.now(); this.errorCount = 0; this.requestCount = 0; this.responseTimes = []; this.config = { enabled: true, checkInterval: 30000, // 30 seconds errorThreshold: 5, // 5% error rate memoryThreshold: 80, // 80% memory usage responseTimeThreshold: 1000, // 1 second logRetentionDays: 7, maxLogFileSize: 10 * 1024 * 1024, // 10MB autoCleanup: true, autoRestart: false, onIssueDetected: () => { }, onMaintenanceComplete: () => { }, ...config, }; } /** * Initialize the plugin */ initialize(app, logger) { if (!this.config.enabled) return; this.app = app; this.logger = logger; // Install monitoring middleware this.installMonitoringMiddleware(); // Start background maintenance this.startBackgroundMaintenance(); // Setup error handlers this.setupErrorHandlers(); this.logger.info("plugins", "Server Maintenance Plugin initialized"); } /** * Install middleware to monitor requests */ installMonitoringMiddleware() { this.app.use((req, res, next) => { const startTime = Date.now(); this.requestCount++; // Override res.end to capture metrics const originalEnd = res.end.bind(res); res.end = (...args) => { const responseTime = Date.now() - startTime; this.responseTimes.push(responseTime); // Keep only last 1000 response times if (this.responseTimes.length > 1000) { this.responseTimes = this.responseTimes.slice(-1000); } if (res.statusCode >= 400) { this.errorCount++; } return originalEnd(...args); }; next(); }); } /** * Setup error handlers */ setupErrorHandlers() { // Uncaught exception handler process.on("uncaughtException", (error) => { this.reportIssue({ type: "error", category: "errors", message: `Uncaught exception: ${error.message}`, severity: 9, timestamp: new Date(), details: { stack: error.stack }, resolved: false, }); }); // Unhandled rejection handler process.on("unhandledRejection", (reason, promise) => { this.reportIssue({ type: "error", category: "errors", message: `Unhandled rejection: ${reason}`, severity: 8, timestamp: new Date(), details: { promise }, resolved: false, }); }); } /** * Start background maintenance checks */ startBackgroundMaintenance() { this.maintenanceTimer = setInterval(() => { this.performMaintenanceCheck(); }, this.config.checkInterval); } /** * Perform comprehensive maintenance check */ performMaintenanceCheck() { const metrics = this.collectHealthMetrics(); this.healthHistory.push(metrics); // Keep only last 100 health records if (this.healthHistory.length > 100) { this.healthHistory = this.healthHistory.slice(-100); } // Check for issues this.checkMemoryUsage(metrics); this.checkErrorRate(metrics); this.checkResponseTime(metrics); this.checkLogFiles(); // Perform automatic cleanup if enabled if (this.config.autoCleanup) { this.performAutomaticCleanup(); } this.emit("health_check", metrics); } /** * Collect current health metrics */ collectHealthMetrics() { const memUsage = process.memoryUsage(); const totalMemory = memUsage.heapTotal + memUsage.external; const usedMemory = memUsage.heapUsed; const avgResponseTime = this.responseTimes.length > 0 ? this.responseTimes.reduce((a, b) => a + b, 0) / this.responseTimes.length : 0; const p95ResponseTime = this.responseTimes.length > 0 ? this.responseTimes.sort((a, b) => a - b)[Math.floor(this.responseTimes.length * 0.95)] : 0; const errorRate = this.requestCount > 0 ? (this.errorCount / this.requestCount) * 100 : 0; return { memoryUsage: { used: usedMemory, total: totalMemory, percentage: (usedMemory / totalMemory) * 100, trend: this.calculateMemoryTrend(), }, cpuUsage: process.cpuUsage().user / 1000000, // Convert to seconds errorRate, responseTime: { average: avgResponseTime, p95: p95ResponseTime, trend: this.calculateResponseTimeTrend(), }, activeConnections: 0, // Would need server reference to get actual count uptime: Date.now() - this.startTime, }; } /** * Calculate memory usage trend */ calculateMemoryTrend() { if (this.healthHistory.length < 3) return "stable"; const recent = this.healthHistory.slice(-3); const trend = recent[2].memoryUsage.percentage - recent[0].memoryUsage.percentage; if (trend > 5) return "increasing"; if (trend < -5) return "decreasing"; return "stable"; } /** * Calculate response time trend */ calculateResponseTimeTrend() { if (this.healthHistory.length < 3) return "stable"; const recent = this.healthHistory.slice(-3); const trend = recent[2].responseTime.average - recent[0].responseTime.average; if (trend > 100) return "degrading"; if (trend < -100) return "improving"; return "stable"; } /** * Check memory usage */ checkMemoryUsage(metrics) { if (metrics.memoryUsage.percentage > this.config.memoryThreshold) { this.reportIssue({ type: "warning", category: "memory", message: `High memory usage: ${metrics.memoryUsage.percentage.toFixed(1)}%`, severity: 7, timestamp: new Date(), details: metrics.memoryUsage, resolved: false, }); } if (metrics.memoryUsage.trend === "increasing" && metrics.memoryUsage.percentage > 60) { this.reportIssue({ type: "warning", category: "memory", message: "Memory usage is consistently increasing - possible memory leak", severity: 8, timestamp: new Date(), details: metrics.memoryUsage, resolved: false, }); // Trigger automatic memory optimization this.performMemoryOptimization(); } } /** * Check error rate */ checkErrorRate(metrics) { if (metrics.errorRate > this.config.errorThreshold) { this.reportIssue({ type: "error", category: "errors", message: `High error rate: ${metrics.errorRate.toFixed(1)}%`, severity: 8, timestamp: new Date(), details: { errorRate: metrics.errorRate, errorCount: this.errorCount, requestCount: this.requestCount, }, resolved: false, }); } } /** * Check response time */ checkResponseTime(metrics) { if (metrics.responseTime.average > this.config.responseTimeThreshold) { this.reportIssue({ type: "warning", category: "performance", message: `Slow response time: ${metrics.responseTime.average.toFixed(0)}ms average`, severity: 6, timestamp: new Date(), details: metrics.responseTime, resolved: false, }); } if (metrics.responseTime.trend === "degrading") { this.reportIssue({ type: "warning", category: "performance", message: "Response times are degrading", severity: 7, timestamp: new Date(), details: metrics.responseTime, resolved: false, }); } } /** * Check log files */ checkLogFiles() { try { const logsDir = path__namespace.join(process.cwd(), "logs"); if (!fs__namespace.existsSync(logsDir)) return; const files = fs__namespace.readdirSync(logsDir); for (const file of files) { const filePath = path__namespace.join(logsDir, file); const stats = fs__namespace.statSync(filePath); // Check file size if (stats.size > this.config.maxLogFileSize) { this.reportIssue({ type: "warning", category: "logs", message: `Large log file: ${file} (${(stats.size / 1024 / 1024).toFixed(1)}MB)`, severity: 4, timestamp: new Date(), details: { file, size: stats.size }, resolved: false, }); } // Check file age const ageInDays = (Date.now() - stats.mtime.getTime()) / (1000 * 60 * 60 * 24); if (ageInDays > this.config.logRetentionDays) { this.reportIssue({ type: "info", category: "logs", message: `Old log file: ${file} (${ageInDays.toFixed(1)} days old)`, severity: 2, timestamp: new Date(), details: { file, ageInDays }, resolved: false, }); } } } catch (error) { // Ignore log check errors } } /** * Perform memory optimization */ performMemoryOptimization() { const actions = []; // Force garbage collection if available if (global.gc) { const beforeMemory = process.memoryUsage().heapUsed; global.gc(); const afterMemory = process.memoryUsage().heapUsed; const freed = beforeMemory - afterMemory; if (freed > 0) { actions.push(`Freed ${(freed / 1024 / 1024).toFixed(2)}MB via garbage collection`); } } // Clear internal caches if memory usage is critical const currentMemory = process.memoryUsage(); const memoryPercentage = (currentMemory.heapUsed / currentMemory.heapTotal) * 100; if (memoryPercentage > 85) { // Clear response time history to free memory if (this.responseTimes.length > 100) { this.responseTimes = this.responseTimes.slice(-100); actions.push("Cleared old response time history"); } // Clear old health history if (this.healthHistory.length > 50) { this.healthHistory = this.healthHistory.slice(-50); actions.push("Cleared old health history"); } // Clear resolved issues older than 1 hour const oneHourAgo = Date.now() - 60 * 60 * 1000; const oldIssuesCount = this.issues.length; this.issues = this.issues.filter((issue) => !issue.resolved || issue.timestamp.getTime() > oneHourAgo); if (this.issues.length < oldIssuesCount) { actions.push(`Cleared ${oldIssuesCount - this.issues.length} old resolved issues`); } } if (actions.length > 0) { this.logger.info("plugins", `Memory optimization completed: ${actions.join(", ")}`); this.emit("memory_optimization", actions); } } /** * Perform automatic cleanup */ performAutomaticCleanup() { const actions = []; // Clean up old issues const oldIssues = this.issues.filter((issue) => Date.now() - issue.timestamp.getTime() > 24 * 60 * 60 * 1000 && issue.resolved); if (oldIssues.length > 0) { this.issues = this.issues.filter((issue) => !oldIssues.includes(issue)); actions.push(`Cleaned up ${oldIssues.length} old issues`); } // Force garbage collection if memory usage is high const latestMetrics = this.healthHistory[this.healthHistory.length - 1]; if (latestMetrics && latestMetrics.memoryUsage.percentage > 70) { if (global.gc) { global.gc(); actions.push("Forced garbage collection"); } } // Clean up old log files this.cleanupOldLogs(actions); if (actions.length > 0) { this.logger.info("plugins", `Automatic cleanup completed: ${actions.join(", ")}`); this.config.onMaintenanceComplete(actions); this.emit("maintenance_complete", actions); } } /** * Clean up old log files */ cleanupOldLogs(actions) { try { const logsDir = path__namespace.join(process.cwd(), "logs"); if (!fs__namespace.existsSync(logsDir)) return; const files = fs__namespace.readdirSync(logsDir); let cleanedFiles = 0; for (const file of files) { const filePath = path__namespace.join(logsDir, file); const stats = fs__namespace.statSync(filePath); const ageInDays = (Date.now() - stats.mtime.getTime()) / (1000 * 60 * 60 * 24); if (ageInDays > this.config.logRetentionDays) { fs__namespace.unlinkSync(filePath); cleanedFiles++; } } if (cleanedFiles > 0) { actions.push(`Cleaned up ${cleanedFiles} old log files`); } } catch (error) { // Ignore cleanup errors } } /** * Report a maintenance issue */ reportIssue(issue) { this.issues.push(issue); // Keep only last 1000 issues if (this.issues.length > 1000) { this.issues = this.issues.slice(-1000); } this.logger.warn("plugins", `Maintenance issue detected: ${issue.message}`); this.config.onIssueDetected(issue); this.emit("issue_detected", issue); // Check if critical restart is needed if (this.config.autoRestart && issue.severity >= 9) { this.logger.error("plugins", "Critical issue detected - restart may be required"); this.emit("critical_issue", issue); } } /** * Get current health metrics */ getHealthMetrics() { return this.healthHistory[this.healthHistory.length - 1] || null; } /** * Get all issues */ getIssues() { return [...this.issues]; } /** * Get unresolved issues */ getUnresolvedIssues() { return this.issues.filter((issue) => !issue.resolved); } /** * Resolve an issue */ resolveIssue(issueIndex) { if (this.issues[issueIndex]) { this.issues[issueIndex].resolved = true; this.emit("issue_resolved", this.issues[issueIndex]); } } /** * Force maintenance check */ forceMaintenanceCheck() { this.performMaintenanceCheck(); } /** * Destroy the plugin */ destroy() { if (this.maintenanceTimer) { clearInterval(this.maintenanceTimer); } this.issues = []; this.healthHistory = []; this.removeAllListeners(); } } exports.ServerMaintenancePlugin = ServerMaintenancePlugin; //# sourceMappingURL=server-maintenance-plugin.js.map