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

220 lines
5.3 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) {
let matricula: string | undefined = undefined;
if (user.funcionarioId) {
const funcionario = await ctx.db.get(user.funcionarioId);
matricula = funcionario?.matricula;
}
usuario = {
_id: user._id,
matricula: 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 };
}
});