67 lines
1.9 KiB
JavaScript
67 lines
1.9 KiB
JavaScript
/**
|
|
* 操作审计日志中间件
|
|
*
|
|
* 记录每次 API 操作到日志文件(谁、什么时间、做了什么)
|
|
* 版权:国作登字-2026-A-00037559
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var fs = require('fs');
|
|
var path = require('path');
|
|
|
|
// 优先使用配置的目录,本地开发时回退到项目内目录
|
|
var AUDIT_LOG_DIR = process.env.AUDIT_LOG_DIR ||
|
|
path.join(__dirname, '../../logs/audit');
|
|
|
|
/**
|
|
* 审计日志中间件
|
|
* 拦截请求和响应,记录操作信息
|
|
*/
|
|
function auditLog(req, res, next) {
|
|
var startTime = Date.now();
|
|
|
|
var logEntry = {
|
|
timestamp: new Date().toISOString(),
|
|
devId: (req.user && req.user.devId) || req.headers['x-dev-id'] || 'anonymous',
|
|
method: req.method,
|
|
path: req.path,
|
|
ip: req.ip
|
|
};
|
|
|
|
// 对 POST/PATCH 请求记录请求体(排除敏感字段)
|
|
if ((req.method === 'POST' || req.method === 'PATCH') && req.body) {
|
|
var sanitizedBody = Object.assign({}, req.body);
|
|
delete sanitizedBody.password;
|
|
delete sanitizedBody.token;
|
|
delete sanitizedBody.confirmToken;
|
|
logEntry.body = sanitizedBody;
|
|
}
|
|
|
|
// 拦截响应完成
|
|
var originalSend = res.send;
|
|
res.send = function(body) {
|
|
logEntry.responseTime = Date.now() - startTime;
|
|
logEntry.statusCode = res.statusCode;
|
|
|
|
// 异步写入日志,不阻塞响应
|
|
fs.mkdir(AUDIT_LOG_DIR, { recursive: true }, function(mkdirErr) {
|
|
if (mkdirErr) {
|
|
console.error('审计日志目录创建失败:', mkdirErr.message);
|
|
return;
|
|
}
|
|
var today = new Date().toISOString().split('T')[0];
|
|
var logFile = path.join(AUDIT_LOG_DIR, 'audit-' + today + '.jsonl');
|
|
fs.appendFile(logFile, JSON.stringify(logEntry) + '\n', function(writeErr) {
|
|
if (writeErr) console.error('审计日志写入失败:', writeErr.message);
|
|
});
|
|
});
|
|
|
|
return originalSend.call(this, body);
|
|
};
|
|
|
|
next();
|
|
}
|
|
|
|
module.exports = { auditLog: auditLog };
|