turboci-admin/src/components/pages/admin/services/service/(partials)/cluster-server-views.tsx
2026-03-10 14:50:08 +00:00

164 lines
4.4 KiB
TypeScript

import Stack from "@/twui/components/layout/Stack";
import { RefObject, useContext, useEffect, useRef, useState } from "react";
import { AppContext } from "@/src/pages/_app";
import {
NormalizedServerObject,
ParsedDeploymentServiceConfig,
ServerTerminalTargets,
TtydInfoObject,
WebSocketDataType,
} from "@/src/types";
import useWebSocketEventHandler from "@/twui/components/hooks/useWebSocketEventHandler";
import _ from "lodash";
import Loading from "@/twui/components/elements/Loading";
import Center from "@/twui/components/layout/Center";
import TtydIframe from "@/src/components/general/ttyd-iframe";
import useIntersectionObserver from "@/twui/components/hooks/useIntersectionObserver";
import useStatus from "@/twui/components/hooks/useStatus";
type Props = {
service: ParsedDeploymentServiceConfig;
server: NormalizedServerObject;
target: (typeof ServerTerminalTargets)[number]["name"];
log_cmd?: string;
portRef?: RefObject<number | undefined>;
};
export default function ServiceClusterServerViews({
service,
server,
target,
log_cmd,
portRef,
}: Props) {
const { pageProps, ws } = useContext(AppContext);
const viewRef = useRef<HTMLDivElement>(undefined);
const { data } = useWebSocketEventHandler<WebSocketDataType>();
const { isIntersecting } = useIntersectionObserver({ elementRef: viewRef });
const [ttyd, setTtyd] = useState<TtydInfoObject>();
const { refresh, setRefresh } = useStatus();
const WsReqSentRef = useRef(false);
function sendKillPort() {
if (ttyd?.port) {
ws.sendData({
event: "client:kill-port",
server,
service: _.omit(service, ["servers"]),
port: ttyd.port,
});
}
}
useEffect(() => {
if (!ws?.socket || WsReqSentRef.current) {
return;
}
if (target == "logs") {
ws.sendData({
event: "client:service-server-logs",
server,
service: _.omit(service, ["servers"]),
cmd: log_cmd,
});
} else {
ws.sendData({
event: "client:service-server-shell",
server,
service: _.omit(service, ["servers"]),
});
}
WsReqSentRef.current = true;
return function () {
sendKillPort();
};
}, [ws, refresh]);
useEffect(() => {
console.log("log_cmd", log_cmd);
if (WsReqSentRef.current) {
sendKillPort();
setTtyd(undefined);
WsReqSentRef.current = false;
setRefresh((prev) => prev + 1);
}
}, [target, log_cmd]);
useEffect(() => {
if (ttyd) return;
if (
data?.event == "server:service-server-logs" &&
data?.ttyd &&
data.server?.private_ip == server.private_ip
) {
setTimeout(() => {
setTtyd(data.ttyd);
}, 2000);
}
if (
data?.event == "server:service-server-shell" &&
data?.ttyd &&
data.server?.private_ip == server.private_ip
) {
setTimeout(() => {
setTtyd(data.ttyd);
}, 2000);
}
if (portRef && data?.ttyd?.port) {
portRef.current = data.ttyd.port;
}
}, [data]);
useEffect(() => {
if (!ttyd?.port) return;
if (!isIntersecting) {
sendKillPort();
}
}, [isIntersecting]);
// const dev_logs_url = ttydLogs?.port
// ? `http://localhost:${ttydLogs.port}`
// : undefined;
const title = (
<>
<code>{server.private_ip}</code> {target}
</>
);
return (
<Stack className="gap-0 w-full" componentRef={viewRef as any}>
{isIntersecting && ttyd?.url && ttyd.port ? (
<Stack className="gap-0">
<TtydIframe
url={ttyd?.url}
title={title}
wrapperProps={{
className: "border-none",
}}
/>
</Stack>
) : (
<Center className="p-10 h-[460px]">
<Loading />
</Center>
)}
<hr />
</Stack>
);
}