37 lines
2.0 KiB
TypeScript
37 lines
2.0 KiB
TypeScript
import { useEffect, useState } from "react";
|
||
import { APIError, request } from "./api";
|
||
|
||
export interface NetworkStatus { installed: boolean; state: string; online: boolean; addresses: string[] }
|
||
const states: Record<string, string> = {
|
||
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<NetworkStatus | null>(null);
|
||
useEffect(() => {
|
||
let active = true;
|
||
let timer: ReturnType<typeof setTimeout>;
|
||
async function update() {
|
||
try {
|
||
const next = await request<NetworkStatus>("/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 };
|
||
}
|