Files
sgse-app/packages/backend/convex/logsAcesso.ts

228 lines
5.6 KiB
TypeScript

import { v } from "convex/values";
import { mutation, query } from "./_generated/server";
/**
* Listar logs de acesso com filtros
*/
export const listar = query({
args: {
usuarioId: v.optional(v.id("usuarios")),
tipo: v.optional(
v.union(
v.literal("login"),
v.literal("logout"),
v.literal("acesso_negado"),
v.literal("senha_alterada"),
v.literal("sessao_expirada")
)
),
dataInicio: v.optional(v.number()),
dataFim: v.optional(v.number()),
limite: v.optional(v.number()),
},
returns: v.array(
v.object({
_id: v.id("logsAcesso"),
tipo: v.union(
v.literal("login"),
v.literal("logout"),
v.literal("acesso_negado"),
v.literal("senha_alterada"),
v.literal("sessao_expirada")
),
ipAddress: v.optional(v.string()),
userAgent: v.optional(v.string()),
detalhes: v.optional(v.string()),
timestamp: v.number(),
usuario: v.optional(
v.object({
_id: v.id("usuarios"),
matricula: v.string(),
nome: v.string(),
})
),
})
),
handler: async (ctx, args) => {
let logs;
// Filtrar por usuário
if (args.usuarioId !== undefined) {
const usuarioId = args.usuarioId; // TypeScript agora sabe que não é undefined
logs = await ctx.db
.query("logsAcesso")
.withIndex("by_usuario", (q) => q.eq("usuarioId", usuarioId))
.collect();
} else {
logs = await ctx.db
.query("logsAcesso")
.withIndex("by_timestamp")
.collect();
}
// Filtrar por tipo
if (args.tipo) {
logs = logs.filter((log) => log.tipo === args.tipo);
}
// Filtrar por data
if (args.dataInicio) {
logs = logs.filter((log) => log.timestamp >= args.dataInicio!);
}
if (args.dataFim) {
logs = logs.filter((log) => log.timestamp <= args.dataFim!);
}
// Ordenar por timestamp decrescente
logs.sort((a, b) => b.timestamp - a.timestamp);
// Limitar resultados
if (args.limite) {
logs = logs.slice(0, args.limite);
}
// Buscar informações dos usuários
const resultado = [];
for (const log of logs) {
let usuario = undefined;
if (log.usuarioId) {
const user = await ctx.db.get(log.usuarioId);
if (user) {
usuario = {
_id: user._id,
matricula: user.matricula,
nome: user.nome,
};
}
}
resultado.push({
_id: log._id,
tipo: log.tipo,
ipAddress: log.ipAddress,
userAgent: log.userAgent,
detalhes: log.detalhes,
timestamp: log.timestamp,
usuario,
});
}
return resultado;
},
});
/**
* Obter estatísticas de acessos
*/
export const estatisticas = query({
args: {
dataInicio: v.optional(v.number()),
dataFim: v.optional(v.number()),
},
returns: v.object({
totalLogins: v.number(),
totalLogouts: v.number(),
totalAcessosNegados: v.number(),
totalSenhasAlteradas: v.number(),
totalSessoesExpiradas: v.number(),
loginsPorDia: v.array(
v.object({
data: v.string(),
quantidade: v.number(),
})
),
}),
handler: async (ctx, args) => {
let logs = await ctx.db.query("logsAcesso").collect();
// Filtrar por data
if (args.dataInicio) {
logs = logs.filter((log) => log.timestamp >= args.dataInicio!);
}
if (args.dataFim) {
logs = logs.filter((log) => log.timestamp <= args.dataFim!);
}
// Contar por tipo
const totalLogins = logs.filter((log) => log.tipo === "login").length;
const totalLogouts = logs.filter((log) => log.tipo === "logout").length;
const totalAcessosNegados = logs.filter(
(log) => log.tipo === "acesso_negado"
).length;
const totalSenhasAlteradas = logs.filter(
(log) => log.tipo === "senha_alterada"
).length;
const totalSessoesExpiradas = logs.filter(
(log) => log.tipo === "sessao_expirada"
).length;
// Agrupar logins por dia
const loginsPorDiaMap = new Map<string, number>();
const loginsOnly = logs.filter((log) => log.tipo === "login");
for (const log of loginsOnly) {
const data = new Date(log.timestamp).toISOString().split("T")[0];
loginsPorDiaMap.set(data, (loginsPorDiaMap.get(data) || 0) + 1);
}
const loginsPorDia = Array.from(loginsPorDiaMap.entries())
.map(([data, quantidade]) => ({ data, quantidade }))
.sort((a, b) => a.data.localeCompare(b.data));
return {
totalLogins,
totalLogouts,
totalAcessosNegados,
totalSenhasAlteradas,
totalSessoesExpiradas,
loginsPorDia,
};
},
});
/**
* Limpar logs antigos (apenas TI)
*/
export const limpar = mutation({
args: {
dataLimite: v.number(), // Excluir logs anteriores a esta data
},
returns: v.object({
excluidos: v.number(),
}),
handler: async (ctx, args) => {
const logs = await ctx.db
.query("logsAcesso")
.withIndex("by_timestamp")
.collect();
const logsAntigos = logs.filter((log) => log.timestamp < args.dataLimite);
for (const log of logsAntigos) {
await ctx.db.delete(log._id);
}
return { excluidos: logsAntigos.length };
},
});
/**
* Limpar todos os logs (apenas TI)
*/
export const limparTodos = mutation({
args: {},
returns: v.object({
excluidos: v.number(),
}),
handler: async (ctx) => {
const logs = await ctx.db.query("logsAcesso").collect();
for (const log of logs) {
await ctx.db.delete(log._id);
}
return { excluidos: logs.length };
},
});