Audit activité utilisateurs
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* /api/admin/audit-logs
|
||||
* Accessible admin uniquement (requireAuth + requireAdmin appliqués dans server.js).
|
||||
*/
|
||||
|
||||
import { Router } from 'express';
|
||||
import db from '../db/index.js';
|
||||
|
||||
const router = Router();
|
||||
|
||||
// ── Nettoyage automatique des logs > 30 jours ─────────────────────────────
|
||||
function purgeOldLogs() {
|
||||
db.prepare("DELETE FROM audit_logs WHERE created_at < datetime('now', '-30 days')").run();
|
||||
}
|
||||
|
||||
// ── GET / — liste paginée avec filtres ────────────────────────────────────
|
||||
router.get('/', (req, res, next) => {
|
||||
try {
|
||||
purgeOldLogs();
|
||||
|
||||
const page = Math.max(1, Number(req.query.page) || 1);
|
||||
const limit = Math.min(Number(req.query.limit) || 50, 200);
|
||||
const offset = (page - 1) * limit;
|
||||
const category = req.query.category || null;
|
||||
const search = req.query.search || null; // filtre sur email acteur ou cible
|
||||
const dateFrom = req.query.dateFrom || null;
|
||||
const dateTo = req.query.dateTo || null;
|
||||
const userId = req.query.userId ? Number(req.query.userId) : null;
|
||||
|
||||
const conditions = [];
|
||||
const params = [];
|
||||
|
||||
if (category) {
|
||||
conditions.push('al.category = ?');
|
||||
params.push(category);
|
||||
}
|
||||
if (userId) {
|
||||
conditions.push('(al.actor_id = ? OR al.target_user_id = ?)');
|
||||
params.push(userId, userId);
|
||||
}
|
||||
if (search) {
|
||||
conditions.push('(actor.email LIKE ? OR target.email LIKE ? OR actor.display_name LIKE ? OR target.display_name LIKE ?)');
|
||||
const like = `%${search}%`;
|
||||
params.push(like, like, like, like);
|
||||
}
|
||||
if (dateFrom) {
|
||||
conditions.push('al.created_at >= ?');
|
||||
params.push(dateFrom);
|
||||
}
|
||||
if (dateTo) {
|
||||
conditions.push('al.created_at <= ?');
|
||||
params.push(dateTo + ' 23:59:59');
|
||||
}
|
||||
|
||||
const where = conditions.length ? `WHERE ${conditions.join(' AND ')}` : '';
|
||||
|
||||
const rows = db.prepare(`
|
||||
SELECT
|
||||
al.id,
|
||||
al.action,
|
||||
al.category,
|
||||
al.details,
|
||||
al.ip_address,
|
||||
al.user_agent,
|
||||
al.created_at,
|
||||
actor.id AS actor_id,
|
||||
actor.email AS actor_email,
|
||||
actor.display_name AS actor_name,
|
||||
target.id AS target_id,
|
||||
target.email AS target_email,
|
||||
target.display_name AS target_name
|
||||
FROM audit_logs al
|
||||
LEFT JOIN users actor ON actor.id = al.actor_id
|
||||
LEFT JOIN users target ON target.id = al.target_user_id
|
||||
${where}
|
||||
ORDER BY al.created_at DESC
|
||||
LIMIT ? OFFSET ?
|
||||
`).all(...params, limit, offset);
|
||||
|
||||
const total = db.prepare(`
|
||||
SELECT COUNT(*) AS n
|
||||
FROM audit_logs al
|
||||
LEFT JOIN users actor ON actor.id = al.actor_id
|
||||
LEFT JOIN users target ON target.id = al.target_user_id
|
||||
${where}
|
||||
`).get(...params).n;
|
||||
|
||||
// Parser les details JSON
|
||||
const parsed = rows.map(r => ({
|
||||
...r,
|
||||
details: r.details ? (() => { try { return JSON.parse(r.details); } catch { return r.details; } })() : null,
|
||||
}));
|
||||
|
||||
res.json({ total, page, limit, rows: parsed });
|
||||
} catch (e) { next(e); }
|
||||
});
|
||||
|
||||
// ── GET /categories — liste des catégories présentes ──────────────────────
|
||||
router.get('/categories', (_req, res, next) => {
|
||||
try {
|
||||
const rows = db.prepare(
|
||||
"SELECT DISTINCT category FROM audit_logs WHERE created_at >= datetime('now', '-30 days') ORDER BY category"
|
||||
).all();
|
||||
res.json(rows.map(r => r.category));
|
||||
} catch (e) { next(e); }
|
||||
});
|
||||
|
||||
export default router;
|
||||
Reference in New Issue
Block a user