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

347 lines
9.1 KiB
TypeScript

import { v } from "convex/values";
import { mutation, query } from "./_generated/server";
import { registrarAtividade } from "./logsAtividades";
/**
* Listar todos os perfis customizados
*/
export const listarPerfisCustomizados = query({
args: {},
handler: async (ctx) => {
const perfis = await ctx.db.query("perfisCustomizados").collect();
// Buscar role correspondente para cada perfil
const perfisComDetalhes = await Promise.all(
perfis.map(async (perfil) => {
const role = await ctx.db.get(perfil.roleId);
const criador = await ctx.db.get(perfil.criadoPor);
// Contar usuários usando este perfil
const usuarios = await ctx.db
.query("usuarios")
.withIndex("by_role", (q) => q.eq("roleId", perfil.roleId))
.collect();
return {
...perfil,
roleNome: role?.nome || "Desconhecido",
criadorNome: criador?.nome || "Desconhecido",
numeroUsuarios: usuarios.length,
};
})
);
return perfisComDetalhes;
},
});
/**
* Obter perfil com permissões detalhadas
*/
export const obterPerfilComPermissoes = query({
args: {
perfilId: v.id("perfisCustomizados"),
},
handler: async (ctx, args) => {
const perfil = await ctx.db.get(args.perfilId);
if (!perfil) {
return null;
}
const role = await ctx.db.get(perfil.roleId);
if (!role) {
return null;
}
// Buscar permissões do role
const rolePermissoes = await ctx.db
.query("rolePermissoes")
.withIndex("by_role", (q) => q.eq("roleId", perfil.roleId))
.collect();
const permissoes = await Promise.all(
rolePermissoes.map(async (rp) => {
return await ctx.db.get(rp.permissaoId);
})
);
// Buscar permissões de menu
const menuPermissoes = await ctx.db
.query("menuPermissoes")
.withIndex("by_role", (q) => q.eq("roleId", perfil.roleId))
.collect();
// Buscar usuários usando este perfil
const usuarios = await ctx.db
.query("usuarios")
.withIndex("by_role", (q) => q.eq("roleId", perfil.roleId))
.collect();
return {
perfil,
role,
permissoes: permissoes.filter((p) => p !== null),
menuPermissoes,
usuarios,
};
},
});
/**
* Criar perfil customizado (apenas TI_MASTER)
*/
export const criarPerfilCustomizado = mutation({
args: {
nome: v.string(),
descricao: v.string(),
nivel: v.number(), // >= 3
clonarDeRoleId: v.optional(v.id("roles")), // role para copiar permissões
criadoPorId: v.id("usuarios"),
},
returns: v.union(
v.object({ sucesso: v.literal(true), perfilId: v.id("perfisCustomizados") }),
v.object({ sucesso: v.literal(false), erro: v.string() })
),
handler: async (ctx, args) => {
// Validar nível (deve ser >= 3)
if (args.nivel < 3) {
return { sucesso: false as const, erro: "Perfis customizados devem ter nível >= 3" };
}
// Verificar se nome já existe
const roles = await ctx.db.query("roles").collect();
const nomeExiste = roles.some((r) => r.nome.toLowerCase() === args.nome.toLowerCase());
if (nomeExiste) {
return { sucesso: false as const, erro: "Já existe um perfil com este nome" };
}
// Criar role correspondente
const roleId = await ctx.db.insert("roles", {
nome: args.nome.toLowerCase().replace(/\s+/g, "_"),
descricao: args.descricao,
nivel: args.nivel,
customizado: true,
criadoPor: args.criadoPorId,
editavel: true,
});
// Copiar permissões se especificado
if (args.clonarDeRoleId) {
// Copiar permissões gerais
const permissoesClonar = await ctx.db
.query("rolePermissoes")
.withIndex("by_role", (q) => q.eq("roleId", args.clonarDeRoleId))
.collect();
for (const perm of permissoesClonar) {
await ctx.db.insert("rolePermissoes", {
roleId,
permissaoId: perm.permissaoId,
});
}
// Copiar permissões de menu
const menuPermsClonar = await ctx.db
.query("menuPermissoes")
.withIndex("by_role", (q) => q.eq("roleId", args.clonarDeRoleId))
.collect();
for (const menuPerm of menuPermsClonar) {
await ctx.db.insert("menuPermissoes", {
roleId,
menuPath: menuPerm.menuPath,
podeAcessar: menuPerm.podeAcessar,
podeConsultar: menuPerm.podeConsultar,
podeGravar: menuPerm.podeGravar,
});
}
}
// Criar perfil customizado
const perfilId = await ctx.db.insert("perfisCustomizados", {
nome: args.nome,
descricao: args.descricao,
nivel: args.nivel,
roleId,
criadoPor: args.criadoPorId,
criadoEm: Date.now(),
atualizadoEm: Date.now(),
});
// Log de atividade
await registrarAtividade(
ctx,
args.criadoPorId,
"criar",
"perfis",
JSON.stringify({ perfilId, nome: args.nome, nivel: args.nivel }),
perfilId
);
return { sucesso: true as const, perfilId };
},
});
/**
* Editar perfil customizado (apenas TI_MASTER)
*/
export const editarPerfilCustomizado = mutation({
args: {
perfilId: v.id("perfisCustomizados"),
nome: v.optional(v.string()),
descricao: v.optional(v.string()),
editadoPorId: v.id("usuarios"),
},
returns: v.union(
v.object({ sucesso: v.literal(true) }),
v.object({ sucesso: v.literal(false), erro: v.string() })
),
handler: async (ctx, args) => {
const perfil = await ctx.db.get(args.perfilId);
if (!perfil) {
return { sucesso: false as const, erro: "Perfil não encontrado" };
}
// Atualizar perfil
const updates: any = {
atualizadoEm: Date.now(),
};
if (args.nome !== undefined) updates.nome = args.nome;
if (args.descricao !== undefined) updates.descricao = args.descricao;
await ctx.db.patch(args.perfilId, updates);
// Atualizar role correspondente se nome mudou
if (args.nome !== undefined) {
await ctx.db.patch(perfil.roleId, {
nome: args.nome.toLowerCase().replace(/\s+/g, "_"),
});
}
if (args.descricao !== undefined) {
await ctx.db.patch(perfil.roleId, {
descricao: args.descricao,
});
}
// Log de atividade
await registrarAtividade(
ctx,
args.editadoPorId,
"editar",
"perfis",
JSON.stringify(updates),
args.perfilId
);
return { sucesso: true as const };
},
});
/**
* Excluir perfil customizado (apenas TI_MASTER)
*/
export const excluirPerfilCustomizado = mutation({
args: {
perfilId: v.id("perfisCustomizados"),
excluidoPorId: v.id("usuarios"),
},
returns: v.union(
v.object({ sucesso: v.literal(true) }),
v.object({ sucesso: v.literal(false), erro: v.string() })
),
handler: async (ctx, args) => {
const perfil = await ctx.db.get(args.perfilId);
if (!perfil) {
return { sucesso: false as const, erro: "Perfil não encontrado" };
}
// Verificar se existem usuários usando este perfil
const usuarios = await ctx.db
.query("usuarios")
.withIndex("by_role", (q) => q.eq("roleId", perfil.roleId))
.collect();
if (usuarios.length > 0) {
return {
sucesso: false as const,
erro: `Não é possível excluir. ${usuarios.length} usuário(s) ainda usa(m) este perfil.`,
};
}
// Remover permissões associadas ao role
const rolePermissoes = await ctx.db
.query("rolePermissoes")
.withIndex("by_role", (q) => q.eq("roleId", perfil.roleId))
.collect();
for (const rp of rolePermissoes) {
await ctx.db.delete(rp._id);
}
// Remover permissões de menu
const menuPermissoes = await ctx.db
.query("menuPermissoes")
.withIndex("by_role", (q) => q.eq("roleId", perfil.roleId))
.collect();
for (const mp of menuPermissoes) {
await ctx.db.delete(mp._id);
}
// Excluir role
await ctx.db.delete(perfil.roleId);
// Excluir perfil
await ctx.db.delete(args.perfilId);
// Log de atividade
await registrarAtividade(
ctx,
args.excluidoPorId,
"excluir",
"perfis",
JSON.stringify({ perfilId: args.perfilId, nome: perfil.nome }),
args.perfilId
);
return { sucesso: true as const };
},
});
/**
* Clonar perfil existente
*/
export const clonarPerfil = mutation({
args: {
perfilOrigemId: v.id("perfisCustomizados"),
novoNome: v.string(),
novaDescricao: v.string(),
criadoPorId: v.id("usuarios"),
},
returns: v.union(
v.object({ sucesso: v.literal(true), perfilId: v.id("perfisCustomizados") }),
v.object({ sucesso: v.literal(false), erro: v.string() })
),
handler: async (ctx, args) => {
const perfilOrigem = await ctx.db.get(args.perfilOrigemId);
if (!perfilOrigem) {
return { sucesso: false as const, erro: "Perfil origem não encontrado" };
}
// Criar novo perfil clonando o original
const resultado = await criarPerfilCustomizado(ctx, {
nome: args.novoNome,
descricao: args.novaDescricao,
nivel: perfilOrigem.nivel,
clonarDeRoleId: perfilOrigem.roleId,
criadoPorId: args.criadoPorId,
});
return resultado;
},
});