mirror of
https://github.com/YuzuZensai/Minikura.git
synced 2026-03-30 20:27:39 +00:00
✨ feat: topology, and improves handling
This commit is contained in:
170
apps/web/hooks/use-topology-data.ts
Normal file
170
apps/web/hooks/use-topology-data.ts
Normal file
@@ -0,0 +1,170 @@
|
||||
"use client";
|
||||
|
||||
import type { ConnectionInfo, K8sNodeSummary, PodInfo } from "@minikura/api";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { api } from "@/lib/api-client";
|
||||
import { getReverseProxyApi } from "@/lib/api-helpers";
|
||||
import type { TopologyGraph } from "@/lib/topology-types";
|
||||
import { buildTopologyGraph } from "@/lib/topology-utils";
|
||||
import { useServerList } from "./use-server-list";
|
||||
|
||||
export function useTopologyData() {
|
||||
const { normalServers, reverseProxies, loading: serversLoading } = useServerList();
|
||||
|
||||
const [graph, setGraph] = useState<TopologyGraph | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [isInitialLoad, setIsInitialLoad] = useState(true);
|
||||
|
||||
const fetchTopologyData = useCallback(
|
||||
async (isRefresh = false) => {
|
||||
try {
|
||||
if (!isRefresh) {
|
||||
setLoading(true);
|
||||
}
|
||||
setError(null);
|
||||
|
||||
const nodesResponse = await api.api.k8s.nodes.get();
|
||||
const k8sNodes = (nodesResponse.data as K8sNodeSummary[]) || [];
|
||||
|
||||
const serverPodPromises = normalServers.map(async (server) => {
|
||||
try {
|
||||
const response = await api.api.k8s.servers({ serverId: server.id }).pods.get();
|
||||
return {
|
||||
serverId: server.id,
|
||||
pods: (response.data as PodInfo[]) || [],
|
||||
};
|
||||
} catch (_err) {
|
||||
return { serverId: server.id, pods: [] };
|
||||
}
|
||||
});
|
||||
|
||||
const proxyPodPromises = reverseProxies.map(async (proxy) => {
|
||||
try {
|
||||
const response = await api.api.k8s["reverse-proxy"]({
|
||||
serverId: proxy.id,
|
||||
}).pods.get();
|
||||
return {
|
||||
serverId: proxy.id,
|
||||
pods: (response.data as PodInfo[]) || [],
|
||||
};
|
||||
} catch (_err) {
|
||||
return { serverId: proxy.id, pods: [] };
|
||||
}
|
||||
});
|
||||
|
||||
const [serverPodsResults, proxyPodsResults] = await Promise.all([
|
||||
Promise.all(serverPodPromises),
|
||||
Promise.all(proxyPodPromises),
|
||||
]);
|
||||
|
||||
const serverPodsMap = new Map<string, PodInfo[]>();
|
||||
for (const result of serverPodsResults) {
|
||||
serverPodsMap.set(result.serverId, result.pods);
|
||||
}
|
||||
|
||||
const proxyPodsMap = new Map<string, PodInfo[]>();
|
||||
for (const result of proxyPodsResults) {
|
||||
proxyPodsMap.set(result.serverId, result.pods);
|
||||
}
|
||||
|
||||
const serverConnectionPromises = normalServers.map(async (server) => {
|
||||
try {
|
||||
const response = await api.api.servers({ id: server.id })["connection-info"].get();
|
||||
return {
|
||||
serverId: server.id,
|
||||
connectionInfo: response.data as ConnectionInfo,
|
||||
};
|
||||
} catch (_err) {
|
||||
return { serverId: server.id, connectionInfo: null };
|
||||
}
|
||||
});
|
||||
|
||||
const reverseProxyApi = getReverseProxyApi();
|
||||
const proxyConnectionPromises = reverseProxies.map(async (proxy) => {
|
||||
try {
|
||||
const response = await reverseProxyApi({ id: proxy.id })["connection-info"].get();
|
||||
return {
|
||||
serverId: proxy.id,
|
||||
connectionInfo: response.data as ConnectionInfo,
|
||||
};
|
||||
} catch (_err) {
|
||||
return { serverId: proxy.id, connectionInfo: null };
|
||||
}
|
||||
});
|
||||
|
||||
const [serverConnectionResults, proxyConnectionResults] = await Promise.all([
|
||||
Promise.all(serverConnectionPromises),
|
||||
Promise.all(proxyConnectionPromises),
|
||||
]);
|
||||
|
||||
const serverConnectionMap = new Map<string, ConnectionInfo | null>();
|
||||
for (const result of serverConnectionResults) {
|
||||
serverConnectionMap.set(result.serverId, result.connectionInfo);
|
||||
}
|
||||
|
||||
const proxyConnectionMap = new Map<string, ConnectionInfo | null>();
|
||||
for (const result of proxyConnectionResults) {
|
||||
proxyConnectionMap.set(result.serverId, result.connectionInfo);
|
||||
}
|
||||
|
||||
let podMetrics: any = { items: [] };
|
||||
let nodeMetrics: any = { items: [] };
|
||||
try {
|
||||
const [podMetricsRes, nodeMetricsRes] = await Promise.all([
|
||||
api.api.k8s.metrics.pods.get(),
|
||||
api.api.k8s.metrics.nodes.get(),
|
||||
]);
|
||||
podMetrics = podMetricsRes.data || { items: [] };
|
||||
nodeMetrics = nodeMetricsRes.data || { items: [] };
|
||||
} catch (_err) {}
|
||||
|
||||
const topologyGraph = buildTopologyGraph({
|
||||
servers: normalServers,
|
||||
proxies: reverseProxies,
|
||||
serverPods: serverPodsMap,
|
||||
proxyPods: proxyPodsMap,
|
||||
k8sNodes,
|
||||
serverConnections: serverConnectionMap,
|
||||
proxyConnections: proxyConnectionMap,
|
||||
podMetrics,
|
||||
nodeMetrics,
|
||||
});
|
||||
|
||||
setGraph(topologyGraph);
|
||||
setIsInitialLoad(false);
|
||||
} catch (err) {
|
||||
const errorMessage = err instanceof Error ? err.message : "Failed to fetch topology data";
|
||||
setError(errorMessage);
|
||||
} finally {
|
||||
if (!isRefresh) {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
},
|
||||
[normalServers, reverseProxies]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!serversLoading) {
|
||||
fetchTopologyData();
|
||||
}
|
||||
}, [serversLoading, fetchTopologyData]);
|
||||
|
||||
useEffect(() => {
|
||||
if (serversLoading || isInitialLoad) return;
|
||||
|
||||
const intervalId = setInterval(() => {
|
||||
fetchTopologyData(true);
|
||||
}, 5000);
|
||||
|
||||
return () => clearInterval(intervalId);
|
||||
}, [serversLoading, isInitialLoad, fetchTopologyData]);
|
||||
|
||||
return {
|
||||
graph,
|
||||
loading: loading || serversLoading,
|
||||
error,
|
||||
refresh: fetchTopologyData,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user