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

529 lines
14 KiB
TypeScript

import { v } from "convex/values";
import { mutation, query } from "./_generated/server";
import type { Id } from "./_generated/dataModel";
/**
* Lista de menus do sistema
*/
export const MENUS_SISTEMA = [
{ path: "/recursos-humanos", nome: "Recursos Humanos", descricao: "Gestão de funcionários e símbolos" },
{ path: "/recursos-humanos/funcionarios", nome: "Funcionários", descricao: "Cadastro e gestão de funcionários" },
{ path: "/recursos-humanos/simbolos", nome: "Símbolos", descricao: "Cadastro e gestão de símbolos" },
{ path: "/financeiro", nome: "Financeiro", descricao: "Gestão financeira" },
{ path: "/controladoria", nome: "Controladoria", descricao: "Controle e auditoria" },
{ path: "/licitacoes", nome: "Licitações", descricao: "Gestão de licitações" },
{ path: "/compras", nome: "Compras", descricao: "Gestão de compras" },
{ path: "/juridico", nome: "Jurídico", descricao: "Departamento jurídico" },
{ path: "/comunicacao", nome: "Comunicação", descricao: "Gestão de comunicação" },
{ path: "/programas-esportivos", nome: "Programas Esportivos", descricao: "Gestão de programas esportivos" },
{ path: "/secretaria-executiva", nome: "Secretaria Executiva", descricao: "Secretaria executiva" },
{ path: "/gestao-pessoas", nome: "Gestão de Pessoas", descricao: "Gestão de recursos humanos" },
{ path: "/ti", nome: "Tecnologia da Informação", descricao: "TI e suporte técnico" },
{ path: "/ti/painel-administrativo", nome: "Painel Administrativo TI", descricao: "Painel de administração do sistema" },
] as const;
/**
* Listar todas as permissões de menu para uma role
*/
export const listarPorRole = query({
args: { roleId: v.id("roles") },
returns: v.array(
v.object({
_id: v.id("menuPermissoes"),
roleId: v.id("roles"),
menuPath: v.string(),
podeAcessar: v.boolean(),
podeConsultar: v.boolean(),
podeGravar: v.boolean(),
})
),
handler: async (ctx, args) => {
return await ctx.db
.query("menuPermissoes")
.withIndex("by_role", (q) => q.eq("roleId", args.roleId))
.collect();
},
});
/**
* Verificar se um usuário tem permissão para acessar um menu
* Prioridade: Permissão personalizada > Permissão da role
*/
export const verificarAcesso = query({
args: {
usuarioId: v.id("usuarios"),
menuPath: v.string(),
},
returns: v.object({
podeAcessar: v.boolean(),
podeConsultar: v.boolean(),
podeGravar: v.boolean(),
motivo: v.optional(v.string()),
}),
handler: async (ctx, args) => {
// Buscar o usuário
const usuario = await ctx.db.get(args.usuarioId);
if (!usuario) {
return {
podeAcessar: false,
podeConsultar: false,
podeGravar: false,
motivo: "Usuário não encontrado",
};
}
// Verificar se o usuário está ativo
if (!usuario.ativo) {
return {
podeAcessar: false,
podeConsultar: false,
podeGravar: false,
motivo: "Usuário inativo",
};
}
// Buscar a role do usuário
const role = await ctx.db.get(usuario.roleId);
if (!role) {
return {
podeAcessar: false,
podeConsultar: false,
podeGravar: false,
motivo: "Role não encontrada",
};
}
// Apenas TI_MASTER (nível 0) tem acesso total irrestrito
// Admin, TI_USUARIO e outros (nível >= 1) têm permissões configuráveis
if (role.nivel === 0) {
return {
podeAcessar: true,
podeConsultar: true,
podeGravar: true,
};
}
// Dashboard e Solicitar Acesso são públicos
if (args.menuPath === "/" || args.menuPath === "/solicitar-acesso") {
return {
podeAcessar: true,
podeConsultar: true,
podeGravar: false,
};
}
// 1. Verificar se existe permissão personalizada para este usuário
const permissaoPersonalizada = await ctx.db
.query("menuPermissoesPersonalizadas")
.withIndex("by_usuario_and_menu", (q) =>
q.eq("usuarioId", args.usuarioId).eq("menuPath", args.menuPath)
)
.first();
if (permissaoPersonalizada) {
return {
podeAcessar: permissaoPersonalizada.podeAcessar,
podeConsultar: permissaoPersonalizada.podeConsultar,
podeGravar: permissaoPersonalizada.podeGravar,
};
}
// 2. Se não houver permissão personalizada, verificar permissão da role
const permissaoRole = await ctx.db
.query("menuPermissoes")
.withIndex("by_role_and_menu", (q) =>
q.eq("roleId", usuario.roleId).eq("menuPath", args.menuPath)
)
.first();
if (!permissaoRole) {
return {
podeAcessar: false,
podeConsultar: false,
podeGravar: false,
motivo: "Sem permissão configurada para este menu",
};
}
return {
podeAcessar: permissaoRole.podeAcessar,
podeConsultar: permissaoRole.podeConsultar,
podeGravar: permissaoRole.podeGravar,
};
},
});
/**
* Atualizar ou criar permissão de menu para uma role
*/
export const atualizarPermissao = mutation({
args: {
roleId: v.id("roles"),
menuPath: v.string(),
podeAcessar: v.boolean(),
podeConsultar: v.boolean(),
podeGravar: v.boolean(),
},
returns: v.id("menuPermissoes"),
handler: async (ctx, args) => {
// Verificar se já existe uma permissão
const existente = await ctx.db
.query("menuPermissoes")
.withIndex("by_role_and_menu", (q) =>
q.eq("roleId", args.roleId).eq("menuPath", args.menuPath)
)
.first();
if (existente) {
// Atualizar permissão existente
await ctx.db.patch(existente._id, {
podeAcessar: args.podeAcessar,
podeConsultar: args.podeConsultar,
podeGravar: args.podeGravar,
});
return existente._id;
} else {
// Criar nova permissão
return await ctx.db.insert("menuPermissoes", {
roleId: args.roleId,
menuPath: args.menuPath,
podeAcessar: args.podeAcessar,
podeConsultar: args.podeConsultar,
podeGravar: args.podeGravar,
});
}
},
});
/**
* Remover permissão de menu
*/
export const removerPermissao = mutation({
args: {
permissaoId: v.id("menuPermissoes"),
},
returns: v.null(),
handler: async (ctx, args) => {
await ctx.db.delete(args.permissaoId);
return null;
},
});
/**
* Inicializar permissões padrão para uma role
*/
export const inicializarPermissoesRole = mutation({
args: {
roleId: v.id("roles"),
},
returns: v.null(),
handler: async (ctx, args) => {
// Buscar a role
const role = await ctx.db.get(args.roleId);
if (!role) {
throw new Error("Role não encontrada");
}
// Admin e TI não precisam de permissões específicas (acesso total)
if (role.nivel <= 1) {
return null;
}
// Para outras roles, criar permissões básicas (apenas consulta)
for (const menu of MENUS_SISTEMA) {
// Verificar se já existe permissão
const existente = await ctx.db
.query("menuPermissoes")
.withIndex("by_role_and_menu", (q) =>
q.eq("roleId", args.roleId).eq("menuPath", menu.path)
)
.first();
if (!existente) {
// Criar permissão padrão (sem acesso)
await ctx.db.insert("menuPermissoes", {
roleId: args.roleId,
menuPath: menu.path,
podeAcessar: false,
podeConsultar: false,
podeGravar: false,
});
}
}
return null;
},
});
/**
* Listar todos os menus do sistema
*/
export const listarMenus = query({
args: {},
returns: v.array(
v.object({
path: v.string(),
nome: v.string(),
descricao: v.string(),
})
),
handler: async (ctx) => {
return MENUS_SISTEMA.map((menu) => ({
path: menu.path,
nome: menu.nome,
descricao: menu.descricao,
}));
},
});
/**
* Obter matriz de permissões (role x menu) para o painel de controle
*/
export const obterMatrizPermissoes = query({
args: {},
returns: v.array(
v.object({
role: v.object({
_id: v.id("roles"),
nome: v.string(),
nivel: v.number(),
descricao: v.string(),
}),
permissoes: v.array(
v.object({
menuPath: v.string(),
menuNome: v.string(),
podeAcessar: v.boolean(),
podeConsultar: v.boolean(),
podeGravar: v.boolean(),
permissaoId: v.optional(v.id("menuPermissoes")),
})
),
})
),
handler: async (ctx) => {
// Buscar todas as roles
// TI_MASTER (nível 0) aparece mas não é editável
// Admin, TI_USUARIO e outros (nível >= 1) são configuráveis
const roles = await ctx.db.query("roles").collect();
const matriz = [];
for (const role of roles) {
const permissoes = [];
for (const menu of MENUS_SISTEMA) {
// Buscar permissão específica
const permissao = await ctx.db
.query("menuPermissoes")
.withIndex("by_role_and_menu", (q) =>
q.eq("roleId", role._id).eq("menuPath", menu.path)
)
.first();
// Admin e TI têm acesso total automático
if (role.nivel <= 1) {
permissoes.push({
menuPath: menu.path,
menuNome: menu.nome,
podeAcessar: true,
podeConsultar: true,
podeGravar: true,
permissaoId: permissao?._id,
});
} else {
permissoes.push({
menuPath: menu.path,
menuNome: menu.nome,
podeAcessar: permissao?.podeAcessar ?? false,
podeConsultar: permissao?.podeConsultar ?? false,
podeGravar: permissao?.podeGravar ?? false,
permissaoId: permissao?._id,
});
}
}
matriz.push({
role: {
_id: role._id,
nome: role.nome,
nivel: role.nivel,
descricao: role.descricao,
},
permissoes,
});
}
return matriz;
},
});
/**
* Criar ou atualizar permissão personalizada por matrícula
*/
export const atualizarPermissaoPersonalizada = mutation({
args: {
matricula: v.string(),
menuPath: v.string(),
podeAcessar: v.boolean(),
podeConsultar: v.boolean(),
podeGravar: v.boolean(),
},
returns: v.union(v.id("menuPermissoesPersonalizadas"), v.null()),
handler: async (ctx, args) => {
// Buscar usuário pela matrícula
const usuario = await ctx.db
.query("usuarios")
.withIndex("by_matricula", (q) => q.eq("matricula", args.matricula))
.first();
if (!usuario) {
throw new Error("Usuário não encontrado com esta matrícula");
}
// Verificar se já existe permissão personalizada
const existente = await ctx.db
.query("menuPermissoesPersonalizadas")
.withIndex("by_usuario_and_menu", (q) =>
q.eq("usuarioId", usuario._id).eq("menuPath", args.menuPath)
)
.first();
if (existente) {
// Atualizar permissão existente
await ctx.db.patch(existente._id, {
podeAcessar: args.podeAcessar,
podeConsultar: args.podeConsultar,
podeGravar: args.podeGravar,
});
return existente._id;
} else {
// Criar nova permissão
return await ctx.db.insert("menuPermissoesPersonalizadas", {
usuarioId: usuario._id,
matricula: args.matricula,
menuPath: args.menuPath,
podeAcessar: args.podeAcessar,
podeConsultar: args.podeConsultar,
podeGravar: args.podeGravar,
});
}
},
});
/**
* Remover permissão personalizada
*/
export const removerPermissaoPersonalizada = mutation({
args: {
permissaoId: v.id("menuPermissoesPersonalizadas"),
},
returns: v.null(),
handler: async (ctx, args) => {
await ctx.db.delete(args.permissaoId);
return null;
},
});
/**
* Listar permissões personalizadas de um usuário por matrícula
*/
export const listarPermissoesPersonalizadas = query({
args: {
matricula: v.string(),
},
returns: v.array(
v.object({
_id: v.id("menuPermissoesPersonalizadas"),
menuPath: v.string(),
menuNome: v.string(),
podeAcessar: v.boolean(),
podeConsultar: v.boolean(),
podeGravar: v.boolean(),
})
),
handler: async (ctx, args) => {
// Buscar usuário
const usuario = await ctx.db
.query("usuarios")
.withIndex("by_matricula", (q) => q.eq("matricula", args.matricula))
.first();
if (!usuario) {
return [];
}
// Buscar permissões personalizadas
const permissoes = await ctx.db
.query("menuPermissoesPersonalizadas")
.withIndex("by_usuario", (q) => q.eq("usuarioId", usuario._id))
.collect();
// Mapear com nomes dos menus
return permissoes.map((p) => {
const menu = MENUS_SISTEMA.find((m) => m.path === p.menuPath);
return {
_id: p._id,
menuPath: p.menuPath,
menuNome: menu?.nome || p.menuPath,
podeAcessar: p.podeAcessar,
podeConsultar: p.podeConsultar,
podeGravar: p.podeGravar,
};
});
},
});
/**
* Buscar usuário por matrícula para o painel de personalização
*/
export const buscarUsuarioPorMatricula = query({
args: {
matricula: v.string(),
},
returns: v.union(
v.object({
_id: v.id("usuarios"),
matricula: v.string(),
nome: v.string(),
email: v.string(),
role: v.object({
nome: v.string(),
nivel: v.number(),
descricao: v.string(),
}),
ativo: v.boolean(),
}),
v.null()
),
handler: async (ctx, args) => {
const usuario = await ctx.db
.query("usuarios")
.withIndex("by_matricula", (q) => q.eq("matricula", args.matricula))
.first();
if (!usuario) {
return null;
}
const role = await ctx.db.get(usuario.roleId);
if (!role) {
return null;
}
return {
_id: usuario._id,
matricula: usuario.matricula,
nome: usuario.nome,
email: usuario.email,
role: {
nome: role.nome,
nivel: role.nivel,
descricao: role.descricao,
},
ativo: usuario.ativo,
};
},
});