/** * Seed de RBAC (agente.md secoes 142, 145, 199, 200): catálogo de * permissions, roles de sistema, e o Platform Super Admin inicial. * * Idempotente — seguro rodar de novo (upsert por chave única). Roda com: * pnpm --filter @b2bcall/auth run seed */ import { randomBytes } from "node:crypto"; import { writeFileSync, chmodSync } from "node:fs"; import { resolve } from "node:path"; import { getPrismaClient } from "@b2bcall/database"; import { hashPassword } from "./password"; // Catálogo completo de permissions (agente.md secao 145). const PERMISSIONS: Array<{ key: string; description: string }> = [ { key: "tenants.view", description: "Ver tenants (plataforma)" }, { key: "tenants.manage", description: "Criar/editar/suspender tenants" }, { key: "billing.view", description: "Ver consumo e faturas" }, { key: "billing.manage", description: "Gerenciar fechamentos de billing" }, { key: "pricing.manage", description: "Gerenciar planos e price books" }, { key: "dashboard.view", description: "Ver dashboards" }, { key: "extensions.view", description: "Ver ramais" }, { key: "extensions.manage", description: "Criar/editar ramais" }, { key: "trunks.view", description: "Ver troncos" }, { key: "trunks.manage", description: "Criar/editar troncos" }, { key: "agents.view", description: "Ver agentes" }, { key: "agents.manage", description: "Criar/editar agentes" }, { key: "queues.view", description: "Ver filas" }, { key: "queues.manage", description: "Criar/editar filas" }, { key: "campaigns.view", description: "Ver campanhas" }, { key: "campaigns.create", description: "Criar campanhas" }, { key: "campaigns.update", description: "Editar campanhas" }, { key: "campaigns.start", description: "Iniciar campanhas" }, { key: "campaigns.pause", description: "Pausar campanhas" }, { key: "campaigns.stop", description: "Parar campanhas" }, { key: "monitoring.view", description: "Ver monitoramento em tempo real" }, { key: "reports.view", description: "Ver relatórios" }, { key: "reports.export", description: "Exportar relatórios" }, { key: "recordings.view", description: "Ver gravações" }, { key: "recordings.download", description: "Baixar gravações" }, { key: "ai.view", description: "Ver análises de IA" }, { key: "ai.manage", description: "Configurar providers/prompts de IA" }, { key: "ai.analyze", description: "Disparar análise de IA manualmente" }, { key: "freeswitch.view", description: "Ver estado do FreeSWITCH" }, { key: "freeswitch.configure", description: "Configurar FreeSWITCH" }, { key: "users.manage", description: "Gerenciar usuários" }, { key: "roles.manage", description: "Gerenciar roles/permissões" }, { key: "audit.view", description: "Ver audit log" }, ]; // Mapeamento inicial role -> permissions. Ponto de partida razoável; // revisar quando existir uma UI de administração de RBAC. const ROLE_PERMISSIONS: Record = { platform_super_admin: PERMISSIONS.map((p) => p.key), // tudo tenant_admin: PERMISSIONS.map((p) => p.key).filter( (key) => !["tenants.view", "tenants.manage", "pricing.manage"].includes(key), ), supervisor: [ "dashboard.view", "extensions.view", "trunks.view", "agents.view", "agents.manage", "queues.view", "campaigns.view", "campaigns.update", "campaigns.start", "campaigns.pause", "campaigns.stop", "monitoring.view", "reports.view", "reports.export", "recordings.view", "recordings.download", "ai.view", ], agent: ["dashboard.view", "campaigns.view"], }; const SYSTEM_ROLES: Array<{ key: string; name: string; scope: "PLATFORM" | "TENANT" }> = [ { key: "platform_super_admin", name: "Platform Super Admin", scope: "PLATFORM" }, { key: "tenant_admin", name: "Tenant Admin", scope: "TENANT" }, { key: "supervisor", name: "Supervisor", scope: "TENANT" }, { key: "agent", name: "Agente", scope: "TENANT" }, ]; const PLATFORM_ADMIN_EMAIL = "admin@b2bcall.local"; async function main() { const prisma = getPrismaClient(); for (const permission of PERMISSIONS) { await prisma.permission.upsert({ where: { key: permission.key }, update: { description: permission.description }, create: permission, }); } console.log(`Permissions: ${PERMISSIONS.length} sincronizadas.`); for (const role of SYSTEM_ROLES) { const created = await prisma.role.upsert({ where: { key: role.key }, update: { name: role.name, scope: role.scope, isSystem: true }, create: { ...role, isSystem: true }, }); const permissionKeys = ROLE_PERMISSIONS[role.key] ?? []; const permissions = await prisma.permission.findMany({ where: { key: { in: permissionKeys } }, }); await prisma.rolePermission.deleteMany({ where: { roleId: created.id } }); if (permissions.length > 0) { await prisma.rolePermission.createMany({ data: permissions.map((p) => ({ roleId: created.id, permissionId: p.id })), skipDuplicates: true, }); } console.log(`Role '${role.key}': ${permissions.length} permissions.`); } const existingPlatformAdmin = await prisma.userRole.findFirst({ where: { tenantId: null, role: { key: "platform_super_admin" } }, }); if (existingPlatformAdmin) { console.log("Platform Super Admin já existe, pulando criação."); } else { const platformSuperAdminRole = await prisma.role.findUniqueOrThrow({ where: { key: "platform_super_admin" }, }); const initialPassword = randomBytes(18).toString("base64url"); const passwordHash = await hashPassword(initialPassword); const admin = await prisma.user.upsert({ where: { email: PLATFORM_ADMIN_EMAIL }, update: {}, create: { email: PLATFORM_ADMIN_EMAIL, passwordHash, name: "Platform Super Admin", mustChangePassword: true, }, }); await prisma.userRole.create({ data: { userId: admin.id, roleId: platformSuperAdminRole.id, tenantId: null }, }); const firstLoginPath = resolve(__dirname, "../../../FIRST_LOGIN.txt"); writeFileSync( firstLoginPath, `B2BCall — Platform Super Admin (gerado em ${new Date().toISOString()})\n` + `Email: ${PLATFORM_ADMIN_EMAIL}\n` + `Senha: ${initialPassword}\n\n` + `Troca de senha OBRIGATÓRIA no primeiro login. Apague este arquivo depois de guardar a senha em um local seguro.\n`, ); chmodSync(firstLoginPath, 0o600); console.log(`Platform Super Admin criado: ${PLATFORM_ADMIN_EMAIL}`); console.log(`Senha salva em ${firstLoginPath} (permissao 600) — nao sera exibida no terminal.`); } await prisma.$disconnect(); } main().catch(async (err) => { console.error(err); process.exit(1); });