diff --git a/agents.md b/agents.md index 162fb24..5bed7d3 100644 --- a/agents.md +++ b/agents.md @@ -5,6 +5,21 @@ Construir o nucleo de tickets compartilhado entre web e desktop (Tauri), garanti ### Contato principal - **Esdras Renan** — monkeyesdras@gmail.com + +### Credenciais seed (ambiente local) +- Administrador padrão: `admin@sistema.dev` / `admin123` +- Agentes carregados via seed (senha inicial `agent123`, altere após o primeiro acesso): + - Gabriel Oliveira — gabriel.oliveira@rever.com.br + - George Araujo — george.araujo@rever.com.br + - Hugo Soares — hugo.soares@rever.com.br + - Julio Cesar — julio@rever.com.br + - Lorena Magalhães — lorena@rever.com.br + - Rever — renan.pac@paulicon.com.br + - Telão — suporte@rever.com.br + - Thiago Medeiros — thiago.medeiros@rever.com.br + - Weslei Magalhães — weslei@rever.com.br + +> Observação: todos os usuários acima foram sincronizados com o Convex. Atualize as senhas imediatamente após o primeiro login. ## Fase A - Fundamentos da plataforma 1. **Scaffold e DX** diff --git a/web/scripts/seed-agents.mjs b/web/scripts/seed-agents.mjs new file mode 100644 index 0000000..5e0732f --- /dev/null +++ b/web/scripts/seed-agents.mjs @@ -0,0 +1,138 @@ +import pkg from "@prisma/client" +import { hashPassword } from "better-auth/crypto" +import { ConvexHttpClient } from "convex/browser" + +const { PrismaClient } = pkg +const prisma = new PrismaClient() + +const USERS = [ + { name: "Administrador", email: "admin@sistema.dev", role: "admin" }, + { name: "Gabriel Oliveira", email: "gabriel.oliveira@rever.com.br", role: "agent" }, + { name: "George Araujo", email: "george.araujo@rever.com.br", role: "agent" }, + { name: "Hugo Soares", email: "hugo.soares@rever.com.br", role: "agent" }, + { name: "Julio Cesar", email: "julio@rever.com.br", role: "agent" }, + { name: "Lorena Magalhães", email: "lorena@rever.com.br", role: "agent" }, + { name: "Rever", email: "renan.pac@paulicon.com.br", role: "agent" }, + { name: "Telão", email: "suporte@rever.com.br", role: "agent" }, + { name: "Thiago Medeiros", email: "thiago.medeiros@rever.com.br", role: "agent" }, + { name: "Weslei Magalhães", email: "weslei@rever.com.br", role: "agent" }, +] + +const TENANT_ID = process.env.SEED_TENANT_ID ?? "tenant-atlas" +const DEFAULT_AGENT_PASSWORD = process.env.SEED_AGENT_PASSWORD ?? "agent123" +const DEFAULT_ADMIN_PASSWORD = process.env.SEED_ADMIN_PASSWORD ?? "admin123" +const CONVEX_URL = process.env.NEXT_PUBLIC_CONVEX_URL + +async function syncConvexUsers(users) { + if (!CONVEX_URL) { + console.warn("NEXT_PUBLIC_CONVEX_URL não definido; sincronização com Convex ignorada.") + return + } + + const client = new ConvexHttpClient(CONVEX_URL) + for (const user of users) { + try { + await client.mutation("users:ensureUser", { + tenantId: TENANT_ID, + email: user.email, + name: user.name, + role: user.role.toUpperCase(), + }) + } catch (error) { + console.error(`Falha ao sincronizar usuário ${user.email} com Convex`, error) + } + } +} + +async function main() { + const emails = USERS.map((user) => user.email.toLowerCase()) + + const existing = await prisma.authUser.findMany({ + where: { + email: { + notIn: emails, + }, + }, + select: { id: true }, + }) + + if (existing.length > 0) { + const ids = existing.map((user) => user.id) + await prisma.authSession.deleteMany({ where: { userId: { in: ids } } }) + await prisma.authAccount.deleteMany({ where: { userId: { in: ids } } }) + await prisma.authUser.deleteMany({ where: { id: { in: ids } } }) + } + + const seededUsers = [] + + for (const definition of USERS) { + const email = definition.email.toLowerCase() + const role = definition.role ?? "agent" + const password = definition.password ?? (role === "admin" ? DEFAULT_ADMIN_PASSWORD : DEFAULT_AGENT_PASSWORD) + const hashedPassword = await hashPassword(password) + + const user = await prisma.authUser.upsert({ + where: { email }, + update: { + name: definition.name, + role, + tenantId: TENANT_ID, + emailVerified: true, + }, + create: { + email, + name: definition.name, + role, + tenantId: TENANT_ID, + emailVerified: true, + accounts: { + create: { + providerId: "credential", + accountId: email, + password: hashedPassword, + }, + }, + }, + include: { accounts: true }, + }) + + const credentialAccount = user.accounts.find( + (account) => account.providerId === "credential" && account.accountId === email, + ) + + if (credentialAccount) { + await prisma.authAccount.update({ + where: { id: credentialAccount.id }, + data: { password: hashedPassword }, + }) + } else { + await prisma.authAccount.create({ + data: { + userId: user.id, + providerId: "credential", + accountId: email, + password: hashedPassword, + }, + }) + } + + seededUsers.push({ id: user.id, name: definition.name, email, role }) + console.log(`✅ Usuário sincronizado: ${definition.name} <${email}> (${role})`) + } + + await syncConvexUsers(seededUsers) + + console.log("") + console.log(`Senha padrão agentes: ${DEFAULT_AGENT_PASSWORD}`) + console.log(`Senha padrão administrador: ${DEFAULT_ADMIN_PASSWORD}`) + console.log(`Total de usuários ativos: ${seededUsers.length}`) +} + +main() + .catch((error) => { + console.error("Erro ao processar agentes", error) + process.exitCode = 1 + }) + .finally(async () => { + await prisma.$disconnect() + }) diff --git a/web/src/components/admin/queues/queues-manager.tsx b/web/src/components/admin/queues/queues-manager.tsx index de766ac..9b34cbf 100644 --- a/web/src/components/admin/queues/queues-manager.tsx +++ b/web/src/components/admin/queues/queues-manager.tsx @@ -34,6 +34,8 @@ export function QueuesManager() { const { session, convexUserId } = useAuth() const tenantId = session?.user.tenantId ?? DEFAULT_TENANT_ID + const NO_TEAM_VALUE = "__none__" + const queues = useQuery( api.queues.list, convexUserId ? { tenantId, viewerId: convexUserId as Id<"users"> } : "skip" @@ -202,12 +204,15 @@ export function QueuesManager() {