Updates
This commit is contained in:
parent
aeff7c6ff0
commit
dcd7f022bd
@ -1,19 +1,73 @@
|
||||
import { AppContext } from "@/src/pages/_app";
|
||||
import { ParsedDeploymentServiceConfig } from "@/src/types";
|
||||
import {
|
||||
NormalizedServerObject,
|
||||
ParsedDeploymentServiceConfig,
|
||||
} from "@/src/types";
|
||||
import ArrowedLink from "@/twui/components/layout/ArrowedLink";
|
||||
import Button from "@/twui/components/layout/Button";
|
||||
import H2 from "@/twui/components/layout/H2";
|
||||
import Row from "@/twui/components/layout/Row";
|
||||
import Stack from "@/twui/components/layout/Stack";
|
||||
import { useContext } from "react";
|
||||
import { useContext, useEffect, useRef, useState } from "react";
|
||||
import ServiceClusterServer from "../service/(partials)/cluster-server";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import Select from "@/twui/components/form/Select";
|
||||
import useStatus from "@/twui/components/hooks/useStatus";
|
||||
import Loading from "@/twui/components/elements/Loading";
|
||||
import _ from "lodash";
|
||||
|
||||
type Props = {
|
||||
service: ParsedDeploymentServiceConfig;
|
||||
};
|
||||
|
||||
export default function DeploymentService({ service }: Props) {
|
||||
const { pageProps } = useContext(AppContext);
|
||||
const { pageProps, ws } = useContext(AppContext);
|
||||
const { deployment } = pageProps;
|
||||
|
||||
const { ready, setReady } = useStatus();
|
||||
|
||||
const children_services = deployment?.services.filter(
|
||||
(srv) => srv.parent_service_name == service.service_name,
|
||||
);
|
||||
|
||||
const all_clusters = [service, ...(children_services || [])];
|
||||
|
||||
const [targetCluster, setTargetCluster] = useState<
|
||||
ParsedDeploymentServiceConfig | undefined
|
||||
>(all_clusters?.[0]);
|
||||
|
||||
const targetClusterIndex =
|
||||
all_clusters.findIndex(
|
||||
(cl) => cl.service_name == targetCluster?.service_name,
|
||||
) + 1;
|
||||
|
||||
const cluster_servers = targetCluster?.servers;
|
||||
|
||||
const [targetServer, setTargetServer] = useState<
|
||||
NormalizedServerObject | undefined
|
||||
>(cluster_servers?.[0]);
|
||||
|
||||
const portRef = useRef<number>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
setReady(false);
|
||||
|
||||
console.log("portRef", portRef);
|
||||
|
||||
if (targetServer && portRef.current) {
|
||||
ws.sendData({
|
||||
event: "client:kill-port",
|
||||
server: targetServer,
|
||||
service: _.omit(service, ["servers"]),
|
||||
port: portRef.current,
|
||||
});
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
setReady(true);
|
||||
}, 2000);
|
||||
}, [targetCluster, targetServer]);
|
||||
|
||||
return (
|
||||
<Stack className="grid-cell">
|
||||
<Stack className="grid-cell-content">
|
||||
@ -27,7 +81,75 @@ export default function DeploymentService({ service }: Props) {
|
||||
/>
|
||||
</Row>
|
||||
|
||||
<code>{service.service_name}</code>
|
||||
<code>{service.service_name} service</code>
|
||||
</Stack>
|
||||
|
||||
<hr />
|
||||
<Stack className="gap-0">
|
||||
<Row className="p-4 grid md:grid-cols-2">
|
||||
<Select
|
||||
options={all_clusters.map((cl) => ({
|
||||
value: cl.service_name,
|
||||
title: `Cluster #${targetClusterIndex}`,
|
||||
}))}
|
||||
changeHandler={(v) => {
|
||||
setTargetCluster(
|
||||
all_clusters.find((cl) => cl.service_name == v),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
{cluster_servers ? (
|
||||
<Select
|
||||
options={cluster_servers.map((srv) => ({
|
||||
value: srv.private_ip!,
|
||||
title: srv.private_ip,
|
||||
}))}
|
||||
changeHandler={(v) => {
|
||||
setTargetServer(
|
||||
cluster_servers.find(
|
||||
(srv) => srv.private_ip == v,
|
||||
),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
) : undefined}
|
||||
|
||||
{/* {cluster_servers?.map((server, index) => {
|
||||
const is_active =
|
||||
server?.private_ip == targetServer?.private_ip;
|
||||
|
||||
return (
|
||||
<Button
|
||||
title={`Cluster ${index + 1}`}
|
||||
key={index}
|
||||
color="gray"
|
||||
variant={is_active ? undefined : "outlined"}
|
||||
size="small"
|
||||
onClick={() => {
|
||||
setTargetServer(server);
|
||||
}}
|
||||
>
|
||||
<span
|
||||
className={twMerge(
|
||||
is_active ? "font-semibold" : "",
|
||||
)}
|
||||
>
|
||||
{server.private_ip}
|
||||
</span>
|
||||
</Button>
|
||||
);
|
||||
})} */}
|
||||
</Row>
|
||||
|
||||
{ready ? (
|
||||
targetServer ? (
|
||||
<ServiceClusterServer
|
||||
server={targetServer}
|
||||
service={service}
|
||||
portRef={portRef}
|
||||
/>
|
||||
) : undefined
|
||||
) : undefined}
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
|
||||
@ -0,0 +1,65 @@
|
||||
import { Dispatch, Fragment, SetStateAction, useContext, useRef } from "react";
|
||||
import { ParsedDeploymentServiceConfig } from "@/src/types";
|
||||
import Select, { TWUISelectOptionObject } from "@/twui/components/form/Select";
|
||||
import Row from "@/twui/components/layout/Row";
|
||||
import Button from "@/twui/components/layout/Button";
|
||||
import Span from "@/twui/components/layout/Span";
|
||||
import Border from "@/twui/components/elements/Border";
|
||||
import { X } from "lucide-react";
|
||||
|
||||
type Props = {
|
||||
service: ParsedDeploymentServiceConfig;
|
||||
setLog: Dispatch<SetStateAction<string | undefined>>;
|
||||
log?: string;
|
||||
};
|
||||
|
||||
export default function ServiceClusterServerLogSelectorSelectLog({
|
||||
service,
|
||||
setLog,
|
||||
log,
|
||||
}: Props) {
|
||||
const logs = service.logs;
|
||||
|
||||
const log_strings = logs?.map((l) => (typeof l == "string" ? l : l.cmd));
|
||||
const is_custom_log =
|
||||
log?.match(/./) && !Boolean(log_strings?.find((l) => l == log));
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{is_custom_log ? (
|
||||
<Row className="grow">
|
||||
<Border className="w-full py-1 h-[42px]">
|
||||
<Row className="w-full justify-between">
|
||||
<Span>{log}</Span>
|
||||
<Button
|
||||
title="Clear Custom Log"
|
||||
variant="ghost"
|
||||
className="p-1"
|
||||
onClick={() => {
|
||||
setLog(undefined);
|
||||
}}
|
||||
>
|
||||
<X size={17} />
|
||||
</Button>
|
||||
</Row>
|
||||
</Border>
|
||||
</Row>
|
||||
) : (
|
||||
<Select
|
||||
options={[
|
||||
...(log_strings?.map(
|
||||
(l) =>
|
||||
({
|
||||
value: l,
|
||||
title: l,
|
||||
}) as TWUISelectOptionObject,
|
||||
) || []),
|
||||
]}
|
||||
changeHandler={(v) => {
|
||||
setLog(v);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,63 @@
|
||||
import Stack from "@/twui/components/layout/Stack";
|
||||
import { Dispatch, SetStateAction, useRef } from "react";
|
||||
import Button from "@/twui/components/layout/Button";
|
||||
import Modal from "@/twui/components/elements/Modal";
|
||||
import H3 from "@/twui/components/layout/H3";
|
||||
import Span from "@/twui/components/layout/Span";
|
||||
import useStatus from "@/twui/components/hooks/useStatus";
|
||||
import Textarea from "@/twui/components/form/Textarea";
|
||||
import LucideIcon from "@/twui/components/elements/lucide-icon";
|
||||
|
||||
type Props = {
|
||||
setLog: Dispatch<SetStateAction<string | undefined>>;
|
||||
};
|
||||
|
||||
export default function ServiceClusterServerLogSelectorSetCustomLog({
|
||||
setLog,
|
||||
}: Props) {
|
||||
const { open, setOpen } = useStatus();
|
||||
|
||||
const customLogRef = useRef("");
|
||||
|
||||
return (
|
||||
<Modal
|
||||
target={
|
||||
<Button
|
||||
title="Enter custom command"
|
||||
size="small"
|
||||
color="gray"
|
||||
className="w-[42px] h-[42px]"
|
||||
>
|
||||
<LucideIcon name="Edit3" size={14} />
|
||||
</Button>
|
||||
}
|
||||
setOpen={setOpen}
|
||||
open={open}
|
||||
>
|
||||
<Stack>
|
||||
<Stack className="gap-1">
|
||||
<H3 className="admin-h3">Enter a custom Command</H3>
|
||||
<Span variant="faded">
|
||||
Enter a command to run a custom log
|
||||
</Span>
|
||||
</Stack>
|
||||
<Textarea
|
||||
placeholder="Enter custom command"
|
||||
changeHandler={(v) => {
|
||||
customLogRef.current = v;
|
||||
}}
|
||||
autoFocus
|
||||
/>
|
||||
<Button
|
||||
title="Set Custom Command"
|
||||
onClick={() => {
|
||||
setLog(customLogRef.current.trim());
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
Set Custom Command
|
||||
</Button>
|
||||
</Stack>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
@ -1,102 +1,34 @@
|
||||
import Stack from "@/twui/components/layout/Stack";
|
||||
import {
|
||||
Dispatch,
|
||||
SetStateAction,
|
||||
useContext,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { AppContext } from "@/src/pages/_app";
|
||||
import { Dispatch, SetStateAction } from "react";
|
||||
import {
|
||||
NormalizedServerObject,
|
||||
ParsedDeploymentServiceConfig,
|
||||
} from "@/src/types";
|
||||
import Select, { TWUISelectOptionObject } from "@/twui/components/form/Select";
|
||||
import AceEditor from "@/twui/components/editors/AceEditor";
|
||||
import Row from "@/twui/components/layout/Row";
|
||||
import Button from "@/twui/components/layout/Button";
|
||||
import Modal from "@/twui/components/elements/Modal";
|
||||
import H3 from "@/twui/components/layout/H3";
|
||||
import Span from "@/twui/components/layout/Span";
|
||||
import useStatus from "@/twui/components/hooks/useStatus";
|
||||
import ServiceClusterServerLogSelectorSetCustomLog from "./cluster-server-log-selector-set-custom-log";
|
||||
import ServiceClusterServerLogSelectorSelectLog from "./cluster-server-log-selector-select-log";
|
||||
|
||||
type Props = {
|
||||
service: ParsedDeploymentServiceConfig;
|
||||
server: NormalizedServerObject;
|
||||
setLog: Dispatch<SetStateAction<string | undefined>>;
|
||||
log?: string;
|
||||
};
|
||||
|
||||
export default function ServiceClusterServerLogSelector({
|
||||
service,
|
||||
server,
|
||||
setLog: externalSetLog,
|
||||
setLog,
|
||||
log,
|
||||
}: 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);
|
||||
|
||||
const { open, setOpen } = useStatus();
|
||||
|
||||
const customLogRef = useRef("");
|
||||
|
||||
|
||||
return (
|
||||
<Stack className="w-full gap-2 p-4">
|
||||
<Row className="flex-nowrap">
|
||||
<Select
|
||||
options={[
|
||||
...(log_strings?.map(
|
||||
(l) =>
|
||||
({
|
||||
value: l,
|
||||
title: l,
|
||||
}) as TWUISelectOptionObject,
|
||||
) || []),
|
||||
]}
|
||||
changeHandler={(v) => {
|
||||
if (v == "custom") {
|
||||
setIsCustomLog(true);
|
||||
} else {
|
||||
setIsCustomLog(false);
|
||||
setLog(v);
|
||||
}
|
||||
}}
|
||||
<ServiceClusterServerLogSelectorSetCustomLog setLog={setLog} />
|
||||
<ServiceClusterServerLogSelectorSelectLog
|
||||
service={service}
|
||||
setLog={setLog}
|
||||
log={log}
|
||||
/>
|
||||
<Modal
|
||||
target={
|
||||
<Button title="Enter custom command" size="small">
|
||||
Custom
|
||||
</Button>
|
||||
}
|
||||
setOpen={setOpen}
|
||||
open={open}
|
||||
>
|
||||
<Stack>
|
||||
<Stack className="gap-1">
|
||||
<H3 className="admin-h3">Enter a custom Command</H3>
|
||||
<Span variant="faded">
|
||||
Enter a command to run a custom log
|
||||
</Span>
|
||||
</Stack>
|
||||
<AceEditor
|
||||
placeholder="Enter custom command"
|
||||
onChange={(v) => {
|
||||
customLogRef.current = v;
|
||||
}}
|
||||
/>
|
||||
<Button title="Set Custom Command" onClick={()=>{
|
||||
|
||||
}}>
|
||||
Set Custom Command
|
||||
</Button>
|
||||
</Stack>
|
||||
</Modal>
|
||||
</Row>
|
||||
</Stack>
|
||||
);
|
||||
|
||||
@ -20,14 +20,16 @@ type Props = {
|
||||
service: ParsedDeploymentServiceConfig;
|
||||
server: NormalizedServerObject;
|
||||
target: (typeof ServerTerminalTargets)[number]["name"];
|
||||
log?: string;
|
||||
log_cmd?: string;
|
||||
portRef?: RefObject<number | undefined>;
|
||||
};
|
||||
|
||||
export default function ServiceClusterServerViews({
|
||||
service,
|
||||
server,
|
||||
target,
|
||||
log,
|
||||
log_cmd,
|
||||
portRef,
|
||||
}: Props) {
|
||||
const { pageProps, ws } = useContext(AppContext);
|
||||
|
||||
@ -49,7 +51,6 @@ export default function ServiceClusterServerViews({
|
||||
server,
|
||||
service: _.omit(service, ["servers"]),
|
||||
port: ttyd.port,
|
||||
log,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -64,6 +65,7 @@ export default function ServiceClusterServerViews({
|
||||
event: "client:service-server-logs",
|
||||
server,
|
||||
service: _.omit(service, ["servers"]),
|
||||
cmd: log_cmd,
|
||||
});
|
||||
} else {
|
||||
ws.sendData({
|
||||
@ -81,6 +83,8 @@ export default function ServiceClusterServerViews({
|
||||
}, [ws, refresh]);
|
||||
|
||||
useEffect(() => {
|
||||
console.log("log_cmd", log_cmd);
|
||||
|
||||
if (WsReqSentRef.current) {
|
||||
sendKillPort();
|
||||
|
||||
@ -88,7 +92,7 @@ export default function ServiceClusterServerViews({
|
||||
WsReqSentRef.current = false;
|
||||
setRefresh((prev) => prev + 1);
|
||||
}
|
||||
}, [target]);
|
||||
}, [target, log_cmd]);
|
||||
|
||||
useEffect(() => {
|
||||
if (ttyd) return;
|
||||
@ -112,6 +116,10 @@ export default function ServiceClusterServerViews({
|
||||
setTtyd(data.ttyd);
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
if (portRef && data?.ttyd?.port) {
|
||||
portRef.current = data.ttyd.port;
|
||||
}
|
||||
}, [data]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import Stack from "@/twui/components/layout/Stack";
|
||||
import { ComponentProps, useContext, useRef, useState } from "react";
|
||||
import { ComponentProps, RefObject, useContext, useRef, useState } from "react";
|
||||
import { AppContext } from "@/src/pages/_app";
|
||||
import {
|
||||
NormalizedServerObject,
|
||||
@ -19,20 +19,23 @@ type Props = {
|
||||
service: ParsedDeploymentServiceConfig;
|
||||
server: NormalizedServerObject;
|
||||
wrapperProps?: ComponentProps<typeof Stack>;
|
||||
portRef?: RefObject<number | undefined>;
|
||||
};
|
||||
|
||||
export default function ServiceClusterServer({
|
||||
service,
|
||||
server,
|
||||
wrapperProps,
|
||||
portRef,
|
||||
}: Props) {
|
||||
const { pageProps } = useContext(AppContext);
|
||||
|
||||
const elementRef = useRef<HTMLDivElement>(undefined);
|
||||
const { isIntersecting } = useIntersectionObserver({ elementRef });
|
||||
|
||||
const [target, setTarget] =
|
||||
useState<(typeof ServerTerminalTargets)[number]["name"]>("logs");
|
||||
const [target, setTarget] = useState<
|
||||
(typeof ServerTerminalTargets)[number]["name"]
|
||||
>(service.logs?.[0] ? "logs" : "shell");
|
||||
|
||||
const [log, setLog] = useState<string>();
|
||||
|
||||
@ -67,9 +70,18 @@ export default function ServiceClusterServer({
|
||||
})}
|
||||
</Row>
|
||||
</Row>
|
||||
<ServiceClusterServerLogSelector {...{ server, service, setLog }} />
|
||||
{target == "logs" ? (
|
||||
<ServiceClusterServerLogSelector
|
||||
{...{ server, service, setLog, log }}
|
||||
/>
|
||||
) : (
|
||||
<Row className="h-[74px]"></Row>
|
||||
)}
|
||||
<hr />
|
||||
<ServiceClusterServerViews {...{ server, service, target, log }} />
|
||||
<ServiceClusterServerViews
|
||||
{...{ server, service, target, portRef }}
|
||||
log_cmd={log}
|
||||
/>
|
||||
{/* {isIntersecting ? (
|
||||
<ServiceClusterServerViews
|
||||
{...{ server, service, target, log }}
|
||||
|
||||
@ -6,10 +6,7 @@ import {
|
||||
ParsedDeploymentServiceConfig,
|
||||
} from "@/src/types";
|
||||
import Row from "@/twui/components/layout/Row";
|
||||
import H2 from "@/twui/components/layout/H2";
|
||||
import ServiceClusterServer from "./cluster-server";
|
||||
import Button from "@/twui/components/layout/Button";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
type Props = {
|
||||
service: ParsedDeploymentServiceConfig;
|
||||
|
||||
@ -20,6 +20,7 @@ type Params = {
|
||||
service?: Omit<ParsedDeploymentServiceConfig, "servers">;
|
||||
server?: NormalizedServerObject;
|
||||
paradigm: (typeof PrivateServerTtydParadigms)[number]["name"];
|
||||
cmd?: string;
|
||||
};
|
||||
|
||||
export default async function grabTtydServerInfo({
|
||||
@ -27,12 +28,15 @@ export default async function grabTtydServerInfo({
|
||||
service,
|
||||
user,
|
||||
paradigm,
|
||||
cmd: passed_log_cmd,
|
||||
}: Params): Promise<TtydInfoObject> {
|
||||
const first_log = service?.logs?.[0];
|
||||
const final_first_log =
|
||||
typeof first_log == "string" ? first_log : first_log?.cmd;
|
||||
|
||||
if (paradigm == "logs" && !final_first_log) {
|
||||
const final_log = passed_log_cmd || final_first_log;
|
||||
|
||||
if (paradigm == "logs" && !final_log) {
|
||||
throw new Error(`Service Doesn't have logs.`);
|
||||
}
|
||||
|
||||
@ -42,16 +46,17 @@ export default async function grabTtydServerInfo({
|
||||
|
||||
let cmd = ``;
|
||||
|
||||
if (paradigm == "logs") {
|
||||
cmd += ` ${grabSSHPrefix()}`;
|
||||
} else {
|
||||
cmd += ` ssh -i ${TURBOCI_SSH_KEY_FILE}`;
|
||||
}
|
||||
|
||||
// if (paradigm == "logs") {
|
||||
// } else {
|
||||
// cmd += ` ssh -i ${TURBOCI_SSH_KEY_FILE}`;
|
||||
// }
|
||||
|
||||
cmd += ` root@${server?.private_ip}`;
|
||||
|
||||
if (paradigm == "logs" && final_first_log) {
|
||||
cmd += ` ${final_first_log}`;
|
||||
if (paradigm == "logs" && final_log) {
|
||||
cmd += ` ${final_log}`;
|
||||
}
|
||||
|
||||
const ttyd_cmd = grabTtydCmd({
|
||||
|
||||
@ -252,7 +252,7 @@ export type WebSocketDataType = {
|
||||
server?: NormalizedServerObject;
|
||||
ttyd?: TtydInfoObject;
|
||||
port?: string | number;
|
||||
log?: string;
|
||||
cmd?: string;
|
||||
};
|
||||
|
||||
export type WebSocketMessageParam = {
|
||||
|
||||
@ -11,12 +11,16 @@ export default async function socketClientServiceServerLogs({
|
||||
const user = ws.data.user;
|
||||
const service = data?.service;
|
||||
const server = data?.server;
|
||||
const cmd = data?.cmd;
|
||||
|
||||
console.log("cmd", cmd);
|
||||
|
||||
const ttyd = await grabTtydServerInfo({
|
||||
server,
|
||||
service,
|
||||
user,
|
||||
paradigm: "logs",
|
||||
cmd,
|
||||
});
|
||||
|
||||
sendData(ws, {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user