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 { 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); } }