Files
telegram-management-system/marketing-agent/services/compliance-guard/src/routes/audit.js
你的用户名 237c7802e5
Some checks failed
Deploy / deploy (push) Has been cancelled
Initial commit: Telegram Management System
Full-stack web application for Telegram management
- Frontend: Vue 3 + Vben Admin
- Backend: NestJS
- Features: User management, group broadcast, statistics

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-04 15:37:50 +08:00

364 lines
9.4 KiB
JavaScript

import express from 'express';
import { AuditLog } from '../models/AuditLog.js';
import { AuditService } from '../services/auditService.js';
import { auth } from '../utils/auth.js';
import { metrics } from '../utils/metrics.js';
import { logger } from '../utils/logger.js';
const router = express.Router();
const auditService = new AuditService();
/**
* Search audit logs
*/
router.get('/audit-logs', auth(), async (req, res) => {
try {
const {
accountId,
userId,
category,
action,
resource,
startDate,
endDate,
page = 1,
limit = 50
} = req.query;
const criteria = {
accountId: accountId || req.user.accountId,
userId,
category,
action,
resource,
startDate: startDate ? new Date(startDate) : undefined,
endDate: endDate ? new Date(endDate) : undefined,
page: parseInt(page),
limit: parseInt(limit)
};
const logs = await auditService.searchLogs(criteria);
res.json({
success: true,
data: logs,
pagination: {
page: parseInt(page),
limit: parseInt(limit),
hasMore: logs.length === parseInt(limit)
}
});
metrics.recordApiCall('audit.search', 'success');
} catch (error) {
logger.error('Failed to search audit logs:', error);
metrics.recordApiCall('audit.search', 'error');
res.status(500).json({
success: false,
error: 'Failed to search audit logs'
});
}
});
/**
* Get audit trail for a resource
*/
router.get('/audit-trail/:resourceType/:resourceId', auth(), async (req, res) => {
try {
const { resourceType, resourceId } = req.params;
const trail = await auditService.getAuditTrail(resourceId, resourceType);
res.json({
success: true,
data: trail,
count: trail.length
});
metrics.recordApiCall('audit.trail', 'success');
} catch (error) {
logger.error('Failed to get audit trail:', error);
metrics.recordApiCall('audit.trail', 'error');
res.status(500).json({
success: false,
error: 'Failed to get audit trail'
});
}
});
/**
* Generate compliance report
*/
router.post('/audit-reports/compliance', auth(), async (req, res) => {
try {
const { startDate, endDate } = req.body;
if (!startDate || !endDate) {
return res.status(400).json({
success: false,
error: 'Start date and end date are required'
});
}
const report = await auditService.generateComplianceReport(
req.user.accountId,
new Date(startDate),
new Date(endDate)
);
res.json({
success: true,
data: report
});
metrics.recordApiCall('audit.compliance_report', 'success');
} catch (error) {
logger.error('Failed to generate compliance report:', error);
metrics.recordApiCall('audit.compliance_report', 'error');
res.status(500).json({
success: false,
error: 'Failed to generate compliance report'
});
}
});
/**
* Detect anomalies
*/
router.get('/audit-anomalies', auth(), async (req, res) => {
try {
const { window = 3600000 } = req.query; // Default 1 hour
const anomalies = await auditService.detectAnomalies(
req.user.accountId,
parseInt(window)
);
res.json({
success: true,
data: anomalies,
count: anomalies.length,
window: parseInt(window)
});
metrics.recordApiCall('audit.anomalies', 'success');
} catch (error) {
logger.error('Failed to detect anomalies:', error);
metrics.recordApiCall('audit.anomalies', 'error');
res.status(500).json({
success: false,
error: 'Failed to detect anomalies'
});
}
});
/**
* Get activity summary
*/
router.get('/audit-summary', auth(), async (req, res) => {
try {
const { days = 7 } = req.query;
const summary = await AuditLog.getActivitySummary(
req.user.accountId,
parseInt(days)
);
res.json({
success: true,
data: summary,
period: {
days: parseInt(days),
startDate: new Date(Date.now() - parseInt(days) * 24 * 60 * 60 * 1000),
endDate: new Date()
}
});
metrics.recordApiCall('audit.summary', 'success');
} catch (error) {
logger.error('Failed to get activity summary:', error);
metrics.recordApiCall('audit.summary', 'error');
res.status(500).json({
success: false,
error: 'Failed to get activity summary'
});
}
});
/**
* Get audit log by ID
*/
router.get('/audit-logs/:auditId', auth(), async (req, res) => {
try {
const { auditId } = req.params;
const log = await AuditLog.findOne({ auditId });
if (!log) {
return res.status(404).json({
success: false,
error: 'Audit log not found'
});
}
// Check access permissions
if (log.accountId !== req.user.accountId) {
return res.status(403).json({
success: false,
error: 'Access denied'
});
}
res.json({
success: true,
data: log
});
metrics.recordApiCall('audit.get', 'success');
} catch (error) {
logger.error('Failed to get audit log:', error);
metrics.recordApiCall('audit.get', 'error');
res.status(500).json({
success: false,
error: 'Failed to get audit log'
});
}
});
/**
* Export audit logs
*/
router.post('/audit-export', auth(), async (req, res) => {
try {
const {
format = 'json',
startDate,
endDate,
category,
compress = false
} = req.body;
const criteria = {
accountId: req.user.accountId,
startDate: startDate ? new Date(startDate) : new Date(Date.now() - 30 * 24 * 60 * 60 * 1000),
endDate: endDate ? new Date(endDate) : new Date(),
category
};
const logs = await auditService.searchLogs(criteria);
let exportData;
let contentType;
let filename = `audit_export_${Date.now()}`;
switch (format) {
case 'csv':
exportData = convertToCSV(logs);
contentType = 'text/csv';
filename += '.csv';
break;
case 'json':
default:
exportData = JSON.stringify(logs, null, 2);
contentType = 'application/json';
filename += '.json';
break;
}
if (compress) {
// In production, implement compression
filename += '.gz';
contentType = 'application/gzip';
}
res.setHeader('Content-Type', contentType);
res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
res.send(exportData);
metrics.recordApiCall('audit.export', 'success');
} catch (error) {
logger.error('Failed to export audit logs:', error);
metrics.recordApiCall('audit.export', 'error');
res.status(500).json({
success: false,
error: 'Failed to export audit logs'
});
}
});
/**
* Create manual audit log entry
*/
router.post('/audit-logs', auth(), async (req, res) => {
try {
const eventData = {
...req.body,
accountId: req.user.accountId,
actor: {
type: 'user',
id: req.user.id,
ip: req.ip,
userAgent: req.get('user-agent')
}
};
const auditLog = await auditService.logEvent(eventData);
res.status(201).json({
success: true,
data: auditLog
});
metrics.recordApiCall('audit.create', 'success');
} catch (error) {
logger.error('Failed to create audit log:', error);
metrics.recordApiCall('audit.create', 'error');
res.status(500).json({
success: false,
error: 'Failed to create audit log'
});
}
});
/**
* Helper function to convert to CSV
*/
function convertToCSV(logs) {
if (logs.length === 0) return '';
const headers = [
'Timestamp',
'Audit ID',
'Action',
'Category',
'Resource',
'Resource ID',
'Actor Type',
'Actor ID',
'Result',
'IP Address'
];
const rows = logs.map(log => [
log.timestamp,
log.auditId,
log.action,
log.category,
log.resource || '',
log.resourceId || '',
log.actor.type,
log.actor.id,
log.result.status,
log.actor.ip || ''
]);
const csvContent = [
headers.join(','),
...rows.map(row => row.map(cell =>
typeof cell === 'string' && cell.includes(',') ? `"${cell}"` : cell
).join(','))
].join('\n');
return csvContent;
}
export default router;