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.dashboard', recurso: 'funcionarios', acao: 'dashboard', descricao: 'Acessar o painel de funcionários' }, { nome: 'funcionarios.ver', recurso: 'funcionarios', acao: 'ver', descricao: 'Visualizar detalhes de funcionários' }, { nome: 'funcionarios.listar', recurso: 'funcionarios', acao: 'listar', descricao: 'Listar 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: 'funcionarios.aprovar_ferias', recurso: 'funcionarios', acao: 'aprovar_ferias', descricao: 'Aprovar férias de funcionários' }, // 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' }, // 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> = {}; for (const perm of permissoes) { const set = (recursos[perm.recurso] ||= new Set()); 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> = {}; for (const rp of rolePerms) { const perm = await ctx.db.get(rp.permissaoId); if (!perm) continue; const set = (actionsByResource[perm.recurso] ||= new Set()); 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'); // Níveis administrativos têm acesso total if (role.nivel <= 1) 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.nivel <= 1) 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; } });