Initial commit: Telegram Management System
Some checks failed
Deploy / deploy (push) Has been cancelled
Some checks failed
Deploy / deploy (push) Has been cancelled
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>
This commit is contained in:
364
marketing-agent/services/compliance-guard/src/routes/audit.js
Normal file
364
marketing-agent/services/compliance-guard/src/routes/audit.js
Normal file
@@ -0,0 +1,364 @@
|
||||
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;
|
||||
Reference in New Issue
Block a user