const AuditLog = require('../models/AuditLog'); const logger = require('../utils/logger'); /** * Get all audit logs with pagination and filtering */ const getAuditLogs = async (req, res) => { try { const page = Math.max(1, parseInt(req.query.page) || 1); const limit = Math.min(100, Math.max(1, parseInt(req.query.limit) || 50)); const skip = (page - 1) * limit; // Build filter const filter = {}; if (req.query.action) { filter.action = req.query.action; } if (req.query.resource) { filter.resource = req.query.resource; } if (req.query.adminUsername) { filter.adminUsername = req.query.adminUsername; } if (req.query.success !== undefined) { filter.success = req.query.success === 'true'; } // Date range filter if (req.query.startDate || req.query.endDate) { filter.createdAt = {}; if (req.query.startDate) { filter.createdAt.$gte = new Date(req.query.startDate); } if (req.query.endDate) { filter.createdAt.$lte = new Date(req.query.endDate); } } const [logs, total] = await Promise.all([ AuditLog.find(filter) .sort({ createdAt: -1 }) .skip(skip) .limit(limit) .populate('adminId', 'username') .select('-__v'), AuditLog.countDocuments(filter) ]); res.json({ success: true, data: logs, pagination: { page, limit, total, pages: Math.ceil(total / limit) } }); } catch (error) { logger.error('Fehler beim Abrufen der Audit-Logs:', error); res.status(500).json({ success: false, message: 'Fehler beim Abrufen der Audit-Logs' }); } }; /** * Get audit logs for a specific resource */ const getResourceAuditLogs = async (req, res) => { try { const { resource, resourceId } = req.params; const logs = await AuditLog.find({ resource, resourceId }) .sort({ createdAt: -1 }) .limit(50) .populate('adminId', 'username') .select('-__v'); res.json({ success: true, data: logs, count: logs.length }); } catch (error) { logger.error('Fehler beim Abrufen der Resource-Audit-Logs:', error); res.status(500).json({ success: false, message: 'Fehler beim Abrufen der Resource-Audit-Logs' }); } }; /** * Get admin activity summary */ const getAdminActivity = async (req, res) => { try { const { adminId } = req.params; const stats = await AuditLog.aggregate([ { $match: { adminId: require('mongoose').Types.ObjectId(adminId) } }, { $group: { _id: '$action', count: { $sum: 1 } } }, { $sort: { count: -1 } } ]); const recentActivity = await AuditLog.find({ adminId }) .sort({ createdAt: -1 }) .limit(20) .select('-__v'); res.json({ success: true, data: { stats, recentActivity } }); } catch (error) { logger.error('Fehler beim Abrufen der Admin-Aktivität:', error); res.status(500).json({ success: false, message: 'Fehler beim Abrufen der Admin-Aktivität' }); } }; /** * Aggregate statistics: counts by action, resource, admin, success/fail, daily activity */ const getAuditStats = async (req, res) => { try { const days = Math.min(365, Math.max(1, parseInt(req.query.days) || 30)); const since = new Date(Date.now() - days * 24 * 60 * 60 * 1000); const [byAction, byAdmin, byResource, bySuccess, dailyActivity] = await Promise.all([ AuditLog.aggregate([ { $match: { createdAt: { $gte: since } } }, { $group: { _id: '$action', count: { $sum: 1 } } }, { $sort: { count: -1 } } ]), AuditLog.aggregate([ { $match: { createdAt: { $gte: since } } }, { $group: { _id: '$adminUsername', count: { $sum: 1 } } }, { $sort: { count: -1 } }, { $limit: 10 } ]), AuditLog.aggregate([ { $match: { createdAt: { $gte: since } } }, { $group: { _id: '$resource', count: { $sum: 1 } } }, { $sort: { count: -1 } } ]), AuditLog.aggregate([ { $match: { createdAt: { $gte: since } } }, { $group: { _id: '$success', count: { $sum: 1 } } } ]), AuditLog.aggregate([ { $match: { createdAt: { $gte: since } } }, { $group: { _id: { $dateToString: { format: '%Y-%m-%d', date: '$createdAt' } }, count: { $sum: 1 }, failed: { $sum: { $cond: [{ $eq: ['$success', false] }, 1, 0] } } }}, { $sort: { _id: 1 } } ]) ]); const total = byAction.reduce((s, a) => s + a.count, 0); const failed = bySuccess.find(s => s._id === false)?.count || 0; res.json({ success: true, data: { total, failed, days, since, byAction: byAction.map(a => ({ action: a._id, count: a.count })), byAdmin: byAdmin.map(a => ({ admin: a._id || 'unknown', count: a.count })), byResource: byResource.map(a => ({ resource: a._id, count: a.count })), dailyActivity: dailyActivity.map(a => ({ date: a._id, count: a.count, failed: a.failed })) } }); } catch (error) { logger.error('Fehler beim Abrufen der Audit-Statistiken:', error); res.status(500).json({ success: false, message: 'Fehler beim Abrufen der Audit-Statistiken' }); } }; /** * Export audit logs as CSV (respects same filters as getAuditLogs) */ const exportAuditLogs = async (req, res) => { try { const filter = {}; if (req.query.action) filter.action = req.query.action; if (req.query.resource) filter.resource = req.query.resource; if (req.query.adminUsername) filter.adminUsername = req.query.adminUsername; if (req.query.success !== undefined) filter.success = req.query.success === 'true'; if (req.query.startDate || req.query.endDate) { filter.createdAt = {}; if (req.query.startDate) filter.createdAt.$gte = new Date(req.query.startDate); if (req.query.endDate) filter.createdAt.$lte = new Date(req.query.endDate); } const logs = await AuditLog.find(filter) .sort({ createdAt: -1 }) .limit(10000) .lean(); const escapeCell = (val) => { if (val == null) return ''; const str = String(val); return str.includes(',') || str.includes('"') || str.includes('\n') ? `"${str.replace(/"/g, '""')}"` : str; }; const header = [ 'Zeitstempel', 'Aktion', 'Ressource', 'Ressourcen-Name', 'Admin', 'IP-Adresse', 'Methode', 'Pfad', 'Status-Code', 'Dauer (ms)', 'Erfolgreich', 'Fehlermeldung', 'Geänderte Felder', 'Metadaten' ].join(','); const rows = logs.map(log => [ escapeCell(new Date(log.createdAt).toLocaleString('de-DE')), escapeCell(log.action), escapeCell(log.resource), escapeCell(log.resourceName), escapeCell(log.adminUsername), escapeCell(log.ipAddress), escapeCell(log.requestMethod), escapeCell(log.requestPath), escapeCell(log.statusCode), escapeCell(log.duration), log.success ? 'Ja' : 'Nein', escapeCell(log.errorMessage), escapeCell(log.changes ? Object.keys(log.changes).join(', ') : ''), escapeCell(log.metadata ? JSON.stringify(log.metadata) : '') ].join(',')); const filename = `audit-logs_${new Date().toISOString().slice(0, 10)}.csv`; res.setHeader('Content-Type', 'text/csv; charset=utf-8'); res.setHeader('Content-Disposition', `attachment; filename="${filename}"`); res.send('\uFEFF' + [header, ...rows].join('\n')); } catch (error) { logger.error('Fehler beim Exportieren der Audit-Logs:', error); res.status(500).json({ success: false, message: 'Fehler beim Exportieren der Audit-Logs' }); } }; module.exports = { getAuditLogs, getResourceAuditLogs, getAdminActivity, getAuditStats, exportAuditLogs };