const fs = require('fs'); const path = require('path'); class ApiDebugLogger { constructor() { // Use mounted volume directory that's accessible from host this.logFile = '/app/debug_logs/api_debug.log'; this.enabled = process.env.NODE_ENV === 'development' || process.env.API_DEBUG === 'true'; this.fileLoggingEnabled = false; // Debug logging setup if (this.enabled) { console.log(`🐛 ApiDebugLogger: Enabled (NODE_ENV=${process.env.NODE_ENV}, API_DEBUG=${process.env.API_DEBUG})`); console.log(`🐛 ApiDebugLogger: Log file path: ${this.logFile}`); // Ensure the debug_logs directory exists try { const fs = require('fs'); const debugDir = '/app/debug_logs'; if (!fs.existsSync(debugDir)) { fs.mkdirSync(debugDir, { recursive: true }); console.log(`🐛 ApiDebugLogger: Created debug logs directory at ${debugDir}`); } fs.writeFileSync(this.logFile, `# API Debug Log Started at ${new Date().toISOString()}\n`); this.fileLoggingEnabled = true; console.log(`🐛 ApiDebugLogger: File logging enabled at ${this.logFile} (mounted to host: ./debug_logs/)`); } catch (error) { console.warn(`⚠️ ApiDebugLogger: File logging disabled - using console only. Error: ${error.message}`); this.fileLoggingEnabled = false; } } else { console.log(`🐛 ApiDebugLogger: Disabled (NODE_ENV=${process.env.NODE_ENV}, API_DEBUG=${process.env.API_DEBUG})`); } } log(method, url, statusCode, requestBody = {}, responseBody = {}, headers = {}) { if (!this.enabled) return; const timestamp = new Date().toISOString(); const sanitizedRequest = this.sanitizeData(requestBody); const sanitizedResponse = this.sanitizeData(responseBody); const sanitizedHeaders = this.sanitizeHeaders(headers); const logEntry = [ `[${timestamp}]`, method.toUpperCase(), url, `-`, `STATUS:${statusCode}`, `-`, `REQ:${JSON.stringify(sanitizedRequest)}`, `-`, `RES:${JSON.stringify(sanitizedResponse)}`, `-`, `HEADERS:${JSON.stringify(sanitizedHeaders)}` ].join(' '); // Always log to console console.log(`🐛 API Log: ${method.toUpperCase()} ${url} - ${statusCode}`); // Try to log to file if enabled if (this.fileLoggingEnabled) { try { fs.appendFileSync(this.logFile, logEntry + '\n'); } catch (error) { console.warn(`⚠️ File logging failed, disabling: ${error.message}`); this.fileLoggingEnabled = false; } } } sanitizeData(data) { if (!data || typeof data !== 'object') return data; const sanitized = { ...data }; // Remove sensitive fields const sensitiveFields = ['password', 'token', 'secret', 'key', 'auth', 'authorization']; sensitiveFields.forEach(field => { if (sanitized[field]) { sanitized[field] = '***REDACTED***'; } }); return sanitized; } sanitizeHeaders(headers) { if (!headers || typeof headers !== 'object') return {}; const sanitized = { ...headers }; // Remove sensitive headers const sensitiveHeaders = ['authorization', 'cookie', 'x-api-key', 'x-auth-token']; sensitiveHeaders.forEach(header => { if (sanitized[header]) { sanitized[header] = '***REDACTED***'; } }); return sanitized; } logRequest(req) { if (!this.enabled) return; const timestamp = new Date().toISOString(); const logEntry = `[${timestamp}] INCOMING ${req.method} ${req.url} - BODY:${JSON.stringify(this.sanitizeData(req.body))} - HEADERS:${JSON.stringify(this.sanitizeHeaders(req.headers))}`; // Always log to console console.log(`🐛 API Request: ${req.method} ${req.url}`); // Try to log to file if enabled if (this.fileLoggingEnabled) { try { fs.appendFileSync(this.logFile, logEntry + '\n'); } catch (error) { console.warn(`⚠️ File logging failed, disabling: ${error.message}`); this.fileLoggingEnabled = false; } } } logResponse(req, res, responseBody) { if (!this.enabled) return; this.log( req.method, req.url, res.statusCode, req.body, responseBody, { 'content-type': res.getHeader('content-type'), 'user-agent': req.headers['user-agent'] } ); } clear() { try { fs.writeFileSync(this.logFile, '# API Debug Log Cleared at ' + new Date().toISOString() + '\n'); console.log('API debug log cleared'); } catch (error) { console.error('Failed to clear API debug log:', error); } } } // Middleware function for Express function apiDebugMiddleware(req, res, next) { const logger = new ApiDebugLogger(); // Log incoming request logger.logRequest(req); // Override res.json to capture response const originalJson = res.json; res.json = function(data) { logger.logResponse(req, res, data); return originalJson.call(this, data); }; next(); } module.exports = { ApiDebugLogger, apiDebugMiddleware };