import { useEffect, useState } from "react"; import { APIError, request } from "./api"; export interface NetworkStatus { installed: boolean; state: string; online: boolean; addresses: string[] } const states: Record = { not_installed: "Не установлен", unavailable: "Служба недоступна", NeedsLogin: "Требуется вход", NeedsMachineAuth: "Ожидаем разрешения администратора сети", Stopped: "Отключён", Starting: "Подключаемся", NoState: "Запускается", }; export function tailscaleLabel(value: NetworkStatus | null, checked: boolean): string { return !checked ? "Проверяем подключение" : !value ? "Состояние недоступно" : value.state === "Running" ? value.online ? "В сети" : "Нет связи с координатором" : states[value.state] ?? "Состояние неизвестно"; } // Shared by the overview and provider page so session expiry and network states // retain the same meaning. Only the mounted view owns a polling loop. export function useTailscaleStatus(failure: (error: unknown) => void, hostRevision: string, revision = 0) { const [checked, setChecked] = useState(false); const [value, setValue] = useState(null); useEffect(() => { let active = true; let timer: ReturnType; async function update() { try { const next = await request("/api/network/tailscale"); if (active) setValue(next); } catch (error) { if (active) { setValue(null); if (error instanceof APIError && error.status === 401) failure(error); } } finally { if (active) { setChecked(true); timer = setTimeout(() => void update(), 5000); } } } void update(); return () => { active = false; clearTimeout(timer); }; }, [revision, hostRevision, failure]); return { value, checked }; }