Files
HoruxDespachos/apps/api/src/controllers/audit-log.controller.ts
2026-04-27 01:11:06 -06:00

88 lines
3.1 KiB
TypeScript

import type { Request, Response, NextFunction } from 'express';
import { prisma } from '../config/database.js';
import { isGlobalAdmin } from '../utils/global-admin.js';
async function requireGlobalAdmin(req: Request, res: Response): Promise<boolean> {
const isAdmin = await isGlobalAdmin(req.user!.tenantId, req.user!.role);
if (!isAdmin) {
res.status(403).json({ message: 'Solo el administrador global puede consultar el audit log' });
}
return isAdmin;
}
/**
* Lista eventos de audit con filtros opcionales. Admin global only.
*
* Query params:
* action — filtra por action prefix (ej: "subscription." matches todas las subs)
* tenantId — filtra a un tenant específico
* userId — filtra a un user específico
* from, to — rango de fechas (ISO)
* page, limit — paginación (default: 1, 50; max limit 200)
*/
export async function listAuditLog(req: Request, res: Response, next: NextFunction) {
try {
if (!(await requireGlobalAdmin(req, res))) return;
const action = String(req.query.action || '').trim();
const tenantId = String(req.query.tenantId || '').trim();
const userId = String(req.query.userId || '').trim();
const from = String(req.query.from || '').trim();
const to = String(req.query.to || '').trim();
const page = Math.max(1, parseInt(String(req.query.page || '1'), 10) || 1);
const limit = Math.min(200, Math.max(1, parseInt(String(req.query.limit || '50'), 10) || 50));
const where: any = {};
if (action) where.action = { startsWith: action };
if (tenantId) where.tenantId = tenantId;
if (userId) where.userId = userId;
if (from || to) {
where.createdAt = {};
if (from) where.createdAt.gte = new Date(from);
if (to) where.createdAt.lte = new Date(to);
}
const [total, rows] = await Promise.all([
prisma.auditLog.count({ where }),
prisma.auditLog.findMany({
where,
orderBy: { createdAt: 'desc' },
skip: (page - 1) * limit,
take: limit,
}),
]);
// Enriquecer con user.email y tenant.nombre para display
const userIds = [...new Set(rows.map(r => r.userId).filter(Boolean))] as string[];
const tenantIds = [...new Set(rows.map(r => r.tenantId).filter(Boolean))] as string[];
const [users, tenants] = await Promise.all([
userIds.length
? prisma.user.findMany({ where: { id: { in: userIds } }, select: { id: true, email: true, nombre: true } })
: [],
tenantIds.length
? prisma.tenant.findMany({ where: { id: { in: tenantIds } }, select: { id: true, nombre: true, rfc: true } })
: [],
]);
const userMap = new Map(users.map(u => [u.id, u]));
const tenantMap = new Map(tenants.map(t => [t.id, t]));
const data = rows.map(r => ({
...r,
user: r.userId ? userMap.get(r.userId) || null : null,
tenant: r.tenantId ? tenantMap.get(r.tenantId) || null : null,
}));
res.json({
data,
page,
limit,
total,
totalPages: Math.ceil(total / limit),
});
} catch (error) {
next(error);
}
}