mirror of
https://github.com/khairul169/vaulterm.git
synced 2025-04-28 16:49:39 +07:00
feat: add pve vnc session
This commit is contained in:
parent
bde42ca729
commit
2adde048b0
@ -1,13 +1,33 @@
|
|||||||
import { View, Text, ScrollView, Button } from "react-native";
|
import { View, ScrollView, Button } from "react-native";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { Stack } from "expo-router";
|
import { Stack } from "expo-router";
|
||||||
import InteractiveSession from "@/components/containers/interactive-session";
|
import InteractiveSession, {
|
||||||
|
InteractiveSessionProps,
|
||||||
|
} from "@/components/containers/interactive-session";
|
||||||
import PagerView from "@/components/ui/pager-view";
|
import PagerView from "@/components/ui/pager-view";
|
||||||
|
|
||||||
let nextSession = 1;
|
let nextSession = 1;
|
||||||
|
|
||||||
|
type Session = InteractiveSessionProps & { id: string };
|
||||||
|
|
||||||
const HomePage = () => {
|
const HomePage = () => {
|
||||||
const [sessions, setSessions] = useState<string[]>(["1"]);
|
const [sessions, setSessions] = useState<Session[]>([
|
||||||
|
{
|
||||||
|
id: "1",
|
||||||
|
type: "ssh",
|
||||||
|
params: { serverId: "1" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "2",
|
||||||
|
type: "pve",
|
||||||
|
params: { client: "vnc", serverId: "2" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "3",
|
||||||
|
type: "pve",
|
||||||
|
params: { client: "xtermjs", serverId: "3" },
|
||||||
|
},
|
||||||
|
]);
|
||||||
const [curSession, setSession] = useState(0);
|
const [curSession, setSession] = useState(0);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -21,18 +41,18 @@ const HomePage = () => {
|
|||||||
>
|
>
|
||||||
{sessions.map((session, idx) => (
|
{sessions.map((session, idx) => (
|
||||||
<View
|
<View
|
||||||
key={session}
|
key={session.id}
|
||||||
style={{ flexDirection: "row", alignItems: "center" }}
|
style={{ flexDirection: "row", alignItems: "center" }}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
title={"Session " + session}
|
title={"Session " + session.id}
|
||||||
color="#222"
|
color="#222"
|
||||||
onPress={() => setSession(idx)}
|
onPress={() => setSession(idx)}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
title="X"
|
title="X"
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
const newSessions = sessions.filter((s) => s !== session);
|
const newSessions = sessions.filter((s) => s.id !== session.id);
|
||||||
setSessions(newSessions);
|
setSessions(newSessions);
|
||||||
setSession(
|
setSession(
|
||||||
Math.min(Math.max(curSession, 0), newSessions.length - 1)
|
Math.min(Math.max(curSession, 0), newSessions.length - 1)
|
||||||
@ -42,23 +62,19 @@ const HomePage = () => {
|
|||||||
</View>
|
</View>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
<Button
|
{/* <Button
|
||||||
title="[ + ]"
|
title="[ + ]"
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
nextSession += 1;
|
nextSession += 1;
|
||||||
setSessions([...sessions, nextSession.toString()]);
|
setSessions([...sessions, nextSession.toString()]);
|
||||||
setSession(sessions.length);
|
setSession(sessions.length);
|
||||||
}}
|
}}
|
||||||
/>
|
/> */}
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
<PagerView style={{ flex: 1 }} page={curSession}>
|
<PagerView style={{ flex: 1 }} page={curSession}>
|
||||||
{sessions.map((session) => (
|
{sessions.map((session) => (
|
||||||
<InteractiveSession
|
<InteractiveSession key={session.id} {...session} />
|
||||||
key={session}
|
|
||||||
type="ssh"
|
|
||||||
options={{ serverId: session }}
|
|
||||||
/>
|
|
||||||
))}
|
))}
|
||||||
</PagerView>
|
</PagerView>
|
||||||
</View>
|
</View>
|
||||||
|
@ -1,32 +1,45 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Terminal from "./terminal";
|
import Terminal from "./terminal";
|
||||||
import { BASE_WS_URL } from "@/lib/api";
|
import { BASE_WS_URL } from "@/lib/api";
|
||||||
|
import VNCViewer from "./vncviewer";
|
||||||
|
|
||||||
type SSHSessionProps = {
|
type SSHSessionProps = {
|
||||||
type: "ssh";
|
type: "ssh";
|
||||||
options: {
|
params: {
|
||||||
serverId: string;
|
serverId: string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
type Props = SSHSessionProps;
|
type PVESessionProps = {
|
||||||
|
type: "pve";
|
||||||
|
params: {
|
||||||
|
client: "vnc" | "xtermjs";
|
||||||
|
serverId: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type InteractiveSessionProps = SSHSessionProps | PVESessionProps;
|
||||||
|
|
||||||
|
const InteractiveSession = ({ type, params }: InteractiveSessionProps) => {
|
||||||
|
const query = new URLSearchParams({
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
|
||||||
const InteractiveSession = ({ type, options }: Props) => {
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "ssh":
|
case "ssh":
|
||||||
const params = new URLSearchParams({
|
return <Terminal wsUrl={`${BASE_WS_URL}/ws/ssh?${query}`} />;
|
||||||
serverId: options.serverId,
|
|
||||||
token: "token",
|
case "pve":
|
||||||
});
|
const url = `${BASE_WS_URL}/ws/pve?${query}`;
|
||||||
return (
|
return params.client === "vnc" ? (
|
||||||
<Terminal client="xtermjs" wsUrl={`${BASE_WS_URL}/ws/ssh?${params}`} />
|
<VNCViewer url={url} />
|
||||||
|
) : (
|
||||||
|
<Terminal wsUrl={url} />
|
||||||
);
|
);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Error("Unknown interactive session type");
|
throw new Error("Unknown interactive session type");
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default InteractiveSession;
|
export default InteractiveSession;
|
||||||
|
@ -27,13 +27,13 @@ const Keys = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type XTermJsProps = {
|
type XTermJsProps = {
|
||||||
client: "xtermjs";
|
client?: "xtermjs";
|
||||||
wsUrl: string;
|
wsUrl: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type TerminalProps = ComponentPropsWithoutRef<typeof View> & XTermJsProps;
|
type TerminalProps = ComponentPropsWithoutRef<typeof View> & XTermJsProps;
|
||||||
|
|
||||||
const Terminal = ({ client, style, ...props }: TerminalProps) => {
|
const Terminal = ({ client = "xtermjs", style, ...props }: TerminalProps) => {
|
||||||
const xtermRef = React.useRef<XTermRef>(null);
|
const xtermRef = React.useRef<XTermRef>(null);
|
||||||
|
|
||||||
const send = (data: string) => {
|
const send = (data: string) => {
|
||||||
|
90
frontend/components/containers/vncviewer.tsx
Normal file
90
frontend/components/containers/vncviewer.tsx
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
"use dom";
|
||||||
|
|
||||||
|
import React, { useEffect, useRef } from "react";
|
||||||
|
|
||||||
|
type VNCViewerProps = {
|
||||||
|
url: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const VNCViewer = ({ ...props }: VNCViewerProps) => {
|
||||||
|
const screenRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let clean = () => {};
|
||||||
|
|
||||||
|
(async function () {
|
||||||
|
// Dynamically load noVNC library
|
||||||
|
const { default: RFB } = await import("@novnc/novnc/lib/rfb");
|
||||||
|
|
||||||
|
const rfb = new RFB(screenRef.current!, props.url);
|
||||||
|
rfb.scaleViewport = true;
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
const ws: WebSocket = rfb._sock._websocket;
|
||||||
|
let password: string | null = null;
|
||||||
|
|
||||||
|
const onConnect = () => {
|
||||||
|
// console.log("Connected");
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDisconnect = () => {
|
||||||
|
// console.log("Disconnected");
|
||||||
|
};
|
||||||
|
|
||||||
|
const onMessage = (e: MessageEvent) => {
|
||||||
|
const msg = String(e.data);
|
||||||
|
|
||||||
|
// Capture password from server
|
||||||
|
if (msg.startsWith("\x01")) {
|
||||||
|
password = msg.substring(1);
|
||||||
|
ws.removeEventListener("message", onMessage);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onCredentialsRequired = () => {
|
||||||
|
rfb.sendCredentials({
|
||||||
|
password: password || "",
|
||||||
|
username: "",
|
||||||
|
target: "",
|
||||||
|
});
|
||||||
|
password = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
// const onDesktopName = (e: CustomEvent<{ name: string }>) => {
|
||||||
|
// console.log("Desktop name:", e.detail.name);
|
||||||
|
// };
|
||||||
|
|
||||||
|
ws.addEventListener("message", onMessage);
|
||||||
|
rfb.addEventListener("connect", onConnect);
|
||||||
|
rfb.addEventListener("disconnect", onDisconnect);
|
||||||
|
rfb.addEventListener("credentialsrequired", onCredentialsRequired);
|
||||||
|
// rfb.addEventListener("desktopname", onDesktopName);
|
||||||
|
|
||||||
|
// Hack: trigger scale update on visibility change
|
||||||
|
const observer = new ResizeObserver(([entry]) => {
|
||||||
|
if (entry.contentRect.width > 0 && rfb.scaleViewport) {
|
||||||
|
(rfb as any)._updateScale();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
observer.observe(screenRef.current!);
|
||||||
|
|
||||||
|
clean = () => {
|
||||||
|
ws.removeEventListener("message", onMessage);
|
||||||
|
rfb.disconnect();
|
||||||
|
rfb.removeEventListener("connect", onConnect);
|
||||||
|
rfb.removeEventListener("disconnect", onDisconnect);
|
||||||
|
rfb.removeEventListener("credentialsrequired", onCredentialsRequired);
|
||||||
|
// rfb.removeEventListener("desktopname", onDesktopName);
|
||||||
|
observer.disconnect();
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
clean();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return <div ref={screenRef} style={{ width: "100%", height: "100vh" }}></div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default VNCViewer;
|
@ -1,10 +1,11 @@
|
|||||||
"use dom";
|
"use dom";
|
||||||
|
|
||||||
import React, { CSSProperties, FC, forwardRef, useEffect, useRef } from "react";
|
import React, { CSSProperties, forwardRef, useEffect, useRef } from "react";
|
||||||
import {
|
import {
|
||||||
DOMImperativeFactory,
|
DOMImperativeFactory,
|
||||||
DOMProps,
|
DOMProps,
|
||||||
useDOMImperativeHandle,
|
useDOMImperativeHandle,
|
||||||
|
IS_DOM,
|
||||||
} from "expo/dom";
|
} from "expo/dom";
|
||||||
import { Terminal as XTerm } from "@xterm/xterm";
|
import { Terminal as XTerm } from "@xterm/xterm";
|
||||||
import "@xterm/xterm/css/xterm.css";
|
import "@xterm/xterm/css/xterm.css";
|
||||||
@ -19,9 +20,6 @@ type XTermJsProps = {
|
|||||||
wsUrl: string;
|
wsUrl: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
const IS_DOM = typeof ReactNativeWebView !== "undefined";
|
|
||||||
|
|
||||||
export interface XTermRef extends DOMImperativeFactory {
|
export interface XTermRef extends DOMImperativeFactory {
|
||||||
send: (...args: JSONValue[]) => void;
|
send: (...args: JSONValue[]) => void;
|
||||||
}
|
}
|
||||||
@ -89,6 +87,15 @@ const XTermJs = forwardRef<XTermRef, XTermJsProps>((props, ref) => {
|
|||||||
ws.addEventListener("close", onClose);
|
ws.addEventListener("close", onClose);
|
||||||
xterm.onResize(resizeTerminal);
|
xterm.onResize(resizeTerminal);
|
||||||
window.addEventListener("resize", onResize);
|
window.addEventListener("resize", onResize);
|
||||||
|
|
||||||
|
// Hack: trigger scale update on visibility change
|
||||||
|
const observer = new ResizeObserver(([entry]) => {
|
||||||
|
if (entry.contentRect.width > 0) {
|
||||||
|
fitAddon.fit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
observer.observe(containerRef.current!);
|
||||||
|
|
||||||
onLoad?.();
|
onLoad?.();
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
@ -100,6 +107,8 @@ const XTermJs = forwardRef<XTermRef, XTermJsProps>((props, ref) => {
|
|||||||
ws.removeEventListener("open", onOpen);
|
ws.removeEventListener("open", onOpen);
|
||||||
ws.removeEventListener("close", onClose);
|
ws.removeEventListener("close", onClose);
|
||||||
window.removeEventListener("resize", onResize);
|
window.removeEventListener("resize", onResize);
|
||||||
|
|
||||||
|
observer.disconnect();
|
||||||
};
|
};
|
||||||
}, [wsUrl]);
|
}, [wsUrl]);
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@expo/vector-icons": "^14.0.2",
|
"@expo/vector-icons": "^14.0.2",
|
||||||
|
"@novnc/novnc": "^1.5.0",
|
||||||
"@react-navigation/bottom-tabs": "7.0.0-rc.36",
|
"@react-navigation/bottom-tabs": "7.0.0-rc.36",
|
||||||
"@react-navigation/native": "7.0.0-rc.21",
|
"@react-navigation/native": "7.0.0-rc.21",
|
||||||
"@xterm/addon-attach": "^0.11.0",
|
"@xterm/addon-attach": "^0.11.0",
|
||||||
@ -48,6 +49,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.25.2",
|
"@babel/core": "^7.25.2",
|
||||||
"@types/jest": "^29.5.12",
|
"@types/jest": "^29.5.12",
|
||||||
|
"@types/novnc__novnc": "^1.5.0",
|
||||||
"@types/react": "~18.3.12",
|
"@types/react": "~18.3.12",
|
||||||
"@types/react-test-renderer": "^18.3.0",
|
"@types/react-test-renderer": "^18.3.0",
|
||||||
"jest": "^29.2.1",
|
"jest": "^29.2.1",
|
||||||
|
125
frontend/pnpm-lock.yaml
generated
125
frontend/pnpm-lock.yaml
generated
@ -11,6 +11,9 @@ importers:
|
|||||||
'@expo/vector-icons':
|
'@expo/vector-icons':
|
||||||
specifier: ^14.0.2
|
specifier: ^14.0.2
|
||||||
version: 14.0.4
|
version: 14.0.4
|
||||||
|
'@novnc/novnc':
|
||||||
|
specifier: ^1.5.0
|
||||||
|
version: 1.5.0
|
||||||
'@react-navigation/bottom-tabs':
|
'@react-navigation/bottom-tabs':
|
||||||
specifier: 7.0.0-rc.36
|
specifier: 7.0.0-rc.36
|
||||||
version: 7.0.0-rc.36(@react-navigation/native@7.0.0-rc.21(react-native@0.76.1(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.12)(react@18.3.1))(react@18.3.1))(react-native-safe-area-context@4.12.0(react-native@0.76.1(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.12)(react@18.3.1))(react@18.3.1))(react-native-screens@4.0.0-beta.16(react-native@0.76.1(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.12)(react@18.3.1))(react@18.3.1))(react-native@0.76.1(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.12)(react@18.3.1))(react@18.3.1)
|
version: 7.0.0-rc.36(@react-navigation/native@7.0.0-rc.21(react-native@0.76.1(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.12)(react@18.3.1))(react@18.3.1))(react-native-safe-area-context@4.12.0(react-native@0.76.1(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.12)(react@18.3.1))(react@18.3.1))(react-native-screens@4.0.0-beta.16(react-native@0.76.1(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.12)(react@18.3.1))(react@18.3.1))(react-native@0.76.1(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.12)(react@18.3.1))(react@18.3.1)
|
||||||
@ -99,6 +102,9 @@ importers:
|
|||||||
'@types/jest':
|
'@types/jest':
|
||||||
specifier: ^29.5.12
|
specifier: ^29.5.12
|
||||||
version: 29.5.14
|
version: 29.5.14
|
||||||
|
'@types/novnc__novnc':
|
||||||
|
specifier: ^1.5.0
|
||||||
|
version: 1.5.0
|
||||||
'@types/react':
|
'@types/react':
|
||||||
specifier: ~18.3.12
|
specifier: ~18.3.12
|
||||||
version: 18.3.12
|
version: 18.3.12
|
||||||
@ -107,10 +113,10 @@ importers:
|
|||||||
version: 18.3.0
|
version: 18.3.0
|
||||||
jest:
|
jest:
|
||||||
specifier: ^29.2.1
|
specifier: ^29.2.1
|
||||||
version: 29.7.0(@types/node@22.9.0)
|
version: 29.7.0(@types/node@22.9.0)(babel-plugin-macros@3.1.0)
|
||||||
jest-expo:
|
jest-expo:
|
||||||
specifier: ~52.0.0-preview.3
|
specifier: ~52.0.0-preview.3
|
||||||
version: 52.0.0-preview.3(@babel/core@7.26.0)(expo@52.0.0-preview.19(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(react-native-webview@13.12.2(react-native@0.76.1(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.12)(react@18.3.1))(react@18.3.1))(react-native@0.76.1(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.12)(react@18.3.1))(react@18.3.1))(jest@29.7.0(@types/node@22.9.0))(react-dom@18.3.1(react@18.3.1))(react-native@0.76.1(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.12)(react@18.3.1))(react@18.3.1)(webpack@5.96.1)
|
version: 52.0.0-preview.3(@babel/core@7.26.0)(expo@52.0.0-preview.19(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(react-native-webview@13.12.2(react-native@0.76.1(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.12)(react@18.3.1))(react@18.3.1))(react-native@0.76.1(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.12)(react@18.3.1))(react@18.3.1))(jest@29.7.0(@types/node@22.9.0)(babel-plugin-macros@3.1.0))(react-dom@18.3.1(react@18.3.1))(react-native@0.76.1(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.12)(react@18.3.1))(react@18.3.1)(webpack@5.96.1)
|
||||||
react-test-renderer:
|
react-test-renderer:
|
||||||
specifier: 18.3.1
|
specifier: 18.3.1
|
||||||
version: 18.3.1(react@18.3.1)
|
version: 18.3.1(react@18.3.1)
|
||||||
@ -1060,6 +1066,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
|
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
|
|
||||||
|
'@novnc/novnc@1.5.0':
|
||||||
|
resolution: {integrity: sha512-4yGHOtUCnEJUCsgEt/L78eeJu00kthurLBWXFiaXfonNx0pzbs6R/3gJb1byZe6iAE8V9MF0syQb0xIL8MSOtQ==}
|
||||||
|
|
||||||
'@npmcli/fs@3.1.1':
|
'@npmcli/fs@3.1.1':
|
||||||
resolution: {integrity: sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==}
|
resolution: {integrity: sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==}
|
||||||
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
|
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
|
||||||
@ -1298,6 +1307,12 @@ packages:
|
|||||||
'@types/node@22.9.0':
|
'@types/node@22.9.0':
|
||||||
resolution: {integrity: sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==}
|
resolution: {integrity: sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==}
|
||||||
|
|
||||||
|
'@types/novnc__novnc@1.5.0':
|
||||||
|
resolution: {integrity: sha512-9DrDJK1hUT6Cbp4t03IsU/DsR6ndnIrDgZVrzITvspldHQ7n81F3wUDfq89zmPM3wg4GErH11IQa0QuTgLMf+w==}
|
||||||
|
|
||||||
|
'@types/parse-json@4.0.2':
|
||||||
|
resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==}
|
||||||
|
|
||||||
'@types/prop-types@15.7.13':
|
'@types/prop-types@15.7.13':
|
||||||
resolution: {integrity: sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==}
|
resolution: {integrity: sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==}
|
||||||
|
|
||||||
@ -1568,6 +1583,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==}
|
resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==}
|
||||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
|
|
||||||
|
babel-plugin-macros@3.1.0:
|
||||||
|
resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==}
|
||||||
|
engines: {node: '>=10', npm: '>=6'}
|
||||||
|
|
||||||
babel-plugin-polyfill-corejs2@0.4.11:
|
babel-plugin-polyfill-corejs2@0.4.11:
|
||||||
resolution: {integrity: sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==}
|
resolution: {integrity: sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -1884,6 +1903,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==}
|
resolution: {integrity: sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
|
cosmiconfig@7.1.0:
|
||||||
|
resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
create-jest@29.7.0:
|
create-jest@29.7.0:
|
||||||
resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==}
|
resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==}
|
||||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||||
@ -2598,6 +2621,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==}
|
resolution: {integrity: sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
|
import-fresh@3.3.0:
|
||||||
|
resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
import-local@3.2.0:
|
import-local@3.2.0:
|
||||||
resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==}
|
resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@ -3458,6 +3485,10 @@ packages:
|
|||||||
package-json-from-dist@1.0.1:
|
package-json-from-dist@1.0.1:
|
||||||
resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
|
resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
|
||||||
|
|
||||||
|
parent-module@1.0.1:
|
||||||
|
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
parse-json@4.0.0:
|
parse-json@4.0.0:
|
||||||
resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==}
|
resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
@ -3807,6 +3838,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==}
|
resolution: {integrity: sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
|
resolve-from@4.0.0:
|
||||||
|
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
resolve-from@5.0.0:
|
resolve-from@5.0.0:
|
||||||
resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
|
resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@ -4575,6 +4610,10 @@ packages:
|
|||||||
yallist@4.0.0:
|
yallist@4.0.0:
|
||||||
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
|
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
|
||||||
|
|
||||||
|
yaml@1.10.2:
|
||||||
|
resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==}
|
||||||
|
engines: {node: '>= 6'}
|
||||||
|
|
||||||
yargs-parser@21.1.1:
|
yargs-parser@21.1.1:
|
||||||
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
|
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
@ -5849,7 +5888,7 @@ snapshots:
|
|||||||
jest-util: 29.7.0
|
jest-util: 29.7.0
|
||||||
slash: 3.0.0
|
slash: 3.0.0
|
||||||
|
|
||||||
'@jest/core@29.7.0':
|
'@jest/core@29.7.0(babel-plugin-macros@3.1.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@jest/console': 29.7.0
|
'@jest/console': 29.7.0
|
||||||
'@jest/reporters': 29.7.0
|
'@jest/reporters': 29.7.0
|
||||||
@ -5863,7 +5902,7 @@ snapshots:
|
|||||||
exit: 0.1.2
|
exit: 0.1.2
|
||||||
graceful-fs: 4.2.11
|
graceful-fs: 4.2.11
|
||||||
jest-changed-files: 29.7.0
|
jest-changed-files: 29.7.0
|
||||||
jest-config: 29.7.0(@types/node@22.9.0)
|
jest-config: 29.7.0(@types/node@22.9.0)(babel-plugin-macros@3.1.0)
|
||||||
jest-haste-map: 29.7.0
|
jest-haste-map: 29.7.0
|
||||||
jest-message-util: 29.7.0
|
jest-message-util: 29.7.0
|
||||||
jest-regex-util: 29.6.3
|
jest-regex-util: 29.6.3
|
||||||
@ -6040,6 +6079,8 @@ snapshots:
|
|||||||
'@nodelib/fs.scandir': 2.1.5
|
'@nodelib/fs.scandir': 2.1.5
|
||||||
fastq: 1.17.1
|
fastq: 1.17.1
|
||||||
|
|
||||||
|
'@novnc/novnc@1.5.0': {}
|
||||||
|
|
||||||
'@npmcli/fs@3.1.1':
|
'@npmcli/fs@3.1.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
semver: 7.6.3
|
semver: 7.6.3
|
||||||
@ -6399,6 +6440,11 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
undici-types: 6.19.8
|
undici-types: 6.19.8
|
||||||
|
|
||||||
|
'@types/novnc__novnc@1.5.0': {}
|
||||||
|
|
||||||
|
'@types/parse-json@4.0.2':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@types/prop-types@15.7.13': {}
|
'@types/prop-types@15.7.13': {}
|
||||||
|
|
||||||
'@types/react-test-renderer@18.3.0':
|
'@types/react-test-renderer@18.3.0':
|
||||||
@ -6690,6 +6736,13 @@ snapshots:
|
|||||||
'@types/babel__core': 7.20.5
|
'@types/babel__core': 7.20.5
|
||||||
'@types/babel__traverse': 7.20.6
|
'@types/babel__traverse': 7.20.6
|
||||||
|
|
||||||
|
babel-plugin-macros@3.1.0:
|
||||||
|
dependencies:
|
||||||
|
'@babel/runtime': 7.26.0
|
||||||
|
cosmiconfig: 7.1.0
|
||||||
|
resolve: 1.22.8
|
||||||
|
optional: true
|
||||||
|
|
||||||
babel-plugin-polyfill-corejs2@0.4.11(@babel/core@7.26.0):
|
babel-plugin-polyfill-corejs2@0.4.11(@babel/core@7.26.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/compat-data': 7.26.2
|
'@babel/compat-data': 7.26.2
|
||||||
@ -7044,13 +7097,22 @@ snapshots:
|
|||||||
js-yaml: 3.14.1
|
js-yaml: 3.14.1
|
||||||
parse-json: 4.0.0
|
parse-json: 4.0.0
|
||||||
|
|
||||||
create-jest@29.7.0(@types/node@22.9.0):
|
cosmiconfig@7.1.0:
|
||||||
|
dependencies:
|
||||||
|
'@types/parse-json': 4.0.2
|
||||||
|
import-fresh: 3.3.0
|
||||||
|
parse-json: 5.2.0
|
||||||
|
path-type: 4.0.0
|
||||||
|
yaml: 1.10.2
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
create-jest@29.7.0(@types/node@22.9.0)(babel-plugin-macros@3.1.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@jest/types': 29.6.3
|
'@jest/types': 29.6.3
|
||||||
chalk: 4.1.2
|
chalk: 4.1.2
|
||||||
exit: 0.1.2
|
exit: 0.1.2
|
||||||
graceful-fs: 4.2.11
|
graceful-fs: 4.2.11
|
||||||
jest-config: 29.7.0(@types/node@22.9.0)
|
jest-config: 29.7.0(@types/node@22.9.0)(babel-plugin-macros@3.1.0)
|
||||||
jest-util: 29.7.0
|
jest-util: 29.7.0
|
||||||
prompts: 2.4.2
|
prompts: 2.4.2
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@ -7121,7 +7183,9 @@ snapshots:
|
|||||||
|
|
||||||
decode-uri-component@0.2.2: {}
|
decode-uri-component@0.2.2: {}
|
||||||
|
|
||||||
dedent@1.5.3: {}
|
dedent@1.5.3(babel-plugin-macros@3.1.0):
|
||||||
|
optionalDependencies:
|
||||||
|
babel-plugin-macros: 3.1.0
|
||||||
|
|
||||||
deep-extend@0.6.0: {}
|
deep-extend@0.6.0: {}
|
||||||
|
|
||||||
@ -7790,6 +7854,12 @@ snapshots:
|
|||||||
caller-path: 2.0.0
|
caller-path: 2.0.0
|
||||||
resolve-from: 3.0.0
|
resolve-from: 3.0.0
|
||||||
|
|
||||||
|
import-fresh@3.3.0:
|
||||||
|
dependencies:
|
||||||
|
parent-module: 1.0.1
|
||||||
|
resolve-from: 4.0.0
|
||||||
|
optional: true
|
||||||
|
|
||||||
import-local@3.2.0:
|
import-local@3.2.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
pkg-dir: 4.2.0
|
pkg-dir: 4.2.0
|
||||||
@ -7944,7 +8014,7 @@ snapshots:
|
|||||||
jest-util: 29.7.0
|
jest-util: 29.7.0
|
||||||
p-limit: 3.1.0
|
p-limit: 3.1.0
|
||||||
|
|
||||||
jest-circus@29.7.0:
|
jest-circus@29.7.0(babel-plugin-macros@3.1.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@jest/environment': 29.7.0
|
'@jest/environment': 29.7.0
|
||||||
'@jest/expect': 29.7.0
|
'@jest/expect': 29.7.0
|
||||||
@ -7953,7 +8023,7 @@ snapshots:
|
|||||||
'@types/node': 22.9.0
|
'@types/node': 22.9.0
|
||||||
chalk: 4.1.2
|
chalk: 4.1.2
|
||||||
co: 4.6.0
|
co: 4.6.0
|
||||||
dedent: 1.5.3
|
dedent: 1.5.3(babel-plugin-macros@3.1.0)
|
||||||
is-generator-fn: 2.1.0
|
is-generator-fn: 2.1.0
|
||||||
jest-each: 29.7.0
|
jest-each: 29.7.0
|
||||||
jest-matcher-utils: 29.7.0
|
jest-matcher-utils: 29.7.0
|
||||||
@ -7970,16 +8040,16 @@ snapshots:
|
|||||||
- babel-plugin-macros
|
- babel-plugin-macros
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
jest-cli@29.7.0(@types/node@22.9.0):
|
jest-cli@29.7.0(@types/node@22.9.0)(babel-plugin-macros@3.1.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@jest/core': 29.7.0
|
'@jest/core': 29.7.0(babel-plugin-macros@3.1.0)
|
||||||
'@jest/test-result': 29.7.0
|
'@jest/test-result': 29.7.0
|
||||||
'@jest/types': 29.6.3
|
'@jest/types': 29.6.3
|
||||||
chalk: 4.1.2
|
chalk: 4.1.2
|
||||||
create-jest: 29.7.0(@types/node@22.9.0)
|
create-jest: 29.7.0(@types/node@22.9.0)(babel-plugin-macros@3.1.0)
|
||||||
exit: 0.1.2
|
exit: 0.1.2
|
||||||
import-local: 3.2.0
|
import-local: 3.2.0
|
||||||
jest-config: 29.7.0(@types/node@22.9.0)
|
jest-config: 29.7.0(@types/node@22.9.0)(babel-plugin-macros@3.1.0)
|
||||||
jest-util: 29.7.0
|
jest-util: 29.7.0
|
||||||
jest-validate: 29.7.0
|
jest-validate: 29.7.0
|
||||||
yargs: 17.7.2
|
yargs: 17.7.2
|
||||||
@ -7989,7 +8059,7 @@ snapshots:
|
|||||||
- supports-color
|
- supports-color
|
||||||
- ts-node
|
- ts-node
|
||||||
|
|
||||||
jest-config@29.7.0(@types/node@22.9.0):
|
jest-config@29.7.0(@types/node@22.9.0)(babel-plugin-macros@3.1.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.26.0
|
'@babel/core': 7.26.0
|
||||||
'@jest/test-sequencer': 29.7.0
|
'@jest/test-sequencer': 29.7.0
|
||||||
@ -8000,7 +8070,7 @@ snapshots:
|
|||||||
deepmerge: 4.3.1
|
deepmerge: 4.3.1
|
||||||
glob: 7.2.3
|
glob: 7.2.3
|
||||||
graceful-fs: 4.2.11
|
graceful-fs: 4.2.11
|
||||||
jest-circus: 29.7.0
|
jest-circus: 29.7.0(babel-plugin-macros@3.1.0)
|
||||||
jest-environment-node: 29.7.0
|
jest-environment-node: 29.7.0
|
||||||
jest-get-type: 29.6.3
|
jest-get-type: 29.6.3
|
||||||
jest-regex-util: 29.6.3
|
jest-regex-util: 29.6.3
|
||||||
@ -8062,7 +8132,7 @@ snapshots:
|
|||||||
jest-mock: 29.7.0
|
jest-mock: 29.7.0
|
||||||
jest-util: 29.7.0
|
jest-util: 29.7.0
|
||||||
|
|
||||||
jest-expo@52.0.0-preview.3(@babel/core@7.26.0)(expo@52.0.0-preview.19(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(react-native-webview@13.12.2(react-native@0.76.1(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.12)(react@18.3.1))(react@18.3.1))(react-native@0.76.1(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.12)(react@18.3.1))(react@18.3.1))(jest@29.7.0(@types/node@22.9.0))(react-dom@18.3.1(react@18.3.1))(react-native@0.76.1(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.12)(react@18.3.1))(react@18.3.1)(webpack@5.96.1):
|
jest-expo@52.0.0-preview.3(@babel/core@7.26.0)(expo@52.0.0-preview.19(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(react-native-webview@13.12.2(react-native@0.76.1(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.12)(react@18.3.1))(react@18.3.1))(react-native@0.76.1(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.12)(react@18.3.1))(react@18.3.1))(jest@29.7.0(@types/node@22.9.0)(babel-plugin-macros@3.1.0))(react-dom@18.3.1(react@18.3.1))(react-native@0.76.1(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.12)(react@18.3.1))(react@18.3.1)(webpack@5.96.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@expo/config': 10.0.2
|
'@expo/config': 10.0.2
|
||||||
'@expo/json-file': 9.0.0
|
'@expo/json-file': 9.0.0
|
||||||
@ -8075,7 +8145,7 @@ snapshots:
|
|||||||
jest-environment-jsdom: 29.7.0
|
jest-environment-jsdom: 29.7.0
|
||||||
jest-snapshot: 29.7.0
|
jest-snapshot: 29.7.0
|
||||||
jest-watch-select-projects: 2.0.0
|
jest-watch-select-projects: 2.0.0
|
||||||
jest-watch-typeahead: 2.2.1(jest@29.7.0(@types/node@22.9.0))
|
jest-watch-typeahead: 2.2.1(jest@29.7.0(@types/node@22.9.0)(babel-plugin-macros@3.1.0))
|
||||||
json5: 2.2.3
|
json5: 2.2.3
|
||||||
lodash: 4.17.21
|
lodash: 4.17.21
|
||||||
react-native: 0.76.1(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.12)(react@18.3.1)
|
react-native: 0.76.1(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@types/react@18.3.12)(react@18.3.1)
|
||||||
@ -8270,11 +8340,11 @@ snapshots:
|
|||||||
chalk: 3.0.0
|
chalk: 3.0.0
|
||||||
prompts: 2.4.2
|
prompts: 2.4.2
|
||||||
|
|
||||||
jest-watch-typeahead@2.2.1(jest@29.7.0(@types/node@22.9.0)):
|
jest-watch-typeahead@2.2.1(jest@29.7.0(@types/node@22.9.0)(babel-plugin-macros@3.1.0)):
|
||||||
dependencies:
|
dependencies:
|
||||||
ansi-escapes: 6.2.1
|
ansi-escapes: 6.2.1
|
||||||
chalk: 4.1.2
|
chalk: 4.1.2
|
||||||
jest: 29.7.0(@types/node@22.9.0)
|
jest: 29.7.0(@types/node@22.9.0)(babel-plugin-macros@3.1.0)
|
||||||
jest-regex-util: 29.6.3
|
jest-regex-util: 29.6.3
|
||||||
jest-watcher: 29.7.0
|
jest-watcher: 29.7.0
|
||||||
slash: 5.1.0
|
slash: 5.1.0
|
||||||
@ -8305,12 +8375,12 @@ snapshots:
|
|||||||
merge-stream: 2.0.0
|
merge-stream: 2.0.0
|
||||||
supports-color: 8.1.1
|
supports-color: 8.1.1
|
||||||
|
|
||||||
jest@29.7.0(@types/node@22.9.0):
|
jest@29.7.0(@types/node@22.9.0)(babel-plugin-macros@3.1.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@jest/core': 29.7.0
|
'@jest/core': 29.7.0(babel-plugin-macros@3.1.0)
|
||||||
'@jest/types': 29.6.3
|
'@jest/types': 29.6.3
|
||||||
import-local: 3.2.0
|
import-local: 3.2.0
|
||||||
jest-cli: 29.7.0(@types/node@22.9.0)
|
jest-cli: 29.7.0(@types/node@22.9.0)(babel-plugin-macros@3.1.0)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@types/node'
|
- '@types/node'
|
||||||
- babel-plugin-macros
|
- babel-plugin-macros
|
||||||
@ -8944,6 +9014,11 @@ snapshots:
|
|||||||
|
|
||||||
package-json-from-dist@1.0.1: {}
|
package-json-from-dist@1.0.1: {}
|
||||||
|
|
||||||
|
parent-module@1.0.1:
|
||||||
|
dependencies:
|
||||||
|
callsites: 3.1.0
|
||||||
|
optional: true
|
||||||
|
|
||||||
parse-json@4.0.0:
|
parse-json@4.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
error-ex: 1.3.2
|
error-ex: 1.3.2
|
||||||
@ -9355,6 +9430,9 @@ snapshots:
|
|||||||
|
|
||||||
resolve-from@3.0.0: {}
|
resolve-from@3.0.0: {}
|
||||||
|
|
||||||
|
resolve-from@4.0.0:
|
||||||
|
optional: true
|
||||||
|
|
||||||
resolve-from@5.0.0: {}
|
resolve-from@5.0.0: {}
|
||||||
|
|
||||||
resolve-workspace-root@2.0.0: {}
|
resolve-workspace-root@2.0.0: {}
|
||||||
@ -10072,6 +10150,9 @@ snapshots:
|
|||||||
|
|
||||||
yallist@4.0.0: {}
|
yallist@4.0.0: {}
|
||||||
|
|
||||||
|
yaml@1.10.2:
|
||||||
|
optional: true
|
||||||
|
|
||||||
yargs-parser@21.1.1: {}
|
yargs-parser@21.1.1: {}
|
||||||
|
|
||||||
yargs@17.7.2:
|
yargs@17.7.2:
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
"extends": "expo/tsconfig.base",
|
"extends": "expo/tsconfig.base",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"strict": true,
|
"strict": true,
|
||||||
|
"module": "esnext",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": [
|
"@/*": [
|
||||||
"./*"
|
"./*"
|
||||||
|
@ -89,7 +89,7 @@ func (pve *PVEServer) GetAccessTicket() (*PVEAccessTicket, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type PVEInstance struct {
|
type PVEInstance struct {
|
||||||
Type string
|
Type string // "qemu" | "lxc"
|
||||||
Node string
|
Node string
|
||||||
VMID string
|
VMID string
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,19 @@ type PVEConfig struct {
|
|||||||
PrivateKeyPassphrase string
|
PrivateKeyPassphrase string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pve *PVEServer) NewTerminalSession(c *websocket.Conn, access *PVEAccessTicket, instance *PVEInstance, ticket *PVEVNCTicketData) error {
|
// https://github.com/proxmox/pve-xtermjs/blob/master/README
|
||||||
|
|
||||||
|
func (pve *PVEServer) NewTerminalSession(c *websocket.Conn, instance *PVEInstance) error {
|
||||||
|
access, err := pve.GetAccessTicket()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ticket, err := pve.GetVNCTicket(access, instance, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
url := fmt.Sprintf("wss://%s:%d/api2/json/nodes/%s/%s/%s/vncwebsocket?port=%s&vncticket=%s",
|
url := fmt.Sprintf("wss://%s:%d/api2/json/nodes/%s/%s/%s/vncwebsocket?port=%s&vncticket=%s",
|
||||||
pve.HostName, pve.Port, instance.Node, instance.Type, instance.VMID, ticket.Port, url.QueryEscape(ticket.Ticket))
|
pve.HostName, pve.Port, instance.Node, instance.Type, instance.VMID, ticket.Port, url.QueryEscape(ticket.Ticket))
|
||||||
|
|
||||||
@ -44,8 +56,6 @@ func (pve *PVEServer) NewTerminalSession(c *websocket.Conn, access *PVEAccessTic
|
|||||||
// Send first ticket line
|
// Send first ticket line
|
||||||
ws.WriteMessage(fastWs.TextMessage, []byte(fmt.Sprintf("%s:%s\n", access.Username, access.Ticket)))
|
ws.WriteMessage(fastWs.TextMessage, []byte(fmt.Sprintf("%s:%s\n", access.Username, access.Ticket)))
|
||||||
|
|
||||||
// https://github.com/proxmox/pve-xtermjs/blob/master/README
|
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
t, msg, err := c.ReadMessage()
|
t, msg, err := c.ReadMessage()
|
||||||
@ -65,9 +75,8 @@ func (pve *PVEServer) NewTerminalSession(c *websocket.Conn, access *PVEAccessTic
|
|||||||
}
|
}
|
||||||
|
|
||||||
msg = []byte(fmt.Sprintf("0:%d:%s\n", len(msg), string(msg)))
|
msg = []byte(fmt.Sprintf("0:%d:%s\n", len(msg), string(msg)))
|
||||||
err = ws.WriteMessage(t, msg)
|
|
||||||
|
|
||||||
if err != nil {
|
if err = ws.WriteMessage(t, msg); err != nil {
|
||||||
log.Println("Error writing to Proxmox:", err)
|
log.Println("Error writing to Proxmox:", err)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -85,8 +94,70 @@ func (pve *PVEServer) NewTerminalSession(c *websocket.Conn, access *PVEAccessTic
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
err = c.WriteMessage(t, msg)
|
if err = c.WriteMessage(t, msg); err != nil {
|
||||||
if err != nil {
|
log.Println("Error writing to client:", err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pve *PVEServer) NewVNCSession(c *websocket.Conn, instance *PVEInstance) error {
|
||||||
|
access, err := pve.GetAccessTicket()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ticket, err := pve.GetVNCTicket(access, instance, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
url := fmt.Sprintf("wss://%s:%d/api2/json/nodes/%s/%s/%s/vncwebsocket?port=%s&vncticket=%s",
|
||||||
|
pve.HostName, pve.Port, instance.Node, instance.Type, instance.VMID, ticket.Port, url.QueryEscape(ticket.Ticket))
|
||||||
|
|
||||||
|
headers := http.Header{}
|
||||||
|
headers.Add("Authorization", "PVEAPIToken="+access.Username)
|
||||||
|
headers.Add("Cookie", "PVEAuthCookie="+access.Ticket)
|
||||||
|
|
||||||
|
dialer := fastWs.Dialer{
|
||||||
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||||
|
}
|
||||||
|
|
||||||
|
ws, _, err := dialer.Dial(url, headers)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Error connecting to Proxmox WebSocket:", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer ws.Close()
|
||||||
|
|
||||||
|
// Send vnc password
|
||||||
|
c.WriteMessage(fastWs.TextMessage, []byte(fmt.Sprintf("\x01%s", ticket.Ticket)))
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
t, msg, err := c.ReadMessage()
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Error reading from client:", err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = ws.WriteMessage(t, msg); err != nil {
|
||||||
|
log.Println("Error writing to Proxmox:", err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
for {
|
||||||
|
t, msg, err := ws.ReadMessage()
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Error reading from Proxmox:", err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = c.WriteMessage(t, msg); err != nil {
|
||||||
log.Println("Error writing to client:", err)
|
log.Println("Error writing to client:", err)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -31,42 +31,49 @@ func main() {
|
|||||||
return fiber.ErrUpgradeRequired
|
return fiber.ErrUpgradeRequired
|
||||||
})
|
})
|
||||||
|
|
||||||
// app.Get("/ws/ssh", websocket.New(func(c *websocket.Conn) {
|
|
||||||
// err := lib.NewSSHWebsocketSession(c, &lib.SSHConfig{
|
|
||||||
// HostName: "10.0.0.102",
|
|
||||||
// User: "root",
|
|
||||||
// Password: "ausya2",
|
|
||||||
// })
|
|
||||||
|
|
||||||
// if err != nil {
|
|
||||||
// msg := fmt.Sprintf("\r\n%s\r\n", err.Error())
|
|
||||||
// c.WriteMessage(websocket.TextMessage, []byte(msg))
|
|
||||||
// }
|
|
||||||
// }))
|
|
||||||
|
|
||||||
app.Get("/ws/ssh", websocket.New(func(c *websocket.Conn) {
|
app.Get("/ws/ssh", websocket.New(func(c *websocket.Conn) {
|
||||||
node := &lib.PVEInstance{
|
cfg := &lib.SSHConfig{
|
||||||
Type: "lxc",
|
HostName: "10.0.0.102",
|
||||||
Node: "pve",
|
User: "root",
|
||||||
VMID: "102",
|
Password: "ausya2",
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := lib.NewSSHWebsocketSession(c, cfg); err != nil {
|
||||||
|
c.WriteMessage(websocket.TextMessage, []byte(err.Error()))
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
app.Get("/ws/pve", websocket.New(func(c *websocket.Conn) {
|
||||||
|
client := c.Query("client")
|
||||||
|
serverId := c.Query("serverId")
|
||||||
|
|
||||||
|
var node *lib.PVEInstance
|
||||||
|
|
||||||
|
switch serverId {
|
||||||
|
case "2":
|
||||||
|
node = &lib.PVEInstance{
|
||||||
|
Type: "qemu",
|
||||||
|
Node: "pve",
|
||||||
|
VMID: "105",
|
||||||
|
}
|
||||||
|
case "3":
|
||||||
|
node = &lib.PVEInstance{
|
||||||
|
Type: "lxc",
|
||||||
|
Node: "pve",
|
||||||
|
VMID: "102",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
if client == "vnc" {
|
||||||
|
err = pve.NewVNCSession(c, node)
|
||||||
|
} else {
|
||||||
|
err = pve.NewTerminalSession(c, node)
|
||||||
}
|
}
|
||||||
|
|
||||||
access, err := pve.GetAccessTicket()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.WriteMessage(websocket.TextMessage, []byte(err.Error()))
|
c.WriteMessage(websocket.TextMessage, []byte(err.Error()))
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ticket, err := pve.GetVNCTicket(access, node, false)
|
|
||||||
if err != nil {
|
|
||||||
c.WriteMessage(websocket.TextMessage, []byte(err.Error()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := pve.NewTerminalSession(c, access, node, ticket); err != nil {
|
|
||||||
c.WriteMessage(websocket.TextMessage, []byte(err.Error()))
|
|
||||||
}
|
|
||||||
|
|
||||||
}))
|
}))
|
||||||
app.Listen(":3000")
|
app.Listen(":3000")
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user