import { writeFileSync, chmodSync, existsSync } from 'node:fs'; import { resolve } from 'node:path'; import * as argon2 from 'argon2'; import { PrismaClient } from '@prisma/client'; import { PERMISSIONS, DEFAULT_ROLE_PERMISSIONS } from '../../shared/src/permissions'; import { generateStrongPassword } from '../../shared/src/generate-password'; const prisma = new PrismaClient(); // Caminho do arquivo mostrado uma única vez com a senha do super_admin // (agente.md seção 70). Nunca versionado (.gitignore). const FIRST_LOGIN_PATH = resolve(__dirname, '../../../FIRST_LOGIN.txt'); async function seedPermissions() { for (const key of PERMISSIONS) { await prisma.permission.upsert({ where: { key }, update: {}, create: { key }, }); } } async function seedRoles() { const allPermissions = await prisma.permission.findMany(); const permissionByKey = new Map(allPermissions.map((p) => [p.key, p.id])); for (const [roleName, permissionKeys] of Object.entries(DEFAULT_ROLE_PERMISSIONS)) { const role = await prisma.role.upsert({ where: { name: roleName }, update: {}, create: { name: roleName, isSystem: true, description: `Perfil padrão: ${roleName}` }, }); for (const key of permissionKeys) { const permissionId = permissionByKey.get(key); if (!permissionId) continue; await prisma.rolePermission.upsert({ where: { roleId_permissionId: { roleId: role.id, permissionId } }, update: {}, create: { roleId: role.id, permissionId }, }); } } } async function seedSuperAdmin() { const existing = await prisma.user.findFirst({ where: { roles: { some: { role: { name: 'super_admin' } } } }, }); if (existing) { console.log('[seed] super_admin já existe, pulando bootstrap de senha.'); return; } const superAdminRole = await prisma.role.findUniqueOrThrow({ where: { name: 'super_admin' } }); const password = generateStrongPassword(); const passwordHash = await argon2.hash(password, { type: argon2.argon2id }); const user = await prisma.user.create({ data: { name: 'Super Admin', email: 'admin@b2bcall.local', passwordHash, mustChangePassword: true, roles: { create: { roleId: superAdminRole.id } }, }, }); const content = [ 'B2BCall - credenciais do primeiro acesso', '==========================================', '', `Usuário: ${user.email}`, `Senha: ${password}`, '', 'Este arquivo é gerado UMA ÚNICA VEZ na primeira instalação.', 'A troca de senha será exigida no primeiro login.', 'Remova este arquivo do servidor após o primeiro acesso.', '', ].join('\n'); writeFileSync(FIRST_LOGIN_PATH, content, { mode: 0o600 }); chmodSync(FIRST_LOGIN_PATH, 0o600); console.log(`[seed] super_admin criado. Credenciais em ${FIRST_LOGIN_PATH}`); } async function main() { if (existsSync(FIRST_LOGIN_PATH)) { console.log('[seed] FIRST_LOGIN.txt já existe — não sobrescrevendo (evita vazar/perder credencial ativa).'); } await seedPermissions(); await seedRoles(); await seedSuperAdmin(); } main() .catch((err) => { console.error(err); process.exitCode = 1; }) .finally(async () => { await prisma.$disconnect(); });