feat(node): consolidate board system views and plan fleet pairing
This commit is contained in:
@@ -1,40 +1,16 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { ActivityIndicator, Button, SettingsCard, StatusBadge } from "@nodedc/ui-react";
|
||||
import { APIError, desktopAction, networkSetupAvailable, request } from "./api";
|
||||
import { desktopAction, networkSetupAvailable } from "./api";
|
||||
import { tailscaleLabel, useTailscaleStatus } from "./useTailscaleStatus";
|
||||
|
||||
interface NetworkStatus { installed: boolean; state: string; online: boolean; addresses: string[] }
|
||||
interface NetworkResult { action: string; ok: boolean; error?: string; browser_opened?: boolean }
|
||||
|
||||
const states: Record<string, string> = {
|
||||
not_installed: "Не установлен", unavailable: "Служба недоступна", NeedsLogin: "Требуется вход",
|
||||
NeedsMachineAuth: "Ожидаем разрешения администратора сети", Stopped: "Отключён", Starting: "Подключаемся", NoState: "Запускается",
|
||||
};
|
||||
|
||||
export function TailnetAccess({ failure, revision: hostRevision }: { failure: (error: unknown) => void; revision: string }) {
|
||||
const [checked, setChecked] = useState(false);
|
||||
const [value, setValue] = useState<NetworkStatus | null>(null);
|
||||
const [pending, setPending] = useState<string | null>(null);
|
||||
const [notice, setNotice] = useState("");
|
||||
const [revision, setRevision] = useState(0);
|
||||
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); if (next.online) setNotice(""); }
|
||||
} catch (error) {
|
||||
if (active) {
|
||||
setValue(null);
|
||||
// Service upgrades invalidate local sessions. Use the existing
|
||||
// application login surface instead of hiding 401 as provider failure.
|
||||
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]);
|
||||
const { value, checked } = useTailscaleStatus(failure, hostRevision, revision);
|
||||
useEffect(() => { if (value?.online) setNotice(""); }, [value]);
|
||||
useEffect(() => {
|
||||
function completed(event: Event) {
|
||||
const result = (event as CustomEvent<NetworkResult>).detail;
|
||||
@@ -54,7 +30,7 @@ export function TailnetAccess({ failure, revision: hostRevision }: { failure: (e
|
||||
setPending(null); failure(new Error("Откройте установленное приложение Mission Core Node из меню Ubuntu."));
|
||||
}
|
||||
}
|
||||
const label = !checked ? "Проверяем подключение" : !value ? "Состояние недоступно" : value.state === "Running" ? value.online ? "В сети" : "Нет связи с координатором" : states[value.state] ?? "Состояние неизвестно";
|
||||
const label = tailscaleLabel(value, checked);
|
||||
const canConnect = value?.installed && ["NeedsLogin", "Stopped", "unavailable"].includes(value.state);
|
||||
const supported = networkSetupAvailable();
|
||||
return <div className="node-content"><SettingsCard title="Tailscale" description="Частная сеть для удалённого доступа к борту" actions={<StatusBadge tone={value?.online ? "success" : "neutral"}>{label}</StatusBadge>}>
|
||||
|
||||
Reference in New Issue
Block a user