import { query, mutation, internalQuery } from "./_generated/server"; import { v } from "convex/values"; import type { Doc } from "./_generated/dataModel"; // 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"], }, { 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 identity = await ctx.auth.getUserIdentity(); let usuarioAtual: Doc<"usuarios"> | null = null; if (identity && identity.email) { usuarioAtual = await ctx.db .query("usuarios") .withIndex("by_email", (q) => q.eq("email", identity.email!)) .first(); } if (!usuarioAtual) { const sessaoAtiva = await ctx.db .query("sessoes") .filter((q) => q.eq(q.field("ativo"), true)) .order("desc") .first(); if (sessaoAtiva) { usuarioAtual = await ctx.db.get(sessaoAtiva.usuarioId); } } 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; }, });