UNPKG

marketing-post-generator-mcp

Version:

A powerful MCP server for AI-powered marketing blog post generation with Claude integration

181 lines 6.66 kB
export class LoggingMiddleware { logger; config; constructor(logger, config = {}) { this.logger = logger; this.config = { skipPaths: ['/health', '/metrics', '/favicon.ico'], logRequestBody: true, logResponseBody: false, logHeaders: true, maxBodyLength: 10000, sensitiveHeaders: ['authorization', 'cookie', 'x-api-key', 'x-auth-token'], sensitiveBodyFields: ['password', 'token', 'secret', 'apiKey'], ...config, }; } createMiddleware() { return (req, res, next) => { const startTime = Date.now(); const requestId = req.get('X-Request-ID') || this.generateRequestId(); // Add request ID to request object for downstream usage req.requestId = requestId; // Skip logging for specified paths if (this.shouldSkipLogging(req.path)) { return next(); } // Log request this.logRequest(req, requestId); // Capture response const originalSend = res.send; const originalJson = res.json; let responseBody; let responseCaptured = false; res.send = function (body) { if (!responseCaptured) { responseBody = body; responseCaptured = true; } return originalSend.call(this, body); }; res.json = function (body) { if (!responseCaptured) { responseBody = body; responseCaptured = true; } return originalJson.call(this, body); }; // Log response when request finishes res.on('finish', () => { const responseTime = Date.now() - startTime; this.logResponse(res, responseTime, requestId, responseBody); }); next(); }; } shouldSkipLogging(path) { return this.config.skipPaths?.some((skipPath) => path.startsWith(skipPath)) || false; } generateRequestId() { return `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } logRequest(req, requestId) { const requestData = { requestId, method: req.method, url: req.url, ip: this.getClientIP(req), timestamp: new Date().toISOString(), }; const userAgent = req.get('User-Agent'); if (userAgent) { requestData.userAgent = userAgent; } const contentLength = req.get('Content-Length'); if (contentLength) { requestData.contentLength = parseInt(contentLength); } // Add headers if configured if (this.config.logHeaders) { requestData.headers = this.sanitizeHeaders(req.headers); } // Add body if configured if (this.config.logRequestBody && req.body) { requestData.body = this.sanitizeBody(req.body); } this.logger.info('HTTP Request', requestData); } logResponse(res, responseTime, requestId, responseBody) { const responseData = { requestId, statusCode: res.statusCode, responseTime, timestamp: new Date().toISOString(), }; const contentLength = res.get('Content-Length'); if (contentLength) { responseData.contentLength = parseInt(contentLength); } // Add headers if configured if (this.config.logHeaders) { responseData.headers = this.sanitizeHeaders(res.getHeaders()); } // Add body if configured if (this.config.logResponseBody && responseBody) { responseData.body = this.sanitizeBody(responseBody); } // Choose log level based on status code const logLevel = this.getLogLevel(res.statusCode); this.logger[logLevel]('HTTP Response', responseData); } getClientIP(req) { return (req.get('X-Forwarded-For')?.split(',')[0] || req.get('X-Real-IP') || req.connection.remoteAddress || req.socket.remoteAddress || 'unknown'); } sanitizeHeaders(headers) { const sanitized = {}; for (const [key, value] of Object.entries(headers)) { const lowerKey = key.toLowerCase(); if (this.config.sensitiveHeaders?.includes(lowerKey)) { sanitized[key] = '[REDACTED]'; } else { sanitized[key] = Array.isArray(value) ? value.join(', ') : String(value); } } return sanitized; } sanitizeBody(body) { if (!body) return body; // Handle string bodies if (typeof body === 'string') { if (body.length > (this.config.maxBodyLength || 10000)) { // Ensure we don't break multi-byte characters const maxLength = this.config.maxBodyLength || 10000; const truncated = body.slice(0, maxLength); // Remove any incomplete multi-byte sequence at the end const cleanTruncated = truncated.replace(/[\uD800-\uDBFF]$/, ''); return cleanTruncated + '... [TRUNCATED]'; } return body; } // Handle object bodies if (typeof body === 'object') { const sanitized = { ...body }; // Remove sensitive fields this.config.sensitiveBodyFields?.forEach((field) => { if (sanitized[field]) { sanitized[field] = '[REDACTED]'; } }); // Truncate large objects const stringified = JSON.stringify(sanitized); if (stringified.length > (this.config.maxBodyLength || 10000)) { return stringified.substring(0, this.config.maxBodyLength) + '... [TRUNCATED]'; } return sanitized; } return body; } getLogLevel(statusCode) { if (statusCode >= 500) { return 'error'; } else if (statusCode >= 400) { return 'warn'; } else { return 'info'; } } } // Helper function to create logging middleware export function createLoggingMiddleware(logger, config) { const middleware = new LoggingMiddleware(logger, config); return middleware.createMiddleware(); } //# sourceMappingURL=LoggingMiddleware.js.map