220 lines
5.3 KiB
TypeScript
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 };
|
|
}
|
|
});
|