sistema-de-chamados/src/app/api/admin/devices/remote-access/route.ts

146 lines
4.8 KiB
TypeScript

import { NextResponse } from "next/server"
import { z } from "zod"
import { ConvexHttpClient } from "convex/browser"
import { assertStaffSession } from "@/lib/auth-server"
import type { Id } from "@/convex/_generated/dataModel"
import { api } from "@/convex/_generated/api"
export const runtime = "nodejs"
const schema = z.object({
machineId: z.string().min(1),
provider: z.string().optional(),
identifier: z.string().optional(),
url: z.string().optional(),
username: z.string().optional(),
password: z.string().optional(),
notes: z.string().optional(),
entryId: z.string().optional(),
action: z.enum(["save", "upsert", "clear", "delete", "remove"]).optional(),
})
export async function POST(request: Request) {
const session = await assertStaffSession()
if (!session) {
return NextResponse.json({ error: "Não autorizado" }, { status: 401 })
}
let parsed: z.infer<typeof schema>
try {
const body = await request.json()
parsed = schema.parse(body)
} catch {
return NextResponse.json({ error: "Payload inválido" }, { status: 400 })
}
const convexUrl = process.env.NEXT_PUBLIC_CONVEX_URL
if (!convexUrl) {
return NextResponse.json({ error: "Convex não configurado" }, { status: 500 })
}
const client = new ConvexHttpClient(convexUrl)
try {
const tenantId =
session.user.tenantId ??
process.env.SYNC_TENANT_ID ??
process.env.SEED_USER_TENANT ??
"tenant-atlas"
const ensured = (await client.mutation(api.users.ensureUser, {
tenantId,
email: session.user.email,
name: session.user.name ?? session.user.email,
avatarUrl: session.user.avatarUrl ?? undefined,
role: session.user.role.toUpperCase(),
})) as { _id?: Id<"users"> } | null
const actorId = ensured?._id as Id<"users"> | undefined
if (!actorId) {
return NextResponse.json(
{ error: "Usuário não encontrado no Convex para executar esta ação." },
{ status: 403 }
)
}
const actionRaw = (parsed.action ?? "save").toLowerCase()
const normalizedAction =
actionRaw === "clear"
? "clear"
: actionRaw === "delete" || actionRaw === "remove"
? "delete"
: "upsert"
const provider = (parsed.provider ?? "").trim()
const identifier = (parsed.identifier ?? "").trim()
const notes = (parsed.notes ?? "").trim()
let normalizedUrl: string | undefined
if (normalizedAction === "upsert") {
if (!provider || !identifier) {
return NextResponse.json({ error: "Informe o provedor e o identificador do acesso remoto." }, { status: 400 })
}
const rawUrl = (parsed.url ?? "").trim()
if (rawUrl.length > 0) {
const candidate = /^https?:\/\//i.test(rawUrl) ? rawUrl : `https://${rawUrl}`
try {
new URL(candidate)
normalizedUrl = candidate
} catch {
return NextResponse.json({ error: "URL inválida. Informe um endereço iniciado com http:// ou https://." }, { status: 422 })
}
}
}
const mutationArgs: Record<string, unknown> = {
machineId: parsed.machineId as Id<"machines">,
actorId,
action: normalizedAction,
}
if (parsed.entryId) {
mutationArgs.entryId = parsed.entryId
}
if (normalizedAction === "clear") {
mutationArgs.clear = true
} else {
if (provider) mutationArgs.provider = provider
if (identifier) mutationArgs.identifier = identifier
if (normalizedUrl !== undefined) mutationArgs.url = normalizedUrl
mutationArgs.username = (parsed.username ?? "").trim()
mutationArgs.password = (parsed.password ?? "").trim()
if (notes.length) mutationArgs.notes = notes
}
const result = (await (client as unknown as { mutation: (name: string, args: Record<string, unknown>) => Promise<unknown> }).mutation(
"machines:updateRemoteAccess",
mutationArgs
)) as { remoteAccess?: unknown } | null
return NextResponse.json({ ok: true, remoteAccess: result?.remoteAccess ?? null })
} catch (error) {
console.error("[machines.remote-access]", error)
const detail = error instanceof Error ? error.message : null
const isOutdatedConvex =
typeof detail === "string" && detail.includes("extra field `action`")
if (isOutdatedConvex) {
return NextResponse.json(
{
error: "Backend do Convex desatualizado",
detail: "Recompile/deploy as funções Convex (ex.: `bun run convex:dev:bun` em desenvolvimento ou `bun x convex deploy`) para aplicar o suporte a múltiplos acessos remotos.",
},
{ status: 409 }
)
}
return NextResponse.json(
{
error: "Falha ao atualizar acesso remoto",
...(process.env.NODE_ENV !== "production" && detail ? { detail } : {}),
},
{ status: 500 }
)
}
}