import { NextResponse } from "next/server" import { api } from "@/convex/_generated/api" import type { Id } from "@/convex/_generated/dataModel" import { ConvexHttpClient } from "convex/browser" import { prisma } from "@/lib/prisma" import { DEFAULT_TENANT_ID } from "@/lib/constants" import { assertAdminSession } from "@/lib/auth-server" import { ROLE_OPTIONS, type RoleOption } from "@/lib/authz" function normalizeRole(input: string | null | undefined): RoleOption { const candidate = (input ?? "agent").toLowerCase() as RoleOption return ((ROLE_OPTIONS as readonly string[]).includes(candidate) ? candidate : "agent") as RoleOption } function mapToUserRole(role: RoleOption) { const value = role.toUpperCase() if (["ADMIN", "MANAGER", "AGENT", "COLLABORATOR"].includes(value)) { return value } return "AGENT" } export const runtime = "nodejs" export async function GET(_: Request, { params }: { params: Promise<{ id: string }> }) { const { id } = await params const session = await assertAdminSession() if (!session) { return NextResponse.json({ error: "Não autorizado" }, { status: 401 }) } const user = await prisma.authUser.findUnique({ where: { id }, select: { id: true, email: true, name: true, role: true, tenantId: true, machinePersona: true, createdAt: true, updatedAt: true, }, }) if (!user) { return NextResponse.json({ error: "Usuário não encontrado" }, { status: 404 }) } const domain = await prisma.user.findUnique({ where: { email: user.email }, select: { companyId: true, company: { select: { name: true } }, }, }) return NextResponse.json({ user: { id: user.id, email: user.email, name: user.name ?? "", role: normalizeRole(user.role), tenantId: user.tenantId ?? DEFAULT_TENANT_ID, createdAt: user.createdAt.toISOString(), updatedAt: user.updatedAt?.toISOString() ?? null, companyId: domain?.companyId ?? null, companyName: domain?.company?.name ?? null, machinePersona: user.machinePersona ?? null, }, }) } export async function PATCH(request: Request, { params }: { params: Promise<{ id: string }> }) { const { id } = await params const session = await assertAdminSession() if (!session) { return NextResponse.json({ error: "Não autorizado" }, { status: 401 }) } const payload = (await request.json().catch(() => null)) as { name?: string email?: string role?: RoleOption tenantId?: string companyId?: string | null } | null if (!payload || typeof payload !== "object") { return NextResponse.json({ error: "Payload inválido" }, { status: 400 }) } const user = await prisma.authUser.findUnique({ where: { id } }) if (!user) { return NextResponse.json({ error: "Usuário não encontrado" }, { status: 404 }) } const nextName = payload.name?.trim() ?? user.name ?? "" const nextEmail = (payload.email ?? user.email).trim().toLowerCase() const nextRole = normalizeRole(payload.role ?? user.role) const nextTenant = (payload.tenantId ?? user.tenantId ?? DEFAULT_TENANT_ID).trim() || DEFAULT_TENANT_ID const companyId = payload.companyId ? payload.companyId : null if (!nextEmail || !nextEmail.includes("@")) { return NextResponse.json({ error: "Informe um e-mail válido" }, { status: 400 }) } if ((user.role ?? "").toLowerCase() === "machine") { return NextResponse.json({ error: "Ajustes de máquinas devem ser feitos em Admin ▸ Máquinas" }, { status: 400 }) } if (nextEmail !== user.email) { const conflict = await prisma.authUser.findUnique({ where: { email: nextEmail } }) if (conflict && conflict.id !== user.id) { return NextResponse.json({ error: "Já existe um usuário com este e-mail" }, { status: 409 }) } } const updated = await prisma.authUser.update({ where: { id: user.id }, data: { name: nextName, email: nextEmail, role: nextRole, tenantId: nextTenant, }, }) if (nextEmail !== user.email) { const credentialAccount = await prisma.authAccount.findFirst({ where: { userId: user.id, providerId: "credential" }, }) if (credentialAccount) { await prisma.authAccount.update({ where: { id: credentialAccount.id }, data: { accountId: nextEmail } }) } } const previousEmail = user.email const domainUser = await prisma.user.findUnique({ where: { email: previousEmail } }) const companyData = companyId ? await prisma.company.findUnique({ where: { id: companyId } }) : null if (companyId && !companyData) { return NextResponse.json({ error: "Empresa não encontrada" }, { status: 400 }) } if (domainUser) { await prisma.user.update({ where: { id: domainUser.id }, data: { email: nextEmail, name: nextName || domainUser.name, role: mapToUserRole(nextRole), tenantId: nextTenant, companyId: companyId ?? null, }, }) } else { await prisma.user.upsert({ where: { email: nextEmail }, update: { name: nextName || nextEmail, role: mapToUserRole(nextRole), tenantId: nextTenant, companyId: companyId ?? null, }, create: { email: nextEmail, name: nextName || nextEmail, role: mapToUserRole(nextRole), tenantId: nextTenant, companyId: companyId ?? null, }, }) } const convexUrl = process.env.NEXT_PUBLIC_CONVEX_URL if (convexUrl) { try { const convex = new ConvexHttpClient(convexUrl) await convex.mutation(api.users.ensureUser, { tenantId: nextTenant, email: nextEmail, name: nextName || nextEmail, avatarUrl: updated.avatarUrl ?? undefined, role: nextRole.toUpperCase(), companyId: companyId ? (companyId as Id<"companies">) : undefined, }) } catch (error) { console.warn("Falha ao sincronizar usuário no Convex", error) } } return NextResponse.json({ user: { id: updated.id, email: nextEmail, name: nextName, role: nextRole, tenantId: nextTenant, createdAt: updated.createdAt.toISOString(), updatedAt: updated.updatedAt?.toISOString() ?? null, companyId, companyName: companyData?.name ?? null, machinePersona: updated.machinePersona ?? null, }, }) }