Files
sgse-app/packages/backend/convex/permissoesAcoes.ts
killer-cf 5d76c375c2 refactor: streamline chat widget and backend user management
- Removed the .editorconfig file to simplify project configuration.
- Refactored the ChatWidget component to enhance readability and maintainability, including the integration of current user data and improved notification handling.
- Updated backend functions to utilize the new getCurrentUserFunction for user authentication, ensuring consistent user data retrieval across various modules.
- Cleaned up code in multiple backend files, enhancing clarity and performance while maintaining functionality.
- Improved error handling and user feedback mechanisms in user-related operations.
2025-11-08 17:46:10 -03:00

185 lines
4.9 KiB
TypeScript

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']
},
{
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<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);
}
// Normalizar para todos os recursos do catálogo
const result: Array<{ recurso: string; acoes: Array<string> }> = [];
for (const item of CATALOGO_RECURSOS) {
const granted = Array.from(actionsByResource[item.recurso] ?? new Set<string>());
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;
}
});