const AuditLog = require('../models/AuditLog'); /** * Middleware to log data changes to audit_logs table * * Usage: * const { captureAuditLog } = require('../middleware/auditLogger'); * * router.put('/endpoint/:id', async (req, res) => { * const oldData = await Model.getById(id); // Fetch current data before update * * // Perform update * const newData = await Model.update(id, req.body); * * // Log the change * await captureAuditLog('table_name', id, 'UPDATE', oldData, newData); * * res.json({ success: true, data: newData }); * }); */ /** * Capture audit log for a data change * @param {string} tableName - Name of the table being modified * @param {string|number} recordId - ID of the record being modified * @param {string} action - Action type: CREATE, UPDATE, DELETE * @param {Object} oldValues - Previous values (null for CREATE) * @param {Object} newValues - New values (null for DELETE) * @returns {Promise} Created audit log entry */ async function captureAuditLog(tableName, recordId, action, oldValues = null, newValues = null) { try { // Determine which fields changed const changedFields = []; if (action === 'UPDATE' && oldValues && newValues) { // Compare old and new values to find changed fields for (const key in newValues) { if (oldValues.hasOwnProperty(key)) { // Handle different types of values const oldVal = oldValues[key]; const newVal = newValues[key]; // Deep comparison for objects/arrays, simple comparison otherwise if (JSON.stringify(oldVal) !== JSON.stringify(newVal)) { changedFields.push(key); } } } } // Only log if there are actual changes (for UPDATE) or always for CREATE/DELETE if (action !== 'UPDATE' || changedFields.length > 0) { return await AuditLog.create( tableName, recordId, action, oldValues, newValues, changedFields ); } return null; // No changes to log } catch (error) { // Log error but don't fail the main operation console.error('Error capturing audit log:', error); return null; } } /** * Helper to extract only relevant fields for logging * (excludes timestamps and internal fields) * @param {Object} data - Data object * @param {string[]} excludeFields - Fields to exclude (default: created_at, updated_at) * @returns {Object} Filtered data object */ function filterAuditFields(data, excludeFields = ['created_at', 'updated_at']) { if (!data) return null; const filtered = { ...data }; excludeFields.forEach(field => { delete filtered[field]; }); return filtered; } module.exports = { captureAuditLog, filterAuditFields };