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

895 lines
20 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { v } from 'convex/values';
import type { Doc } from './_generated/dataModel';
import { internalQuery, mutation, query } from './_generated/server';
import { getCurrentUserFunction } from './auth';
// Catálogo de permissões base para seed controlado via mutation
const PERMISSOES_BASE = {
permissoes: [
// Funcionários
{
nome: 'funcionarios.detalhar',
recurso: 'funcionarios',
acao: 'detalhar',
descricao: 'Visualizar detalhes de funcionários'
},
{
nome: 'funcionarios.listar',
recurso: 'funcionarios',
acao: 'listar',
descricao: 'Listar funcionários'
},
{
nome: 'funcionarios.ver',
recurso: 'funcionarios',
acao: 'ver',
descricao: 'Visualizar dados completos de funcionários'
},
{
nome: 'funcionarios.criar',
recurso: 'funcionarios',
acao: 'criar',
descricao: 'Criar novos funcionários'
},
{
nome: 'funcionarios.editar',
recurso: 'funcionarios',
acao: 'editar',
descricao: 'Editar dados de funcionários'
},
{
nome: 'funcionarios.excluir',
recurso: 'funcionarios',
acao: 'excluir',
descricao: 'Excluir funcionários'
},
{
nome: 'funcionarios.aprovar_ausencias',
recurso: 'funcionarios',
acao: 'aprovar_ausencias',
descricao: 'Aprovar ausências de funcionários'
},
{
nome: 'ferias.aprovar',
recurso: 'ferias',
acao: 'aprovar',
descricao: 'Aprovar férias de funcionários'
},
{
nome: 'ferias.dashboard',
recurso: 'ferias',
acao: 'dashboard',
descricao: 'Acessar o painel de férias de funcionários'
},
// Atestados e Licenças
{
nome: 'atestados_licencas.listar',
recurso: 'atestados_licencas',
acao: 'listar',
descricao: 'Listar atestados e licenças'
},
{
nome: 'atestados_licencas.criar',
recurso: 'atestados_licencas',
acao: 'criar',
descricao: 'Registrar novos atestados ou licenças'
},
{
nome: 'atestados_licencas.editar',
recurso: 'atestados_licencas',
acao: 'editar',
descricao: 'Editar atestados ou licenças'
},
{
nome: 'atestados_licencas.excluir',
recurso: 'atestados_licencas',
acao: 'excluir',
descricao: 'Excluir atestados ou licenças'
},
{
nome: 'atestados_licencas.dashboard',
recurso: 'atestados_licencas',
acao: 'dashboard',
descricao: 'Acessar painel e gráficos de atestados e licenças'
},
// Ausências
{
nome: 'ausencias.listar',
recurso: 'ausencias',
acao: 'listar',
descricao: 'Listar solicitações de ausência'
},
{
nome: 'ausencias.criar',
recurso: 'ausencias',
acao: 'criar',
descricao: 'Criar solicitações de ausência'
},
{
nome: 'ausencias.aprovar',
recurso: 'ausencias',
acao: 'aprovar',
descricao: 'Aprovar solicitações de ausência'
},
{
nome: 'ausencias.reprovar',
recurso: 'ausencias',
acao: 'reprovar',
descricao: 'Reprovar solicitações de ausência'
},
{
nome: 'ausencias.excluir',
recurso: 'ausencias',
acao: 'excluir',
descricao: 'Excluir solicitações de ausência'
},
// Ponto e Banco de Horas
{
nome: 'ponto.ver',
recurso: 'ponto',
acao: 'ver',
descricao: 'Visualizar telas e relatórios de ponto'
},
{
nome: 'ponto.registrar',
recurso: 'ponto',
acao: 'registrar',
descricao: 'Registrar batidas de ponto'
},
{
nome: 'ponto.editar',
recurso: 'ponto',
acao: 'editar',
descricao: 'Editar registros de ponto (homologação)'
},
{
nome: 'banco_horas.ver',
recurso: 'banco_horas',
acao: 'ver',
descricao: 'Visualizar saldo e extrato de banco de horas'
},
{
nome: 'banco_horas.ajustar',
recurso: 'banco_horas',
acao: 'ajustar',
descricao: 'Criar e aprovar ajustes de banco de horas'
},
{
nome: 'banco_horas.configurar',
recurso: 'banco_horas',
acao: 'configurar',
descricao: 'Configurar regras e alertas de banco de horas'
},
// Símbolos
{
nome: 'simbolos.dashboard',
recurso: 'simbolos',
acao: 'dashboard',
descricao: 'Acessar o painel de símbolos'
},
{
nome: 'simbolos.ver',
recurso: 'simbolos',
acao: 'ver',
descricao: 'Visualizar detalhes de símbolos'
},
{
nome: 'simbolos.listar',
recurso: 'simbolos',
acao: 'listar',
descricao: 'Listar símbolos'
},
{
nome: 'simbolos.criar',
recurso: 'simbolos',
acao: 'criar',
descricao: 'Criar novos símbolos'
},
{
nome: 'simbolos.editar',
recurso: 'simbolos',
acao: 'editar',
descricao: 'Editar símbolos'
},
{
nome: 'simbolos.excluir',
recurso: 'simbolos',
acao: 'excluir',
descricao: 'Excluir símbolos'
},
// TI - Usuários
{
nome: 'ti_usuarios.listar',
recurso: 'ti_usuarios',
acao: 'listar',
descricao: 'Listar usuários do sistema'
},
{
nome: 'ti_usuarios.criar',
recurso: 'ti_usuarios',
acao: 'criar',
descricao: 'Criar novos usuários de acesso'
},
{
nome: 'ti_usuarios.editar',
recurso: 'ti_usuarios',
acao: 'editar',
descricao: 'Editar usuários de acesso'
},
{
nome: 'ti_usuarios.bloquear',
recurso: 'ti_usuarios',
acao: 'bloquear',
descricao: 'Bloquear ou desbloquear usuários'
},
// TI - Perfis
{
nome: 'ti_perfis.listar',
recurso: 'ti_perfis',
acao: 'listar',
descricao: 'Listar perfis de acesso'
},
{
nome: 'ti_perfis.criar',
recurso: 'ti_perfis',
acao: 'criar',
descricao: 'Criar novos perfis de acesso'
},
{
nome: 'ti_perfis.editar',
recurso: 'ti_perfis',
acao: 'editar',
descricao: 'Editar perfis de acesso'
},
// TI - Painel de Permissões
{
nome: 'ti_painel_permissoes.gerenciar',
recurso: 'ti_painel_permissoes',
acao: 'gerenciar',
descricao: 'Gerenciar matriz de permissões por perfil'
},
// TI - Solicitações de Acesso
{
nome: 'ti_solicitacoes_acesso.ver',
recurso: 'ti_solicitacoes_acesso',
acao: 'ver',
descricao: 'Visualizar solicitações de acesso'
},
{
nome: 'ti_solicitacoes_acesso.aprovar',
recurso: 'ti_solicitacoes_acesso',
acao: 'aprovar',
descricao: 'Aprovar solicitações de acesso'
},
{
nome: 'ti_solicitacoes_acesso.reprovar',
recurso: 'ti_solicitacoes_acesso',
acao: 'reprovar',
descricao: 'Reprovar solicitações de acesso'
},
// TI - Configurações de E-mail
{
nome: 'ti_configuracoes_email.configurar',
recurso: 'ti_configuracoes_email',
acao: 'configurar',
descricao: 'Configurar parâmetros de envio de e-mail'
},
// TI - Monitoramento
{
nome: 'ti_monitoramento.ver',
recurso: 'ti_monitoramento',
acao: 'ver',
descricao: 'Acessar painel de monitoramento geral'
},
{
nome: 'ti_monitoramento_emails.ver',
recurso: 'ti_monitoramento_emails',
acao: 'ver',
descricao: 'Acessar monitoramento de envio de e-mails'
},
// TI - Notificações
{
nome: 'ti_notificacoes.configurar',
recurso: 'ti_notificacoes',
acao: 'configurar',
descricao: 'Configurar notificações do sistema'
},
// TI - Times
{
nome: 'ti_times.gerenciar',
recurso: 'ti_times',
acao: 'gerenciar',
descricao: 'Gerenciar times/equipes de TI'
},
// TI - Painel Administrativo
{
nome: 'ti_painel_administrativo.ver',
recurso: 'ti_painel_administrativo',
acao: 'ver',
descricao: 'Acessar painel administrativo de TI'
},
// Financeiro
{
nome: 'financeiro.ver',
recurso: 'financeiro',
acao: 'ver',
descricao: 'Acessar telas do módulo de financeiro'
},
// Controladoria
{
nome: 'controladoria.ver',
recurso: 'controladoria',
acao: 'ver',
descricao: 'Acessar telas do módulo de controladoria'
},
// Licitações
{
nome: 'licitacoes.ver',
recurso: 'licitacoes',
acao: 'ver',
descricao: 'Acessar telas do módulo de licitações'
},
{
nome: 'contratos.listar',
recurso: 'contratos',
acao: 'listar',
descricao: 'Listar contratos'
},
{
nome: 'contratos.criar',
recurso: 'contratos',
acao: 'criar',
descricao: 'Criar novos contratos'
},
{
nome: 'contratos.editar',
recurso: 'contratos',
acao: 'editar',
descricao: 'Editar contratos'
},
{
nome: 'contratos.excluir',
recurso: 'contratos',
acao: 'excluir',
descricao: 'Excluir contratos'
},
{
nome: 'contratos.ver',
recurso: 'contratos',
acao: 'ver',
descricao: 'Visualizar detalhes de contratos'
},
// Compras
{
nome: 'compras.ver',
recurso: 'compras',
acao: 'ver',
descricao: 'Acessar telas do módulo de compras'
},
// Jurídico
{
nome: 'juridico.ver',
recurso: 'juridico',
acao: 'ver',
descricao: 'Acessar telas do módulo jurídico'
},
// Comunicação
{
nome: 'comunicacao.ver',
recurso: 'comunicacao',
acao: 'ver',
descricao: 'Acessar telas do módulo de comunicação'
},
// Programas Esportivos
{
nome: 'programas_esportivos.ver',
recurso: 'programas_esportivos',
acao: 'ver',
descricao: 'Acessar telas do módulo de programas esportivos'
},
// Secretaria Executiva
{
nome: 'secretaria_executiva.ver',
recurso: 'secretaria_executiva',
acao: 'ver',
descricao: 'Acessar telas do módulo de secretaria executiva'
},
// Gestão de Pessoas
{
nome: 'gestao_pessoas.ver',
recurso: 'gestao_pessoas',
acao: 'ver',
descricao: 'Acessar telas do módulo de gestão de pessoas'
},
// Setores
{
nome: 'setores.listar',
recurso: 'setores',
acao: 'listar',
descricao: 'Listar setores'
},
{
nome: 'setores.criar',
recurso: 'setores',
acao: 'criar',
descricao: 'Criar novos setores'
},
{
nome: 'setores.editar',
recurso: 'setores',
acao: 'editar',
descricao: 'Editar setores'
},
{
nome: 'setores.excluir',
recurso: 'setores',
acao: 'excluir',
descricao: 'Excluir setores'
},
// Flow Templates
{
nome: 'fluxos.templates.listar',
recurso: 'fluxos_templates',
acao: 'listar',
descricao: 'Listar templates de fluxo'
},
{
nome: 'fluxos.templates.criar',
recurso: 'fluxos_templates',
acao: 'criar',
descricao: 'Criar templates de fluxo'
},
{
nome: 'fluxos.templates.editar',
recurso: 'fluxos_templates',
acao: 'editar',
descricao: 'Editar templates de fluxo'
},
{
nome: 'fluxos.templates.excluir',
recurso: 'fluxos_templates',
acao: 'excluir',
descricao: 'Excluir templates de fluxo'
},
// Flow Instances
{
nome: 'fluxos.instancias.listar',
recurso: 'fluxos_instancias',
acao: 'listar',
descricao: 'Listar instâncias de fluxo'
},
{
nome: 'fluxos.instancias.criar',
recurso: 'fluxos_instancias',
acao: 'criar',
descricao: 'Criar instâncias de fluxo'
},
{
nome: 'fluxos.instancias.ver',
recurso: 'fluxos_instancias',
acao: 'ver',
descricao: 'Visualizar detalhes de instâncias de fluxo'
},
{
nome: 'fluxos.instancias.atualizar_status',
recurso: 'fluxos_instancias',
acao: 'atualizar_status',
descricao: 'Atualizar status de instâncias de fluxo'
},
{
nome: 'fluxos.instancias.atribuir',
recurso: 'fluxos_instancias',
acao: 'atribuir',
descricao: 'Atribuir responsáveis em instâncias de fluxo'
},
// Flow Documents
{
nome: 'fluxos.documentos.listar',
recurso: 'fluxos_documentos',
acao: 'listar',
descricao: 'Listar documentos de fluxo'
},
{
nome: 'fluxos.documentos.upload',
recurso: 'fluxos_documentos',
acao: 'upload',
descricao: 'Fazer upload de documentos em fluxos'
},
{
nome: 'fluxos.documentos.excluir',
recurso: 'fluxos_documentos',
acao: 'excluir',
descricao: 'Excluir documentos de fluxos'
},
// Pedidos
{
nome: 'pedidos.listar',
recurso: 'pedidos',
acao: 'listar',
descricao: 'Listar pedidos'
},
{
nome: 'pedidos.criar',
recurso: 'pedidos',
acao: 'criar',
descricao: 'Criar novos pedidos'
},
{
nome: 'pedidos.ver',
recurso: 'pedidos',
acao: 'ver',
descricao: 'Visualizar detalhes de pedidos'
},
{
nome: 'pedidos.editar_status',
recurso: 'pedidos',
acao: 'editar_status',
descricao: 'Alterar status de pedidos'
},
{
nome: 'pedidos.adicionar_item',
recurso: 'pedidos',
acao: 'adicionar_item',
descricao: 'Adicionar itens ao pedido'
},
{
nome: 'pedidos.remover_item',
recurso: 'pedidos',
acao: 'remover_item',
descricao: 'Remover itens do pedido'
},
{
nome: 'pedidos.editar',
recurso: 'pedidos',
acao: 'editar',
descricao: 'Editar dados gerais do pedido'
},
{
nome: 'pedidos.excluir',
recurso: 'pedidos',
acao: 'excluir',
descricao: 'Excluir pedidos'
},
// Atas
{
nome: 'atas.listar',
recurso: 'atas',
acao: 'listar',
descricao: 'Listar atas de registro de preços'
},
{
nome: 'atas.criar',
recurso: 'atas',
acao: 'criar',
descricao: 'Criar novas atas'
},
{
nome: 'atas.ver',
recurso: 'atas',
acao: 'ver',
descricao: 'Visualizar detalhes de atas'
},
{
nome: 'atas.editar',
recurso: 'atas',
acao: 'editar',
descricao: 'Editar atas'
},
{
nome: 'atas.excluir',
recurso: 'atas',
acao: 'excluir',
descricao: 'Excluir atas'
},
// Objetos
{
nome: 'objetos.listar',
recurso: 'objetos',
acao: 'listar',
descricao: 'Listar objetos de contratação'
},
{
nome: 'objetos.criar',
recurso: 'objetos',
acao: 'criar',
descricao: 'Criar novos objetos'
},
{
nome: 'objetos.ver',
recurso: 'objetos',
acao: 'ver',
descricao: 'Visualizar detalhes de objetos'
},
{
nome: 'objetos.editar',
recurso: 'objetos',
acao: 'editar',
descricao: 'Editar objetos'
},
{
nome: 'objetos.excluir',
recurso: 'objetos',
acao: 'excluir',
descricao: 'Excluir objetos'
},
// Empresas
{
nome: 'empresas.listar',
recurso: 'empresas',
acao: 'listar',
descricao: 'Listar empresas'
},
{
nome: 'empresas.criar',
recurso: 'empresas',
acao: 'criar',
descricao: 'Criar novas empresas'
},
{
nome: 'empresas.ver',
recurso: 'empresas',
acao: 'ver',
descricao: 'Visualizar detalhes de empresas'
},
{
nome: 'empresas.editar',
recurso: 'empresas',
acao: 'editar',
descricao: 'Editar empresas'
},
{
nome: 'empresas.excluir',
recurso: 'empresas',
acao: 'excluir',
descricao: 'Excluir empresas'
},
// Produtos
{
nome: 'produtos.listar',
recurso: 'produtos',
acao: 'listar',
descricao: 'Listar produtos'
},
{
nome: 'produtos.criar',
recurso: 'produtos',
acao: 'criar',
descricao: 'Criar novos produtos'
},
{
nome: 'produtos.editar',
recurso: 'produtos',
acao: 'editar',
descricao: 'Editar produtos'
},
{
nome: 'produtos.excluir',
recurso: 'produtos',
acao: 'excluir',
descricao: 'Excluir produtos'
},
// Ações
{
nome: 'acoes.listar',
recurso: 'acoes',
acao: 'listar',
descricao: 'Listar ações'
},
{
nome: 'acoes.criar',
recurso: 'acoes',
acao: 'criar',
descricao: 'Criar novas ações'
},
{
nome: 'acoes.editar',
recurso: 'acoes',
acao: 'editar',
descricao: 'Editar ações'
},
{
nome: 'acoes.excluir',
recurso: 'acoes',
acao: 'excluir',
descricao: 'Excluir ações'
},
// Configuração Compras
{
nome: 'config.compras.gerenciar',
recurso: 'config',
acao: 'gerenciar_compras',
descricao: 'Gerenciar configurações de compras'
}
]
} as const;
export const listarRecursosEAcoes = query({
args: {},
returns: v.array(
v.object({
recurso: v.string(),
acoes: v.array(v.string())
})
),
handler: async (ctx) => {
const permissoes = await ctx.db.query('permissoes').collect();
const recursos: Record<string, Set<string>> = {};
for (const perm of permissoes) {
const set = (recursos[perm.recurso] ||= new Set<string>());
set.add(perm.acao);
}
return Object.entries(recursos).map(([recurso, acoes]) => ({
recurso,
acoes: Array.from(acoes).sort()
}));
}
});
export const listarPermissoesAcoesPorRole = query({
args: { roleId: v.id('roles') },
returns: v.array(
v.object({
recurso: v.string(),
acoes: v.array(v.string())
})
),
handler: async (ctx, args) => {
// Buscar vínculos permissao<-role
const rolePerms = await ctx.db
.query('rolePermissoes')
.withIndex('by_role', (q) => q.eq('roleId', args.roleId))
.collect();
// Carregar documentos de permissões vinculadas a este role
const actionsByResource: Record<string, Set<string>> = {};
for (const rp of rolePerms) {
const perm = await ctx.db.get(rp.permissaoId);
if (!perm) continue;
const set = (actionsByResource[perm.recurso] ||= new Set<string>());
set.add(perm.acao);
}
return Object.entries(actionsByResource).map(([recurso, acoes]) => ({
recurso,
acoes: Array.from(acoes).sort()
}));
}
});
export const atualizarPermissaoAcao = mutation({
args: {
roleId: v.id('roles'),
recurso: v.string(),
acao: v.string(),
conceder: v.boolean()
},
returns: v.null(),
handler: async (ctx, args) => {
// Buscar documento de permissão (recurso+acao)
const permissao = await ctx.db
.query('permissoes')
.withIndex('by_recurso_e_acao', (q) => q.eq('recurso', args.recurso).eq('acao', args.acao))
.first();
if (!permissao) return null;
// Verificar vínculo atual
const existente = await ctx.db
.query('rolePermissoes')
.withIndex('by_role', (q) => q.eq('roleId', args.roleId))
.collect();
const vinculo = existente.find((rp) => rp.permissaoId === permissao!._id);
if (args.conceder) {
if (!vinculo) {
await ctx.db.insert('rolePermissoes', {
roleId: args.roleId,
permissaoId: permissao._id
});
}
} else {
if (vinculo) {
await ctx.db.delete(vinculo._id);
}
}
return null;
}
});
export const seedPermissoesBase = mutation({
args: {},
returns: v.null(),
handler: async (ctx) => {
console.log('🔐 Seed de permissões base...');
for (const perm of PERMISSOES_BASE.permissoes) {
const existente = await ctx.db
.query('permissoes')
.withIndex('by_nome', (q) => q.eq('nome', perm.nome))
.first();
if (existente) {
console.log(` Permissão já existe: ${perm.nome}`);
continue;
}
await ctx.db.insert('permissoes', {
nome: perm.nome,
descricao: perm.descricao,
recurso: perm.recurso,
acao: perm.acao
});
console.log(` ✅ Permissão criada: ${perm.nome}`);
}
return null;
}
});
export const verificarAcao = query({
args: {
usuarioId: v.id('usuarios'),
recurso: v.string(),
acao: v.string()
},
returns: v.null(),
handler: async (ctx, args) => {
const usuario = await ctx.db.get(args.usuarioId);
if (!usuario) throw new Error('acesso_negado');
const role = await ctx.db.get(usuario.roleId);
if (!role) throw new Error('acesso_negado');
// Admins têm acesso total
if (role.admin === true) return null;
// Encontrar permissão
const permissao = await ctx.db
.query('permissoes')
.withIndex('by_recurso_e_acao', (q) => q.eq('recurso', args.recurso).eq('acao', args.acao))
.first();
if (!permissao) throw new Error('acesso_negado');
const hasLink = await ctx.db
.query('rolePermissoes')
.withIndex('by_role', (q) => q.eq('roleId', usuario.roleId))
.collect();
const permitido = hasLink.some((rp) => rp.permissaoId === permissao!._id);
if (!permitido) throw new Error('acesso_negado');
return null;
}
});
export const assertPermissaoAcaoAtual = internalQuery({
args: {
recurso: v.string(),
acao: v.string()
},
returns: v.null(),
handler: async (ctx, args) => {
const usuarioAtual: Doc<'usuarios'> | null = (await getCurrentUserFunction(ctx)) ?? null;
if (!usuarioAtual) throw new Error('acesso_negado');
const role = await ctx.db.get(usuarioAtual.roleId);
if (!role) throw new Error('acesso_negado');
if (role.admin === true) return null;
const permissao = await ctx.db
.query('permissoes')
.withIndex('by_recurso_e_acao', (q) => q.eq('recurso', args.recurso).eq('acao', args.acao))
.first();
if (!permissao) throw new Error('acesso_negado');
const links = await ctx.db
.query('rolePermissoes')
.withIndex('by_role', (q) => q.eq('roleId', role._id))
.collect();
const ok = links.some((rp) => rp.permissaoId === permissao!._id);
if (!ok) throw new Error('acesso_negado');
return null;
}
});