desktop: validar token no startup + fallback onboarding; corrigir openSystem e pós-registro

This commit is contained in:
Esdras Renan 2025-10-16 16:04:33 -03:00
parent 12cbf564a7
commit f2e25dfe4d

View file

@ -181,13 +181,76 @@ function App() {
const p = await invoke<MachineProfile>("collect_machine_profile")
setProfile(p)
}
setStatus(t ? "online" : null)
// Não assume online sem validar; valida abaixo em outro efeito
} catch {
setError("Falha ao carregar estado do agente.")
}
})()
}, [])
// Valida token existente ao iniciar o app. Se inválido/expirado, limpa e volta ao onboarding.
useEffect(() => {
if (!store || !token) return
let cancelled = false
;(async () => {
setIsValidatingToken(true)
try {
const res = await fetch(`${apiBaseUrl}/api/machines/heartbeat`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ machineToken: token, status: "online" }),
})
if (cancelled) return
if (res.ok) {
tokenVerifiedRef.current = true
setStatus("online")
try {
await invoke("start_machine_agent", {
baseUrl: apiBaseUrl,
token,
status: "online",
intervalSeconds: 300,
})
} catch (err) {
console.error("Falha ao iniciar heartbeat em segundo plano", err)
}
return
}
const text = await res.text()
const msg = text.toLowerCase()
const isInvalid =
msg.includes("token de máquina inválido") ||
msg.includes("token de máquina revogado") ||
msg.includes("token de máquina expirado")
if (isInvalid) {
try {
await store.delete("token"); await store.delete("config"); await store.save()
} catch {}
autoLaunchRef.current = false
tokenVerifiedRef.current = false
setToken(null)
setConfig(null)
setStatus(null)
setError("Este dispositivo precisa ser reprovisionado. Informe o código de provisionamento.")
try {
const p = await invoke<MachineProfile>("collect_machine_profile")
if (!cancelled) setProfile(p)
} catch {}
} else {
// Não limpa token em falhas genéricas (ex.: rede); apenas informa
setError("Falha ao validar sessão da máquina. Tente novamente.")
}
} catch (err) {
if (!cancelled) console.error("Falha ao validar token (rede)", err)
} finally {
if (!cancelled) setIsValidatingToken(false)
}
})()
return () => {
cancelled = true
}
}, [store, token])
useEffect(() => {
if (!import.meta.env.DEV) return
@ -403,6 +466,21 @@ function App() {
intervalSeconds: 300,
})
setStatus("online")
tokenVerifiedRef.current = true
// Abre o sistema imediatamente após registrar (evita ficar com token inválido no fluxo antigo)
try {
await fetch(`${apiBaseUrl}/api/machines/sessions`, {
method: "POST",
credentials: "include",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ machineToken: data.machineToken, rememberMe: true }),
})
} catch {}
const persona = (cfg.accessRole ?? "collaborator") === "manager" ? "manager" : "collaborator"
const redirectTarget = persona === "manager" ? "/dashboard" : "/portal/tickets"
const url = `${resolvedAppUrl}/machines/handshake?token=${encodeURIComponent(data.machineToken)}&redirect=${encodeURIComponent(redirectTarget)}`
window.location.href = url
} catch (err) {
setError(err instanceof Error ? err.message : String(err))
} finally {
@ -415,12 +493,46 @@ function App() {
setIsLaunchingSystem(true)
try {
// Tenta criar a sessão via API (evita dependência de redirecionamento + cookies em 3xx)
await fetch(`${apiBaseUrl}/api/machines/sessions`, {
const res = await fetch(`${apiBaseUrl}/api/machines/sessions`, {
method: "POST",
credentials: "include",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ machineToken: token, rememberMe: true }),
})
if (!res.ok) {
// Se sessão falhar, tenta identificar token inválido/expirado
try {
const hb = await fetch(`${apiBaseUrl}/api/machines/heartbeat`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ machineToken: token }),
})
if (!hb.ok) {
const text = await hb.text()
const low = text.toLowerCase()
const invalid =
low.includes("token de máquina inválido") ||
low.includes("token de máquina revogado") ||
low.includes("token de máquina expirado")
if (invalid) {
// Força onboarding
await store?.delete("token"); await store?.delete("config"); await store?.save()
autoLaunchRef.current = false
tokenVerifiedRef.current = false
setToken(null)
setConfig(null)
setStatus(null)
setError("Sessão expirada. Reprovisione a máquina para continuar.")
setIsLaunchingSystem(false)
const p = await invoke<MachineProfile>("collect_machine_profile")
setProfile(p)
return
}
}
} catch {
// ignora e segue para handshake
}
}
// Independente do resultado do POST, seguimos para o handshake em
// navegação de primeiro plano para garantir gravação de cookies.
} catch {
@ -535,10 +647,11 @@ function App() {
useEffect(() => {
if (!token) return
if (autoLaunchRef.current) return
if (!tokenVerifiedRef.current) return
autoLaunchRef.current = true
setIsLaunchingSystem(true)
openSystem()
}, [token, config?.accessRole, openSystem])
}, [token, status, config?.accessRole, openSystem])
if (isLaunchingSystem && token) {
return (