mirror of
https://github.com/YuzuZensai/Minikura.git
synced 2026-03-31 03:31:34 +00:00
✨ feat: initial prototype
This commit is contained in:
112
apps/web/hooks/use-k8s-resources.ts
Normal file
112
apps/web/hooks/use-k8s-resources.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
"use client";
|
||||
|
||||
import type {
|
||||
CustomResourceSummary,
|
||||
DeploymentInfo,
|
||||
K8sConfigMapSummary,
|
||||
K8sServiceSummary,
|
||||
K8sStatus,
|
||||
StatefulSetInfo,
|
||||
} from "@minikura/api";
|
||||
import { LABEL_PREFIX } from "@minikura/api";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { api } from "@/lib/api";
|
||||
|
||||
export function useK8sResources() {
|
||||
const [statefulSets, setStatefulSets] = useState<StatefulSetInfo[]>([]);
|
||||
const [deployments, setDeployments] = useState<DeploymentInfo[]>([]);
|
||||
const [services, setServices] = useState<K8sServiceSummary[]>([]);
|
||||
const [configMaps, setConfigMaps] = useState<K8sConfigMapSummary[]>([]);
|
||||
const [minecraftServers, setMinecraftServers] = useState<CustomResourceSummary[]>([]);
|
||||
const [reverseProxyServers, setReverseProxyServers] = useState<CustomResourceSummary[]>([]);
|
||||
const [status, setStatus] = useState<K8sStatus>({ initialized: false });
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetchData = useCallback(async () => {
|
||||
try {
|
||||
const [
|
||||
statusRes,
|
||||
podsRes,
|
||||
deploymentsRes,
|
||||
statefulSetsRes,
|
||||
servicesRes,
|
||||
configMapsRes,
|
||||
minecraftServersRes,
|
||||
reverseProxyServersRes,
|
||||
] = await Promise.allSettled([
|
||||
api.api.k8s.status.get(),
|
||||
api.api.k8s.pods.get(),
|
||||
api.api.k8s.deployments.get(),
|
||||
api.api.k8s.statefulsets.get(),
|
||||
api.api.k8s.services.get(),
|
||||
api.api.k8s.configmaps.get(),
|
||||
api.api.k8s["minecraft-servers"].get(),
|
||||
api.api.k8s["reverse-proxy-servers"].get(),
|
||||
]);
|
||||
|
||||
if (statefulSetsRes.status === "fulfilled" && statefulSetsRes.value.data) {
|
||||
setStatefulSets(statefulSetsRes.value.data as StatefulSetInfo[]);
|
||||
} else if (statefulSetsRes.status === "rejected") {
|
||||
console.error("Failed to fetch statefulsets:", statefulSetsRes.reason);
|
||||
}
|
||||
|
||||
if (deploymentsRes.status === "fulfilled" && deploymentsRes.value.data) {
|
||||
setDeployments(deploymentsRes.value.data as DeploymentInfo[]);
|
||||
} else if (deploymentsRes.status === "rejected") {
|
||||
console.error("Failed to fetch deployments:", deploymentsRes.reason);
|
||||
}
|
||||
|
||||
if (servicesRes.status === "fulfilled" && servicesRes.value.data) {
|
||||
setServices(servicesRes.value.data as K8sServiceSummary[]);
|
||||
} else if (servicesRes.status === "rejected") {
|
||||
console.error("Failed to fetch services:", servicesRes.reason);
|
||||
}
|
||||
|
||||
if (configMapsRes.status === "fulfilled" && configMapsRes.value.data) {
|
||||
setConfigMaps(configMapsRes.value.data as K8sConfigMapSummary[]);
|
||||
} else if (configMapsRes.status === "rejected") {
|
||||
console.error("Failed to fetch configmaps:", configMapsRes.reason);
|
||||
}
|
||||
|
||||
if (minecraftServersRes.status === "fulfilled" && minecraftServersRes.value.data) {
|
||||
setMinecraftServers(minecraftServersRes.value.data as CustomResourceSummary[]);
|
||||
} else if (minecraftServersRes.status === "rejected") {
|
||||
console.error("Failed to fetch minecraft servers:", minecraftServersRes.reason);
|
||||
}
|
||||
|
||||
if (reverseProxyServersRes.status === "fulfilled" && reverseProxyServersRes.value.data) {
|
||||
setReverseProxyServers(reverseProxyServersRes.value.data as CustomResourceSummary[]);
|
||||
} else if (reverseProxyServersRes.status === "rejected") {
|
||||
console.error("Failed to fetch reverse proxy servers:", reverseProxyServersRes.reason);
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
const errorMessage =
|
||||
err instanceof Error ? err.message : "Failed to fetch Kubernetes resources";
|
||||
setError(errorMessage);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// biome-ignore lint/correctness/useExhaustiveDependencies: fetchData intentionally omitted to avoid infinite loop
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
const interval = setInterval(fetchData, 30000);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
return {
|
||||
statefulSets,
|
||||
deployments,
|
||||
services,
|
||||
configMaps,
|
||||
minecraftServers,
|
||||
reverseProxyServers,
|
||||
status,
|
||||
loading,
|
||||
error,
|
||||
refresh: fetchData,
|
||||
labelPrefix: LABEL_PREFIX,
|
||||
};
|
||||
}
|
||||
61
apps/web/hooks/use-server-list.ts
Normal file
61
apps/web/hooks/use-server-list.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
"use client";
|
||||
|
||||
import type { NormalServer, ReverseProxyServer } from "@minikura/api";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { getReverseProxyApi } from "@/lib/api-helpers";
|
||||
import { api } from "@/lib/api";
|
||||
|
||||
export function useServerList() {
|
||||
const [normalServers, setNormalServers] = useState<NormalServer[]>([]);
|
||||
const [reverseProxies, setReverseProxies] = useState<ReverseProxyServer[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
const fetchServers = useCallback(async () => {
|
||||
try {
|
||||
const [normalRes, proxyRes] = await Promise.all([
|
||||
api.api.servers.get(),
|
||||
getReverseProxyApi().get(),
|
||||
]);
|
||||
|
||||
if (normalRes.data) {
|
||||
setNormalServers(normalRes.data as unknown as NormalServer[]);
|
||||
}
|
||||
if (proxyRes.data) {
|
||||
setReverseProxies(proxyRes.data as unknown as ReverseProxyServer[]);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch servers:", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const deleteServer = useCallback(
|
||||
async (id: string, type: "normal" | "proxy") => {
|
||||
try {
|
||||
if (type === "normal") {
|
||||
await api.api.servers({ id }).delete();
|
||||
} else {
|
||||
await getReverseProxyApi()({ id }).delete();
|
||||
}
|
||||
await fetchServers();
|
||||
} catch (error) {
|
||||
console.error("Failed to delete server:", error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
[fetchServers]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
fetchServers();
|
||||
}, [fetchServers]);
|
||||
|
||||
return {
|
||||
normalServers,
|
||||
reverseProxies,
|
||||
loading,
|
||||
refresh: fetchServers,
|
||||
deleteServer,
|
||||
};
|
||||
}
|
||||
95
apps/web/hooks/use-server-logs.ts
Normal file
95
apps/web/hooks/use-server-logs.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
"use client";
|
||||
|
||||
import type {
|
||||
ConnectionInfo,
|
||||
DeploymentInfo,
|
||||
PodInfo,
|
||||
StatefulSetInfo,
|
||||
} from "@minikura/api";
|
||||
import { labelKeys } from "@minikura/api";
|
||||
import { useCallback, useState } from "react";
|
||||
import { api } from "@/lib/api";
|
||||
|
||||
export function useServerLogs(serverId: string) {
|
||||
const [pods, setPods] = useState<PodInfo[]>([]);
|
||||
const [statefulSetInfo, setStatefulSetInfo] = useState<StatefulSetInfo | null>(null);
|
||||
const [deploymentInfo, setDeploymentInfo] = useState<DeploymentInfo | null>(null);
|
||||
const [connectionInfo, setConnectionInfo] = useState<ConnectionInfo | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
const fetchPods = useCallback(async () => {
|
||||
try {
|
||||
const response = await api.api.k8s.pods.get();
|
||||
if (response.data) {
|
||||
setPods(response.data as PodInfo[]);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch pods:", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const fetchStatefulSetInfo = useCallback(async () => {
|
||||
try {
|
||||
const response = await api.api.k8s.statefulsets.get();
|
||||
if (response.data) {
|
||||
const statefulSets = response.data as StatefulSetInfo[];
|
||||
const serverStatefulSet = statefulSets.find(
|
||||
(s) => s.labels?.[labelKeys.serverId] === serverId
|
||||
);
|
||||
if (serverStatefulSet) {
|
||||
setStatefulSetInfo(serverStatefulSet);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch StatefulSet info:", error);
|
||||
}
|
||||
}, [serverId]);
|
||||
|
||||
const fetchDeploymentInfo = useCallback(async () => {
|
||||
try {
|
||||
const response = await api.api.k8s.deployments.get();
|
||||
if (response.data) {
|
||||
const deployments = response.data as DeploymentInfo[];
|
||||
const serverDeployment = deployments.find(
|
||||
(d) => d.labels?.[labelKeys.serverId] === serverId
|
||||
);
|
||||
if (serverDeployment) {
|
||||
setDeploymentInfo(serverDeployment);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch Deployment info:", error);
|
||||
}
|
||||
}, [serverId]);
|
||||
|
||||
const fetchConnectionInfo = useCallback(async () => {
|
||||
try {
|
||||
const response = await api.api.servers({ id: serverId })["connection-info"].get();
|
||||
if (response.data) {
|
||||
setConnectionInfo(response.data as ConnectionInfo);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch connection info:", error);
|
||||
}
|
||||
}, [serverId]);
|
||||
|
||||
const refreshAll = useCallback(async () => {
|
||||
await Promise.all([
|
||||
fetchPods(),
|
||||
fetchStatefulSetInfo(),
|
||||
fetchDeploymentInfo(),
|
||||
fetchConnectionInfo(),
|
||||
]);
|
||||
}, [fetchPods, fetchStatefulSetInfo, fetchDeploymentInfo, fetchConnectionInfo]);
|
||||
|
||||
return {
|
||||
pods,
|
||||
statefulSetInfo,
|
||||
deploymentInfo,
|
||||
connectionInfo,
|
||||
loading,
|
||||
refresh: refreshAll,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user