Updates
This commit is contained in:
parent
921375c53d
commit
f3b42bc206
@ -49,15 +49,15 @@ export default function TtydIframe({
|
||||
<Span size="small" variant="faded">
|
||||
{title}
|
||||
</Span>
|
||||
<LucideIcon
|
||||
{/* <LucideIcon
|
||||
name="ChevronRight"
|
||||
size={15}
|
||||
opacity={0.3}
|
||||
/>
|
||||
/> */}
|
||||
</Fragment>
|
||||
) : null}
|
||||
|
||||
<Span size="small" variant="faded">
|
||||
{/* <Span size="small" variant="faded">
|
||||
<a
|
||||
href={url}
|
||||
target="_blank"
|
||||
@ -65,7 +65,7 @@ export default function TtydIframe({
|
||||
>
|
||||
{url}
|
||||
</a>
|
||||
</Span>
|
||||
</Span> */}
|
||||
</Row>
|
||||
<Row>
|
||||
<Button
|
||||
|
||||
@ -0,0 +1,66 @@
|
||||
import Stack from "@/twui/components/layout/Stack";
|
||||
import {
|
||||
Dispatch,
|
||||
SetStateAction,
|
||||
useContext,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { AppContext } from "@/src/pages/_app";
|
||||
import {
|
||||
NormalizedServerObject,
|
||||
ParsedDeploymentServiceConfig,
|
||||
} from "@/src/types";
|
||||
import Select, { TWUISelectOptionObject } from "@/twui/components/form/Select";
|
||||
|
||||
type Props = {
|
||||
service: ParsedDeploymentServiceConfig;
|
||||
server: NormalizedServerObject;
|
||||
setLog: Dispatch<SetStateAction<string | undefined>>;
|
||||
};
|
||||
|
||||
export default function ServiceClusterServerLogSelector({
|
||||
service,
|
||||
server,
|
||||
setLog: externalSetLog,
|
||||
}: Props) {
|
||||
const { pageProps } = useContext(AppContext);
|
||||
|
||||
const logs = service.logs;
|
||||
|
||||
const log_strings = logs?.map((l) => (typeof l == "string" ? l : l.cmd));
|
||||
|
||||
const [log, setLog] = useState(log_strings?.[0]);
|
||||
const [isCustomLog, setIsCustomLog] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
externalSetLog(log);
|
||||
}, [log]);
|
||||
|
||||
return (
|
||||
<Stack className="w-full gap-0">
|
||||
<Select
|
||||
options={[
|
||||
...(log_strings?.map(
|
||||
(l) =>
|
||||
({ value: l, title: l }) as TWUISelectOptionObject,
|
||||
) || []),
|
||||
{
|
||||
value: "custom",
|
||||
title: "--Custom--",
|
||||
},
|
||||
]}
|
||||
changeHandler={(v) => {
|
||||
if (v == "custom") {
|
||||
setIsCustomLog(true);
|
||||
} else {
|
||||
setIsCustomLog(false);
|
||||
setLog(v);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{isCustomLog}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
@ -4,6 +4,7 @@ import { AppContext } from "@/src/pages/_app";
|
||||
import {
|
||||
NormalizedServerObject,
|
||||
ParsedDeploymentServiceConfig,
|
||||
ServerTerminalTargets,
|
||||
TtydInfoObject,
|
||||
WebSocketDataType,
|
||||
} from "@/src/types";
|
||||
@ -13,13 +14,21 @@ 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?: string;
|
||||
};
|
||||
|
||||
export default function ServiceClusterServerViews({ service, server }: Props) {
|
||||
export default function ServiceClusterServerViews({
|
||||
service,
|
||||
server,
|
||||
target,
|
||||
log,
|
||||
}: Props) {
|
||||
const { pageProps, ws } = useContext(AppContext);
|
||||
|
||||
const viewRef = useRef<HTMLDivElement>(undefined);
|
||||
@ -27,26 +36,62 @@ export default function ServiceClusterServerViews({ service, server }: Props) {
|
||||
const { data } = useWebSocketEventHandler<WebSocketDataType>();
|
||||
const { isIntersecting } = useIntersectionObserver({ elementRef: viewRef });
|
||||
|
||||
const [ttydLogs, setTtydLogs] = useState<TtydInfoObject>();
|
||||
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,
|
||||
log,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!ws?.socket || WsReqSentRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
ws.sendData({
|
||||
event: "client:service-server-logs",
|
||||
server,
|
||||
service: _.omit(service, ["servers"]),
|
||||
});
|
||||
if (target == "logs") {
|
||||
ws.sendData({
|
||||
event: "client:service-server-logs",
|
||||
server,
|
||||
service: _.omit(service, ["servers"]),
|
||||
});
|
||||
} else {
|
||||
ws.sendData({
|
||||
event: "client:service-server-shell",
|
||||
server,
|
||||
service: _.omit(service, ["servers"]),
|
||||
});
|
||||
}
|
||||
|
||||
WsReqSentRef.current = true;
|
||||
}, [ws]);
|
||||
|
||||
return function () {
|
||||
sendKillPort();
|
||||
};
|
||||
}, [ws, refresh]);
|
||||
|
||||
useEffect(() => {
|
||||
if (ttydLogs) return;
|
||||
if (WsReqSentRef.current) {
|
||||
sendKillPort();
|
||||
|
||||
setTtyd(undefined);
|
||||
WsReqSentRef.current = false;
|
||||
setRefresh((prev) => prev + 1);
|
||||
}
|
||||
}, [target]);
|
||||
|
||||
useEffect(() => {
|
||||
if (ttyd) return;
|
||||
|
||||
if (
|
||||
data?.event == "server:service-server-logs" &&
|
||||
@ -54,53 +99,45 @@ export default function ServiceClusterServerViews({ service, server }: Props) {
|
||||
data.server?.private_ip == server.private_ip
|
||||
) {
|
||||
setTimeout(() => {
|
||||
setTtydLogs(data.ttyd);
|
||||
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);
|
||||
}
|
||||
}, [data]);
|
||||
|
||||
console.log("isIntersecting", isIntersecting);
|
||||
console.log("ttydLogs", ttydLogs);
|
||||
|
||||
useEffect(() => {
|
||||
if (!ttydLogs?.port) return;
|
||||
if (!ttyd?.port) return;
|
||||
|
||||
if (!isIntersecting) {
|
||||
ws.sendData({
|
||||
event: "client:kill-port",
|
||||
server,
|
||||
service: _.omit(service, ["servers"]),
|
||||
port: ttydLogs.port,
|
||||
});
|
||||
sendKillPort();
|
||||
}
|
||||
}, [isIntersecting]);
|
||||
|
||||
const dev_logs_url = ttydLogs?.port
|
||||
? `http://localhost:${ttydLogs.port}`
|
||||
: undefined;
|
||||
// const dev_logs_url = ttydLogs?.port
|
||||
// ? `http://localhost:${ttydLogs.port}`
|
||||
// : undefined;
|
||||
|
||||
const title = (
|
||||
<>
|
||||
{service.service_name} service <code>{server.private_ip}</code> Logs
|
||||
<code>{server.private_ip}</code> {target}
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<Stack className="gap-0 w-full" componentRef={viewRef as any}>
|
||||
{isIntersecting && ttydLogs?.url && ttydLogs.port ? (
|
||||
{isIntersecting && ttyd?.url && ttyd.port ? (
|
||||
<Stack className="gap-0">
|
||||
{/* {dev_logs_url ? (
|
||||
<TtydIframe
|
||||
url={dev_logs_url}
|
||||
title={title}
|
||||
wrapperProps={{
|
||||
className: "border-none",
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
<hr /> */}
|
||||
<TtydIframe
|
||||
url={ttydLogs?.url}
|
||||
url={ttyd?.url}
|
||||
title={title}
|
||||
wrapperProps={{
|
||||
className: "border-none",
|
||||
@ -108,7 +145,7 @@ export default function ServiceClusterServerViews({ service, server }: Props) {
|
||||
/>
|
||||
</Stack>
|
||||
) : (
|
||||
<Center className="p-10 h-[400px]">
|
||||
<Center className="p-10 h-[460px]">
|
||||
<Loading />
|
||||
</Center>
|
||||
)}
|
||||
|
||||
@ -1,14 +1,18 @@
|
||||
import Stack from "@/twui/components/layout/Stack";
|
||||
import { useContext, useRef } from "react";
|
||||
import { useContext, useRef, useState } from "react";
|
||||
import { AppContext } from "@/src/pages/_app";
|
||||
import {
|
||||
NormalizedServerObject,
|
||||
ParsedDeploymentServiceConfig,
|
||||
ServerTerminalTargets,
|
||||
} from "@/src/types";
|
||||
import useIntersectionObserver from "@/twui/components/hooks/useIntersectionObserver";
|
||||
import Center from "@/twui/components/layout/Center";
|
||||
import Loading from "@/twui/components/elements/Loading";
|
||||
import ServiceClusterServerViews from "./cluster-server-views";
|
||||
import Row from "@/twui/components/layout/Row";
|
||||
import Button from "@/twui/components/layout/Button";
|
||||
import ServiceClusterServerLogSelector from "./cluster-server-log-selector";
|
||||
|
||||
type Props = {
|
||||
service: ParsedDeploymentServiceConfig;
|
||||
@ -21,14 +25,47 @@ export default function ServiceClusterServer({ service, server }: Props) {
|
||||
const elementRef = useRef<HTMLDivElement>(undefined);
|
||||
const { isIntersecting } = useIntersectionObserver({ elementRef });
|
||||
|
||||
const [target, setTarget] =
|
||||
useState<(typeof ServerTerminalTargets)[number]["name"]>("logs");
|
||||
|
||||
const [log, setLog] = useState<string>();
|
||||
|
||||
return (
|
||||
<Stack className="grid-cell gap-0" componentRef={elementRef as any}>
|
||||
<Stack className="grid-cell-content">
|
||||
<code>{server.private_ip}</code>
|
||||
<Row className="w-full justify-between">
|
||||
<Row>
|
||||
<code>{server.private_ip}</code>
|
||||
</Row>
|
||||
<Row>
|
||||
{ServerTerminalTargets.map((targ, index) => {
|
||||
const is_active = targ.name == target;
|
||||
|
||||
return (
|
||||
<Button
|
||||
title={`${targ.name}`}
|
||||
onClick={() => {
|
||||
setTarget(targ.name);
|
||||
}}
|
||||
size="smaller"
|
||||
color="gray"
|
||||
variant={is_active ? undefined : "outlined"}
|
||||
>
|
||||
{targ.name}
|
||||
</Button>
|
||||
);
|
||||
})}
|
||||
</Row>
|
||||
</Row>
|
||||
<ServiceClusterServerLogSelector
|
||||
{...{ server, service, setLog }}
|
||||
/>
|
||||
</Stack>
|
||||
<hr />
|
||||
{isIntersecting ? (
|
||||
<ServiceClusterServerViews {...{ server, service }} />
|
||||
<ServiceClusterServerViews
|
||||
{...{ server, service, target, log }}
|
||||
/>
|
||||
) : (
|
||||
<Center>
|
||||
<Loading />
|
||||
|
||||
@ -50,7 +50,7 @@ export default async function grabTtydServerInfo({
|
||||
|
||||
cmd += ` root@${server?.private_ip}`;
|
||||
|
||||
if (final_first_log) {
|
||||
if (paradigm == "logs" && final_first_log) {
|
||||
cmd += ` ${final_first_log}`;
|
||||
}
|
||||
|
||||
|
||||
@ -232,6 +232,7 @@ export const WebSocketEvents = [
|
||||
"client:ping",
|
||||
"client:service-server-logs",
|
||||
"client:kill-port",
|
||||
"client:service-server-shell",
|
||||
|
||||
"server:ping",
|
||||
"server:error",
|
||||
@ -241,6 +242,7 @@ export const WebSocketEvents = [
|
||||
"server:update",
|
||||
"server:service-server-logs",
|
||||
"server:killed-port",
|
||||
"server:service-server-shell",
|
||||
] as const;
|
||||
|
||||
export type WebSocketDataType = {
|
||||
@ -250,6 +252,7 @@ export type WebSocketDataType = {
|
||||
server?: NormalizedServerObject;
|
||||
ttyd?: TtydInfoObject;
|
||||
port?: string | number;
|
||||
log?: string;
|
||||
};
|
||||
|
||||
export type WebSocketMessageParam = {
|
||||
@ -291,3 +294,8 @@ export type WebSocketConnectedUserData = {
|
||||
child_processes: ChildProcess[];
|
||||
ports: (string | number)[];
|
||||
};
|
||||
|
||||
export const ServerTerminalTargets = [
|
||||
{ name: "logs" },
|
||||
{ name: "shell" },
|
||||
] as const;
|
||||
|
||||
@ -6,7 +6,7 @@ type Params = {
|
||||
};
|
||||
|
||||
export default async function killPort({ port, exec_options }: Params) {
|
||||
const kill_ports_cmd = `ss -tlnp | awk -v p=${port} 'NR>1 {split($4,a,":"); if(a[length(a)]==p) print $6}' | grep -oP 'pid=\K[0-9]+' | xargs -r kill -9`;
|
||||
const kill_ports_cmd = `ss -tlnp | awk -v p=${port} 'NR>1 {split($4,a,":"); if(a[length(a)]==p) print $6}' | grep -oP 'pid=\\K[0-9]+' | xargs -r kill -9`;
|
||||
|
||||
execSync(kill_ports_cmd, {
|
||||
...exec_options,
|
||||
|
||||
@ -2,6 +2,7 @@ import { WebSocketMessageParam } from "@/src/types";
|
||||
import sendData from "../(utils)/send-data";
|
||||
import sendError from "../(utils)/send-error";
|
||||
import grabConnectedWebsocketUserdata from "../(utils)/grab-connected-websocket-user-data";
|
||||
import killPort from "../(utils)/kill-port";
|
||||
|
||||
export default async function socketClientKillPort({
|
||||
ws,
|
||||
@ -15,7 +16,15 @@ export default async function socketClientKillPort({
|
||||
|
||||
const connected_user_data = grabConnectedWebsocketUserdata({ user });
|
||||
|
||||
console.log("connected_user_data", connected_user_data);
|
||||
console.log("port", port);
|
||||
|
||||
if (port) {
|
||||
await killPort({ port, exec_options: { stdio: "inherit" } });
|
||||
|
||||
connected_user_data.ports = connected_user_data.ports.filter(
|
||||
(p) => p != port,
|
||||
);
|
||||
}
|
||||
|
||||
sendData(ws, {
|
||||
event: "server:killed-port",
|
||||
|
||||
@ -19,8 +19,6 @@ export default async function socketClientServiceServerLogs({
|
||||
paradigm: "logs",
|
||||
});
|
||||
|
||||
console.log("ttyd", ttyd);
|
||||
|
||||
sendData(ws, {
|
||||
event: "server:service-server-logs",
|
||||
ttyd,
|
||||
|
||||
30
src/websocket/events/client-service-server-shell.ts
Normal file
30
src/websocket/events/client-service-server-shell.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { WebSocketMessageParam } from "@/src/types";
|
||||
import sendData from "../(utils)/send-data";
|
||||
import sendError from "../(utils)/send-error";
|
||||
import grabTtydServerInfo from "@/src/functions/ttyd/grab-ttyd-service-info";
|
||||
|
||||
export default async function socketClientServiceServerShell({
|
||||
ws,
|
||||
data,
|
||||
}: WebSocketMessageParam) {
|
||||
try {
|
||||
const user = ws.data.user;
|
||||
const service = data?.service;
|
||||
const server = data?.server;
|
||||
|
||||
const ttyd = await grabTtydServerInfo({
|
||||
server,
|
||||
service,
|
||||
user,
|
||||
paradigm: "terminal",
|
||||
});
|
||||
|
||||
sendData(ws, {
|
||||
event: "server:service-server-shell",
|
||||
ttyd,
|
||||
server,
|
||||
});
|
||||
} catch (error: any) {
|
||||
sendError(ws, "Service Server Logs Error! " + error.message);
|
||||
}
|
||||
}
|
||||
@ -9,6 +9,7 @@ import socketClientPing from "./events/client-ping";
|
||||
import debugLog from "@moduletrace/datasquirel/dist/package-shared/utils/logging/debug-log";
|
||||
import socketClientServiceServerLogs from "./events/client-service-server-logs";
|
||||
import socketClientKillPort from "./events/client-kill-port";
|
||||
import socketClientServiceServerShell from "./events/client-service-server-shell";
|
||||
|
||||
type Param = {
|
||||
ws: ServerWebSocket<WebSocketData>;
|
||||
@ -47,6 +48,14 @@ export default async function socketMessage({ ws, message }: Param) {
|
||||
});
|
||||
await socketClientServiceServerLogs(websocketMessageParams);
|
||||
break;
|
||||
case "client:service-server-shell":
|
||||
debugLog({
|
||||
log: `${userRef} Getting Service Server Shell ...`,
|
||||
addTime: true,
|
||||
label,
|
||||
});
|
||||
await socketClientServiceServerShell(websocketMessageParams);
|
||||
break;
|
||||
case "client:kill-port":
|
||||
debugLog({
|
||||
log: `${userRef} Killing Port ${data.port} ...`,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user