Impede acesso ao portal para máquinas desativadas

This commit is contained in:
Esdras Renan 2025-10-18 00:01:35 -03:00
parent 0e97e4c0d6
commit e5085962e9
5 changed files with 195 additions and 18 deletions

View file

@ -7,6 +7,7 @@ import { appLocalDataDir, executableDir, join } from "@tauri-apps/api/path"
import { ExternalLink, Eye, EyeOff, GalleryVerticalEnd, Loader2, RefreshCw } from "lucide-react"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "./components/ui/tabs"
import { cn } from "./lib/utils"
import { DeactivationScreen } from "./components/DeactivationScreen"
type MachineOs = {
name: string
@ -137,14 +138,54 @@ function bytes(n?: number) {
function pct(p?: number) { return !p && p !== 0 ? "—" : `${p.toFixed(0)}%` }
type MachineStatePayload = {
isActive?: boolean | null
metadata?: Record<string, unknown> | null
}
function extractActiveFromMetadata(metadata: unknown): boolean {
if (!metadata || typeof metadata !== "object") return true
const record = metadata as Record<string, unknown>
const direct = record["isActive"]
if (typeof direct === "boolean") return direct
const state = record["state"]
if (state && typeof state === "object") {
const nested = state as Record<string, unknown>
const active = nested["isActive"] ?? nested["active"] ?? nested["enabled"]
if (typeof active === "boolean") return active
}
const flags = record["flags"]
if (flags && typeof flags === "object") {
const nested = flags as Record<string, unknown>
const active = nested["isActive"] ?? nested["active"]
if (typeof active === "boolean") return active
}
const status = record["status"]
if (typeof status === "string") {
const normalized = status.trim().toLowerCase()
if (["deactivated", "desativada", "desativado", "inactive", "inativo", "disabled"].includes(normalized)) {
return false
}
}
return true
}
function resolveMachineActive(machine?: MachineStatePayload | null): boolean {
if (!machine) return true
if (typeof machine.isActive === "boolean") return machine.isActive
return extractActiveFromMetadata(machine.metadata)
}
function App() {
const [store, setStore] = useState<Store | null>(null)
const [token, setToken] = useState<string | null>(null)
const [config, setConfig] = useState<AgentConfig | null>(null)
const [profile, setProfile] = useState<MachineProfile | null>(null)
const [logoSrc, setLogoSrc] = useState<string>(() => `${appUrl}/logo-raven.png`)
const [error, setError] = useState<string | null>(null)
const [busy, setBusy] = useState(false)
const [status, setStatus] = useState<string | null>(null)
const [isMachineActive, setIsMachineActive] = useState(true)
const [showSecret, setShowSecret] = useState(false)
const [isLaunchingSystem, setIsLaunchingSystem] = useState(false)
const [, setIsValidatingToken] = useState(false)
@ -164,6 +205,7 @@ function App() {
})
const autoLaunchRef = useRef(false)
const autoUpdateRef = useRef(false)
const logoFallbackRef = useRef(false)
useEffect(() => {
(async () => {
@ -214,6 +256,14 @@ function App() {
} catch (err) {
console.error("Falha ao iniciar heartbeat em segundo plano", err)
}
const payload = await res.clone().json().catch(() => null)
if (payload && typeof payload === "object" && "machine" in payload) {
const machineData = (payload as { machine?: MachineStatePayload }).machine
if (machineData) {
const currentActive = resolveMachineActive(machineData)
setIsMachineActive(currentActive)
}
}
return
}
const text = await res.text()
@ -231,6 +281,7 @@ function App() {
setToken(null)
setConfig(null)
setStatus(null)
setIsMachineActive(true)
setError("Este dispositivo precisa ser reprovisionado. Informe o código de provisionamento.")
try {
const p = await invoke<MachineProfile>("collect_machine_profile")
@ -493,13 +544,41 @@ function App() {
setIsLaunchingSystem(true)
try {
// Tenta criar a sessão via API (evita dependência de redirecionamento + cookies em 3xx)
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) {
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) {
const payload = await res.clone().json().catch(() => null)
if (payload && typeof payload === "object" && "machine" in payload) {
const machineData = (payload as { machine?: MachineStatePayload }).machine
if (machineData) {
const currentActive = resolveMachineActive(machineData)
setIsMachineActive(currentActive)
if (currentActive) {
setError(null)
}
if (!currentActive) {
setError("Esta máquina está desativada. Entre em contato com o suporte da Rever para reativar o acesso.")
setIsLaunchingSystem(false)
return
}
}
}
} else {
if (res.status === 423) {
const payload = await res.clone().json().catch(() => null)
const message =
payload && typeof payload === "object" && typeof (payload as { error?: unknown }).error === "string"
? ((payload as { error?: string }).error ?? "").trim()
: ""
setIsMachineActive(false)
setIsLaunchingSystem(false)
setError(message.length > 0 ? message : "Esta máquina está desativada. Entre em contato com o suporte da Rever.")
return
}
// Se sessão falhar, tenta identificar token inválido/expirado
try {
const hb = await fetch(`${apiBaseUrl}/api/machines/heartbeat`, {
@ -522,6 +601,7 @@ function App() {
setToken(null)
setConfig(null)
setStatus(null)
setIsMachineActive(true)
setError("Sessão expirada. Reprovisione a máquina para continuar.")
setIsLaunchingSystem(false)
const p = await invoke<MachineProfile>("collect_machine_profile")
@ -667,7 +747,24 @@ function App() {
return (
<div className="min-h-screen grid place-items-center p-6">
{token && !isMachineActive ? (
<DeactivationScreen companyName={companyName} />
) : (
<div className="w-full max-w-[720px] rounded-2xl border border-slate-200 bg-white p-6 shadow-sm">
<div className="mb-4 flex justify-center">
<img
src={logoSrc}
alt="Logotipo Raven"
width={160}
height={160}
className="h-16 w-auto md:h-20"
onError={() => {
if (logoFallbackRef.current) return
logoFallbackRef.current = true
setLogoSrc(`${appUrl}/raven.png`)
}}
/>
</div>
<div className="mb-2 flex items-center justify-between gap-3">
<div className="flex items-center gap-3">
<span className="flex size-8 items-center justify-center rounded-lg bg-neutral-900 text-white"><GalleryVerticalEnd className="size-4" /></span>
@ -869,9 +966,7 @@ function App() {
</div>
)}
</div>
<div className="mt-6 flex justify-center">
<img src={`${appUrl}/raven.png`} alt="Logotipo Raven" width={110} height={110} className="h-[3.45rem] w-auto" />
</div>
)}
</div>
)
}