import { query, mutation, internalQuery } from './_generated/server'; import { v } from 'convex/values'; import type { Doc } from './_generated/dataModel'; import { getCurrentUserFunction } from './auth'; // Catálogo base de recursos e ações // Ajuste/expanda conforme os módulos disponíveis no sistema export const CATALOGO_RECURSOS = [ { recurso: 'funcionarios', acoes: [ 'dashboard', 'ver', 'listar', 'criar', 'editar', 'excluir', 'aprovar_ausencias', 'aprovar_ferias' ] }, { recurso: 'simbolos', acoes: ['dashboard', 'ver', 'listar', 'criar', 'editar', 'excluir'] } ] as const; export const listarRecursosEAcoes = query({ args: {}, returns: v.array( v.object({ recurso: v.string(), acoes: v.array(v.string()) }) ), handler: async () => { return CATALOGO_RECURSOS.map((r) => ({ recurso: r.recurso, acoes: [...r.acoes] })); } }); 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 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); } // Normalizar para todos os recursos do catálogo const result: Array<{ recurso: string; acoes: Array }> = []; for (const item of CATALOGO_RECURSOS) { const granted = Array.from(actionsByResource[item.recurso] ?? new Set()); result.push({ recurso: item.recurso, acoes: granted }); } return result; } }); 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) => { // Garantir documento de permissão (recurso+acao) let permissao = await ctx.db .query('permissoes') .withIndex('by_recurso_e_acao', (q) => q.eq('recurso', args.recurso).eq('acao', args.acao)) .first(); if (!permissao) { const nome = `${args.recurso}.${args.acao}`; const descricao = `Permite ${args.acao} em ${args.recurso}`; const id = await ctx.db.insert('permissoes', { nome, descricao, recurso: args.recurso, acao: args.acao }); permissao = await ctx.db.get(id); } 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 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; } });