209 lines
6.3 KiB
TypeScript
209 lines
6.3 KiB
TypeScript
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,
|
|
},
|
|
})
|
|
}
|