Updates
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
import Stack from "@/twui/components/layout/Stack";
|
||||
import { useContext, useEffect, useRef, useState } from "react";
|
||||
import { AppContext } from "@/src/pages/_app";
|
||||
import { 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 useStatus from "@/twui/components/hooks/useStatus";
|
||||
|
||||
type Props = {
|
||||
paradigm: "shell" | "pm2-logs";
|
||||
};
|
||||
|
||||
export default function RelayShellView({ paradigm }: Props) {
|
||||
const { pageProps, ws } = useContext(AppContext);
|
||||
const { user } = pageProps;
|
||||
|
||||
const username = user.super_admin ? "root" : user.username;
|
||||
const cmd =
|
||||
paradigm == "pm2-logs"
|
||||
? "pm2 logs"
|
||||
: user.super_admin
|
||||
? "bash"
|
||||
: username
|
||||
? `su - ${username} -c "bash"`
|
||||
: undefined;
|
||||
|
||||
const { data } = useWebSocketEventHandler<WebSocketDataType>();
|
||||
const [ttyd, setTtyd] = useState<TtydInfoObject>();
|
||||
|
||||
const { refresh, setRefresh } = useStatus();
|
||||
|
||||
const WsReqSentRef = useRef(false);
|
||||
|
||||
function sendKillPort() {
|
||||
if (ttyd?.port) {
|
||||
ws.sendData({
|
||||
event: "client:kill-port",
|
||||
port: ttyd.port,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!ws?.socket || WsReqSentRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
ws.sendData({
|
||||
event:
|
||||
paradigm == "pm2-logs"
|
||||
? "client:relay-pm2-logs"
|
||||
: "client:relay-shell",
|
||||
cmd,
|
||||
});
|
||||
|
||||
WsReqSentRef.current = true;
|
||||
|
||||
return function () {
|
||||
sendKillPort();
|
||||
};
|
||||
}, [ws, refresh]);
|
||||
|
||||
useEffect(() => {
|
||||
if (WsReqSentRef.current) {
|
||||
sendKillPort();
|
||||
|
||||
setTtyd(undefined);
|
||||
WsReqSentRef.current = false;
|
||||
setRefresh((prev) => prev + 1);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (ttyd) return;
|
||||
|
||||
if (
|
||||
(data?.event == "server:relay-shell" ||
|
||||
data?.event == "server:relay-pm2-logs") &&
|
||||
data?.ttyd
|
||||
) {
|
||||
setTimeout(() => {
|
||||
setTtyd(
|
||||
data.ttyd
|
||||
? {
|
||||
...data.ttyd,
|
||||
href: `${window.location.origin}${data.ttyd.url}`,
|
||||
}
|
||||
: undefined,
|
||||
);
|
||||
}, 2000);
|
||||
}
|
||||
}, [data]);
|
||||
|
||||
return (
|
||||
<Stack className="gap-0 w-full">
|
||||
{ttyd?.url && ttyd.port ? (
|
||||
<Stack className="gap-0">
|
||||
<TtydIframe
|
||||
url={ttyd?.url}
|
||||
title={`Relay Shell`}
|
||||
wrapperProps={{
|
||||
className: "border-none",
|
||||
}}
|
||||
/>
|
||||
</Stack>
|
||||
) : (
|
||||
<Center className="p-10 h-[460px]">
|
||||
<Loading />
|
||||
</Center>
|
||||
)}
|
||||
<hr />
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { AppContext } from "@/src/pages/_app";
|
||||
import Stack from "@/twui/components/layout/Stack";
|
||||
import { useContext } from "react";
|
||||
import RelayShellView from "../(partials)/shell-view";
|
||||
|
||||
export default function RelayShellViewsSection() {
|
||||
const { pageProps } = useContext(AppContext);
|
||||
const { user } = pageProps;
|
||||
|
||||
return (
|
||||
<Stack className="w-full nested-grid-frame xl:grid-cols-2">
|
||||
<Stack className="grid-cell w-full">
|
||||
<RelayShellView paradigm="shell" />
|
||||
</Stack>
|
||||
{user.super_admin ? (
|
||||
<Stack className="grid-cell w-full">
|
||||
<RelayShellView paradigm="pm2-logs" />
|
||||
</Stack>
|
||||
) : null}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { Fragment, useContext } from "react";
|
||||
import { AppContext } from "@/src/pages/_app";
|
||||
import Divider from "@/twui/components/layout/Divider";
|
||||
import AdminHero from "@/src/components/general/admin/hero";
|
||||
import RelayShellViewsSection from "./(sections)/shell-views";
|
||||
|
||||
export default function Main() {
|
||||
const { pageProps } = useContext(AppContext);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<AdminHero
|
||||
title={`Shell`}
|
||||
description={<>Access the relay server shell</>}
|
||||
/>
|
||||
<Divider />
|
||||
<RelayShellViewsSection />
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import {
|
||||
NSQLITE_TURBOCI_ADMIN_USERS_PORTS,
|
||||
NSQLiteTables,
|
||||
} from "@/src/db/types";
|
||||
import { TtydInfoObject, User } from "@/src/types";
|
||||
import getNextAvailablePort from "@/src/utils/grab-next-available-port";
|
||||
import grabTtydCmd from "@/src/utils/grab-ttyd-cmd";
|
||||
import grabConnectedWebsocketUserdata from "@/src/websocket/(utils)/grab-connected-websocket-user-data";
|
||||
import BunSQLite from "@moduletrace/bun-sqlite";
|
||||
import { exec } from "child_process";
|
||||
|
||||
type Params = {
|
||||
user: User;
|
||||
cmd: string;
|
||||
};
|
||||
|
||||
export default async function grabTtydInfo({
|
||||
user,
|
||||
cmd,
|
||||
}: Params): Promise<TtydInfoObject> {
|
||||
const available_port = await getNextAvailablePort();
|
||||
|
||||
let url = `/ttyd/${available_port}`;
|
||||
|
||||
const ttyd_cmd = grabTtydCmd({
|
||||
cmd,
|
||||
port: available_port,
|
||||
});
|
||||
|
||||
const ttyd_exec = exec(ttyd_cmd.cmd);
|
||||
|
||||
await Bun.sleep(2000);
|
||||
|
||||
const connected_user_data = grabConnectedWebsocketUserdata({ user });
|
||||
|
||||
await BunSQLite.insert<
|
||||
NSQLITE_TURBOCI_ADMIN_USERS_PORTS,
|
||||
(typeof NSQLiteTables)[number]
|
||||
>({
|
||||
data: [{ user_id: user.id, port: available_port }],
|
||||
table: "users_ports",
|
||||
});
|
||||
|
||||
connected_user_data.child_processes.push(ttyd_exec);
|
||||
connected_user_data.ports.push(available_port);
|
||||
|
||||
return { port: available_port, url };
|
||||
}
|
||||
@@ -1,21 +1,10 @@
|
||||
import {
|
||||
NSQLITE_TURBOCI_ADMIN_USERS_PORTS,
|
||||
NSQLiteTables,
|
||||
} from "@/src/db/types";
|
||||
import { PrivateServerTtydParadigms, TtydInfoObject, User } from "@/src/types";
|
||||
import {
|
||||
NormalizedServerObject,
|
||||
ParsedDeploymentServiceConfig,
|
||||
} from "@/src/types/turboci";
|
||||
import grabDirNames from "@/src/utils/grab-dir-names";
|
||||
import getNextAvailablePort from "@/src/utils/grab-next-available-port";
|
||||
import grabSSHPrefix from "@/src/utils/grab-ssh-prefix";
|
||||
import grabTtydCmd from "@/src/utils/grab-ttyd-cmd";
|
||||
import grabConnectedWebsocketUserdata from "@/src/websocket/(utils)/grab-connected-websocket-user-data";
|
||||
import BunSQLite from "@moduletrace/bun-sqlite";
|
||||
import { exec } from "child_process";
|
||||
|
||||
const { TURBOCI_SSH_KEY_FILE } = grabDirNames();
|
||||
import grabTtydInfo from "./grab-ttyd-info";
|
||||
|
||||
type Params = {
|
||||
user: User;
|
||||
@@ -44,46 +33,15 @@ export default async function grabTtydServerInfo({
|
||||
);
|
||||
}
|
||||
|
||||
const available_port = await getNextAvailablePort();
|
||||
|
||||
let url = `/ttyd/${available_port}`;
|
||||
|
||||
let cmd = ``;
|
||||
|
||||
cmd += ` ${grabSSHPrefix()}`;
|
||||
|
||||
// if (paradigm == "logs") {
|
||||
// } else {
|
||||
// cmd += ` ssh -i ${TURBOCI_SSH_KEY_FILE}`;
|
||||
// }
|
||||
|
||||
cmd += ` root@${server?.private_ip}`;
|
||||
|
||||
if (paradigm == "logs" && final_log) {
|
||||
cmd += ` ${final_log}`;
|
||||
}
|
||||
|
||||
const ttyd_cmd = grabTtydCmd({
|
||||
cmd,
|
||||
port: available_port,
|
||||
});
|
||||
|
||||
const ttyd_exec = exec(ttyd_cmd.cmd);
|
||||
|
||||
await Bun.sleep(2000);
|
||||
|
||||
const connected_user_data = grabConnectedWebsocketUserdata({ user });
|
||||
|
||||
await BunSQLite.insert<
|
||||
NSQLITE_TURBOCI_ADMIN_USERS_PORTS,
|
||||
(typeof NSQLiteTables)[number]
|
||||
>({
|
||||
data: [{ user_id: user.id, port: available_port }],
|
||||
table: "users_ports",
|
||||
});
|
||||
|
||||
connected_user_data.child_processes.push(ttyd_exec);
|
||||
connected_user_data.ports.push(available_port);
|
||||
|
||||
return { port: available_port, url };
|
||||
return await grabTtydInfo({ cmd, user });
|
||||
}
|
||||
|
||||
@@ -24,10 +24,19 @@ export function AdminAsideLinks({ user }: Params) {
|
||||
url: "/admin/users",
|
||||
}
|
||||
: undefined,
|
||||
{
|
||||
title: "Shell",
|
||||
url: "/admin/shell",
|
||||
},
|
||||
{
|
||||
title: "Settings",
|
||||
url: "/admin/settings",
|
||||
},
|
||||
{ divider: true },
|
||||
{
|
||||
title: "Logout",
|
||||
url: "/auth/logout",
|
||||
},
|
||||
];
|
||||
|
||||
return links;
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import Main from "@/src/components/pages/admin/shell";
|
||||
import defaultAdminProps from "@/src/functions/pages/admin/default-admin-props";
|
||||
import Layout from "@/src/layouts/admin";
|
||||
import { GetServerSideProps } from "next";
|
||||
|
||||
export default function AdminDeploymentUsers() {
|
||||
return (
|
||||
<Layout>
|
||||
<Main />
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
export const getServerSideProps: GetServerSideProps = async (ctx) => {
|
||||
return await defaultAdminProps({
|
||||
ctx,
|
||||
});
|
||||
};
|
||||
@@ -79,6 +79,8 @@ export const WebSocketEvents = [
|
||||
"client:kill-port",
|
||||
"client:kill-all-ports",
|
||||
"client:service-server-shell",
|
||||
"client:relay-shell",
|
||||
"client:relay-pm2-logs",
|
||||
|
||||
"server:ping",
|
||||
"server:error",
|
||||
@@ -90,6 +92,8 @@ export const WebSocketEvents = [
|
||||
"server:killed-port",
|
||||
"server:killed-all-ports",
|
||||
"server:service-server-shell",
|
||||
"server:relay-shell",
|
||||
"server:relay-pm2-logs",
|
||||
] as const;
|
||||
|
||||
export type WebSocketDataType = {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { WebSocketMessageParam } from "@/src/types";
|
||||
import sendData from "../(utils)/send-data";
|
||||
import sendError from "../(utils)/send-error";
|
||||
import grabTtydInfo from "@/src/functions/ttyd/grab-ttyd-info";
|
||||
|
||||
export default async function socketClientRelayPm2Logs({
|
||||
ws,
|
||||
data,
|
||||
}: WebSocketMessageParam) {
|
||||
try {
|
||||
const user = ws.data.user;
|
||||
|
||||
const ttyd = await grabTtydInfo({ cmd: `pm2 logs`, user });
|
||||
|
||||
sendData(ws, {
|
||||
event: "server:relay-pm2-logs",
|
||||
ttyd,
|
||||
});
|
||||
} catch (error: any) {
|
||||
sendError(ws, "Relay Shell Error! " + error.message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { WebSocketMessageParam } from "@/src/types";
|
||||
import sendData from "../(utils)/send-data";
|
||||
import sendError from "../(utils)/send-error";
|
||||
import grabTtydInfo from "@/src/functions/ttyd/grab-ttyd-info";
|
||||
|
||||
export default async function socketClientRelayShell({
|
||||
ws,
|
||||
data,
|
||||
}: WebSocketMessageParam) {
|
||||
try {
|
||||
const user = ws.data.user;
|
||||
const cmd = data?.cmd;
|
||||
|
||||
if (!cmd) {
|
||||
throw new Error(`No Command Passed`);
|
||||
}
|
||||
|
||||
const ttyd = await grabTtydInfo({ cmd, user });
|
||||
|
||||
sendData(ws, {
|
||||
event: "server:relay-shell",
|
||||
ttyd,
|
||||
});
|
||||
} catch (error: any) {
|
||||
sendError(ws, "Relay Shell Error! " + error.message);
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,8 @@ import socketClientServiceServerLogs from "./events/client-service-server-logs";
|
||||
import socketClientKillPort from "./events/client-kill-port";
|
||||
import socketClientServiceServerShell from "./events/client-service-server-shell";
|
||||
import socketClientKillAllPorts from "./events/client-kill-all-ports";
|
||||
import socketClientRelayShell from "./events/client-relay-shell";
|
||||
import socketClientRelayPm2Logs from "./events/client-relay-pm2-logs";
|
||||
|
||||
type Param = {
|
||||
ws: ServerWebSocket<WebSocketData>;
|
||||
@@ -41,6 +43,7 @@ export default async function socketMessage({ ws, message }: Param) {
|
||||
});
|
||||
await socketClientPing(websocketMessageParams);
|
||||
break;
|
||||
|
||||
case "client:service-server-logs":
|
||||
debugLog({
|
||||
log: `${userRef} Getting Service Server Logs ...`,
|
||||
@@ -49,6 +52,7 @@ export default async function socketMessage({ ws, message }: Param) {
|
||||
});
|
||||
await socketClientServiceServerLogs(websocketMessageParams);
|
||||
break;
|
||||
|
||||
case "client:service-server-shell":
|
||||
debugLog({
|
||||
log: `${userRef} Getting Service Server Shell ...`,
|
||||
@@ -57,6 +61,25 @@ export default async function socketMessage({ ws, message }: Param) {
|
||||
});
|
||||
await socketClientServiceServerShell(websocketMessageParams);
|
||||
break;
|
||||
|
||||
case "client:relay-shell":
|
||||
debugLog({
|
||||
log: `${userRef} Getting Relay Server Shell ...`,
|
||||
addTime: true,
|
||||
label,
|
||||
});
|
||||
await socketClientRelayShell(websocketMessageParams);
|
||||
break;
|
||||
|
||||
case "client:relay-pm2-logs":
|
||||
debugLog({
|
||||
log: `${userRef} Getting Relay pm2 Logs ...`,
|
||||
addTime: true,
|
||||
label,
|
||||
});
|
||||
await socketClientRelayPm2Logs(websocketMessageParams);
|
||||
break;
|
||||
|
||||
case "client:kill-port":
|
||||
debugLog({
|
||||
log: `${userRef} Killing Port ${data.port} ...`,
|
||||
@@ -65,6 +88,7 @@ export default async function socketMessage({ ws, message }: Param) {
|
||||
});
|
||||
await socketClientKillPort(websocketMessageParams);
|
||||
break;
|
||||
|
||||
case "client:kill-all-ports":
|
||||
debugLog({
|
||||
log: `${userRef} Killing All Ports ...`,
|
||||
|
||||
Reference in New Issue
Block a user