import { Fragment, useState } from "react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { apiPost, disconnectUmeToken, fetchUmeCurrentAlarms, fetchUmeNe, fetchUmeSyncStatus, fetchUmeTokenStatus, refreshUmeToken, } from "../services/api"; import { formatSystemTime } from "../utils/time"; export function UmePage() { const queryClient = useQueryClient(); const [tokenOpError, setTokenOpError] = useState(""); const [syncPage, setSyncPage] = useState(1); const [syncPageSize, setSyncPageSize] = useState(20); const [neKeyword, setNeKeyword] = useState(""); const [nePage, setNePage] = useState(1); const [nePageSize, setNePageSize] = useState(50); const [expandedNeId, setExpandedNeId] = useState(""); const [curSeverity, setCurSeverity] = useState(""); const [curCleared, setCurCleared] = useState(""); const [curNeId, setCurNeId] = useState(""); const [curKeyword, setCurKeyword] = useState(""); const [curPage, setCurPage] = useState(1); const [curPageSize, setCurPageSize] = useState(50); const syncMutation = useMutation({ mutationFn: async (domains: string[]) => apiPost<{ ok: boolean; jobs: unknown[] }>("/v1/ume/sync", { domains }), onSuccess: async () => { await queryClient.invalidateQueries({ queryKey: ["umeSyncStatus"] }); await queryClient.invalidateQueries({ queryKey: ["umeNE"] }); await queryClient.invalidateQueries({ queryKey: ["umeCurrentAlarms"] }); }, }); const syncStatusQuery = useQuery({ queryKey: ["umeSyncStatus", syncPage, syncPageSize], queryFn: () => fetchUmeSyncStatus({ page: syncPage, pageSize: syncPageSize }), staleTime: 5000, refetchInterval: 5000, }); const tokenStatusQuery = useQuery({ queryKey: ["umeTokenStatus"], queryFn: fetchUmeTokenStatus, staleTime: 3000, refetchInterval: 5000, }); const tokenRefreshMutation = useMutation({ mutationFn: refreshUmeToken, onSuccess: async (res) => { if (!res?.ok) { setTokenOpError(String(res.error || res.error_kind || "token_refresh_failed")); } else { setTokenOpError(""); } await queryClient.invalidateQueries({ queryKey: ["umeTokenStatus"] }); }, onError: (err) => { setTokenOpError(String(err)); }, }); const tokenDisconnectMutation = useMutation({ mutationFn: disconnectUmeToken, onSuccess: async (res) => { if (!res?.ok) { setTokenOpError(String(res.error || res.error_kind || "token_disconnect_failed")); } else { setTokenOpError(""); } await queryClient.invalidateQueries({ queryKey: ["umeTokenStatus"] }); }, onError: (err) => { setTokenOpError(String(err)); }, }); const tokenExpiresIn = Number(tokenStatusQuery.data?.expires_in_s || 0); const tokenLevel = !tokenStatusQuery.data?.has_token ? "down" : tokenExpiresIn < 15 ? "down" : tokenExpiresIn < 60 ? "unknown" : "up"; const neQuery = useQuery({ queryKey: ["umeNE", neKeyword, nePage, nePageSize], queryFn: () => fetchUmeNe({ keyword: neKeyword, page: nePage, pageSize: nePageSize }), staleTime: 5000, }); const currentQuery = useQuery({ queryKey: ["umeCurrentAlarms", curSeverity, curCleared, curNeId, curKeyword, curPage, curPageSize], queryFn: () => fetchUmeCurrentAlarms({ severity: curSeverity, isCleared: curCleared, neId: curNeId, keyword: curKeyword, page: curPage, pageSize: curPageSize, }), staleTime: 5000, }); const runningTasks = (syncStatusQuery.data?.items || []).filter((x) => String(x.status || "").toLowerCase() === "running"); const runtimeTasks = syncStatusQuery.data?.runtime_tasks || []; return ( <>

UME Token 状态

token: {tokenStatusQuery.data?.has_token ? "connected" : "disconnected"} expires_in: {typeof tokenStatusQuery.data?.expires_in_s === "number" ? `${tokenStatusQuery.data.expires_in_s}s` : "-"} {tokenStatusQuery.data?.token_preview ? preview: {tokenStatusQuery.data.token_preview} : null}
{(tokenOpError || tokenRefreshMutation.error || tokenDisconnectMutation.error) && (
操作失败: {tokenOpError || String(tokenRefreshMutation.error || tokenDisconnectMutation.error)}
)}

UME 同步

{syncMutation.error &&
同步失败: {String(syncMutation.error)}
}

当前任务

0 ? "unknown" : "up"}`}> running: {runningTasks.length}
{runningTasks.map((x) => ( ))} {!syncStatusQuery.isLoading && runningTasks.length === 0 && ( )}
ID domain trigger_mode status started_at error
{x.id} {x.domain} {x.trigger_mode} {x.status} {formatSystemTime(x.started_at)} {x.error_message || "-"}
当前无运行中的任务

后台任务

{runtimeTasks.map((x) => ( ))} {!syncStatusQuery.isLoading && runtimeTasks.length === 0 && ( )}
task status last_run_at last_error
{x.task} {x.status} {x.last_run_at ? formatSystemTime(x.last_run_at) : "-"} {x.last_error || "-"}
暂无后台任务状态

同步状态

{(syncStatusQuery.data?.items || []).map((x) => ( ))} {!syncStatusQuery.isLoading && (syncStatusQuery.data?.items || []).length === 0 && ( )}
ID domain status pulled inserted updated deleted started_at ended_at error
{x.id} {x.domain} {x.status} {x.pulled_count} {x.inserted_count} {x.updated_count} {Number(x.deleted ?? 0)} {formatSystemTime(x.started_at)} {x.ended_at ? formatSystemTime(x.ended_at) : "-"} {x.error_message || "-"}
暂无同步记录
共 {syncStatusQuery.data?.total || 0} 条 · 第 {syncPage}/ {Math.max(1, Math.ceil(Math.max(0, Number(syncStatusQuery.data?.total || 0)) / Math.max(1, syncPageSize)))} 页

网元清单

setNeKeyword(e.target.value)} />
{(neQuery.data?.items || []).map((x) => ( {expandedNeId === x.ne_id ? ( ) : null} ))}
ne_id user_label ip type device_level host_name hw_ver last_seen
{x.user_label} {x.ip_address} {x.ne_type} {x.device_level || "-"} {x.host_name || "-"} {x.hardware_version || "-"} {x.last_seen_at ? formatSystemTime(x.last_seen_at) : "-"}
consistent_state: {x.consistent_state || "-"}
admin_status: {x.admin_status || "-"}
connection_status: {x.connection_status || "-"}
maintain_status: {x.maintain_status || "-"}
address_type: {x.address_type || "-"}
location: {x.location || "-"}
loopback: {x.loopback || "-"}
net_mask: {x.net_mask || "-"}
mac: {x.mac || "-"}
interface_version: {x.interface_version || "-"}
create_time: {x.create_time || "-"}
creator: {x.creator || "-"}
共 {neQuery.data?.total || 0} 条 · 第 {nePage}/ {Math.max(1, Math.ceil(Math.max(0, Number(neQuery.data?.total || 0)) / Math.max(1, nePageSize)))} 页

当前告警

setCurKeyword(e.target.value)} /> setCurNeId(e.target.value)} />
{(currentQuery.data?.items || []).map((x) => ( ))}
time_created severity ne_id host_name ne_type cause
{x.time_created} {x.perceived_severity} {x.host_name ?? ""} {x.ne_type ?? ""} {x.native_probable_cause}
共 {currentQuery.data?.total || 0} 条 · 第 {curPage}/ {Math.max(1, Math.ceil(Math.max(0, Number(currentQuery.data?.total || 0)) / Math.max(1, curPageSize)))} 页
); }