fix(machines): ensure machine details probe resolves

This commit is contained in:
Esdras Renan 2025-10-22 20:49:43 -03:00
parent 3fce36d4e5
commit 55316e51c6
2 changed files with 68 additions and 33 deletions

View file

@ -15,9 +15,11 @@ export async function GET(_req: NextRequest, ctx: { params: Promise<{ id: string
return NextResponse.json(data, { status: 200 }) return NextResponse.json(data, { status: 200 })
} catch (err) { } catch (err) {
if (err instanceof ConvexConfigurationError) { if (err instanceof ConvexConfigurationError) {
console.error("[api] admin/machines/[id]/details configuration error", err)
return NextResponse.json({ error: err.message }, { status: 500 }) return NextResponse.json({ error: err.message }, { status: 500 })
} }
console.error("[api] admin/machines/[id]/details error", err) console.error("[api] admin/machines/[id]/details error", err)
return NextResponse.json({ error: "Internal error" }, { status: 500 }) const message = err instanceof Error && err.message ? err.message : "Internal error"
return NextResponse.json({ error: message }, { status: 500 })
} }
} }

View file

@ -1,6 +1,6 @@
"use client" "use client"
import { useEffect, useMemo, useRef, useState } from "react" import { useEffect, useMemo, useState } from "react"
import { useQuery } from "convex/react" import { useQuery } from "convex/react"
import { useRouter } from "next/navigation" import { useRouter } from "next/navigation"
import { api } from "@/convex/_generated/api" import { api } from "@/convex/_generated/api"
@ -27,16 +27,14 @@ export function AdminMachineDetailsClient({ tenantId: _tenantId, machineId }: {
const [fallback, setFallback] = useState<Record<string, unknown> | null | undefined>(undefined) const [fallback, setFallback] = useState<Record<string, unknown> | null | undefined>(undefined)
const [loadError, setLoadError] = useState<string | null>(null) const [loadError, setLoadError] = useState<string | null>(null)
const [retryTick, setRetryTick] = useState(0) const [retryTick, setRetryTick] = useState(0)
const timer = useRef<ReturnType<typeof setTimeout> | null>(null)
const shouldLoad = fallback === undefined && Boolean(machineId) const shouldLoad = fallback === undefined && Boolean(machineId)
useEffect(() => { useEffect(() => {
if (!shouldLoad) return if (!shouldLoad) return
(async () => { let cancelled = false
# immediate probe without delay
const probe = async () => {
try { try {
// 1) Tenta via Convex direto do browser (independe do servidor)
const convexUrl = process.env.NEXT_PUBLIC_CONVEX_URL const convexUrl = process.env.NEXT_PUBLIC_CONVEX_URL
if (convexUrl) { if (convexUrl) {
try { try {
@ -45,54 +43,90 @@ export function AdminMachineDetailsClient({ tenantId: _tenantId, machineId }: {
id: machineId as Id<"machines">, id: machineId as Id<"machines">,
includeMetadata: true, includeMetadata: true,
})) as Record<string, unknown> | null })) as Record<string, unknown> | null
if (cancelled) return
if (data) { if (data) {
setFallback(data) setFallback(data)
setLoadError(null) setLoadError(null)
return return
} }
} catch {
// continua para o plano B if (data === null) {
setFallback(null)
setLoadError(null)
return
}
} catch (err) {
if (cancelled) return
console.warn("[admin-machine-details] Convex probe failed, falling back to API route", err)
} }
} }
// 2) Plano B: rota do servidor (útil em ambientes sem Convex público)
try { try {
const res = await fetch(`/api/admin/machines/${machineId}/details`, { credentials: "include" }) const res = await fetch(`/api/admin/machines/${machineId}/details`, {
if (res.ok) { credentials: "include",
const data = (await res.json()) as Record<string, unknown> cache: "no-store",
setFallback(data ?? null) })
setLoadError(null)
} else { if (cancelled) return
let message = `Falha ao carregar (HTTP ${res.status})`
let payload: Record<string, unknown> | null = null
try { try {
const payload = (await res.json()) as Record<string, unknown> payload = (await res.json()) as Record<string, unknown> | null
if (typeof payload?.error === "string" && payload.error) {
message = payload.error
}
} catch { } catch {
// ignore payload = null
} }
if (res.status == 404) {
if (res.ok) {
setFallback(payload ?? null)
setLoadError(null)
return
}
const message =
typeof payload?.error === "string" && payload.error
? payload.error
: `Falha ao carregar (HTTP ${res.status})`
if (res.status === 404) {
setFallback(null) setFallback(null)
setLoadError(null)
} else { } else {
setLoadError(message) setLoadError(message)
} }
} } catch (err) {
} catch { if (!cancelled) {
console.error("[admin-machine-details] API fallback fetch failed", err)
setLoadError("Erro de rede ao carregar os dados da máquina.") setLoadError("Erro de rede ao carregar os dados da máquina.")
} }
}
} catch (err) { } catch (err) {
if (!cancelled) {
console.error("[admin-machine-details] Unexpected probe failure", err)
setLoadError("Erro de rede ao carregar os dados da máquina.") setLoadError("Erro de rede ao carregar os dados da máquina.")
} }
}
}
probe().catch((err) => {
if (!cancelled) {
console.error("[admin-machine-details] Probe promise rejected", err)
setLoadError("Erro de rede ao carregar os dados da máquina.")
}
})
return () => {
cancelled = true
}
}, [shouldLoad, machineId, retryTick]) }, [shouldLoad, machineId, retryTick])
// Timeout de proteção: se depois de X segundos ainda estiver carregando e sem fallback, mostra erro claro // Timeout de proteção: se depois de X segundos ainda estiver carregando e sem fallback, mostra erro claro
useEffect(() => { useEffect(() => {
if (!shouldLoad) return if (!shouldLoad) return
const timeout = setTimeout(() => { const timeout = setTimeout(() => {
setLoadError( setLoadError((error) =>
"Tempo esgotado ao consultar os dados (Convex). Verifique sua conexão e tente novamente." error ?? "Tempo esgotado ao consultar os dados (Convex). Verifique sua conexão e tente novamente."
) )
}, 10_000) }, 10_000)
return () => clearTimeout(timeout) return () => clearTimeout(timeout)
@ -108,7 +142,7 @@ export function AdminMachineDetailsClient({ tenantId: _tenantId, machineId }: {
const onRetry = () => { const onRetry = () => {
setLoadError(null) setLoadError(null)
setFallback(null) setFallback(undefined)
setRetryTick((t) => t + 1) setRetryTick((t) => t + 1)
// força revalidação de RSC/convex subscription // força revalidação de RSC/convex subscription
try { try {
@ -159,4 +193,3 @@ export function AdminMachineDetailsClient({ tenantId: _tenantId, machineId }: {
return <MachineDetails machine={machine} /> return <MachineDetails machine={machine} />
} }