mirror of
https://github.com/khairul169/home-lab.git
synced 2026-10-02 17:13:57 +07:00
feat: add vnc app
This commit is contained in:
+5
-1
@@ -39,7 +39,11 @@ const RootLayout = () => {
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<StatusBar style="auto" />
|
||||
<View style={cn("flex-1 bg-[#f2f7fb]", { paddingTop: insets.top })}>
|
||||
<View
|
||||
style={cn("flex-1 bg-[#f2f7fb] overflow-hidden", {
|
||||
paddingTop: insets.top,
|
||||
})}
|
||||
>
|
||||
<Stack
|
||||
screenOptions={{ contentStyle: { backgroundColor: "#f2f7fb" } }}
|
||||
/>
|
||||
|
||||
@@ -4,7 +4,7 @@ import api from "@/lib/api";
|
||||
import { useAuth } from "@/stores/authStore";
|
||||
import BackButton from "@ui/BackButton";
|
||||
import Input from "@ui/Input";
|
||||
import { Stack, router, useLocalSearchParams } from "expo-router";
|
||||
import { Stack, useLocalSearchParams } from "expo-router";
|
||||
import React, { useState } from "react";
|
||||
import { useMutation, useQuery } from "react-query";
|
||||
import FileDrop from "@/components/pages/files/FileDrop";
|
||||
@@ -13,7 +13,6 @@ import { HStack } from "@ui/Stack";
|
||||
import Button from "@ui/Button";
|
||||
import { Ionicons } from "@ui/Icons";
|
||||
import FileInlineViewer from "@/components/pages/files/FileInlineViewer";
|
||||
import { decodeUrl, encodeUrl } from "@/lib/utils";
|
||||
import { FilesContext } from "@/components/pages/files/FilesContext";
|
||||
import { FileItem } from "@/types/files";
|
||||
|
||||
@@ -23,7 +22,6 @@ const FilesPage = () => {
|
||||
path: "",
|
||||
});
|
||||
const [viewFile, setViewFile] = useState<FileItem | null>(null);
|
||||
const searchParams = useLocalSearchParams();
|
||||
const parentPath =
|
||||
params.path.length > 0
|
||||
? params.path.split("/").slice(0, -1).join("/")
|
||||
@@ -1,16 +0,0 @@
|
||||
import { API_BASEURL } from "@/lib/constants";
|
||||
import authStore from "@/stores/authStore";
|
||||
import { FileItem } from "@/types/files";
|
||||
|
||||
export function openFile(file: FileItem | string, dl = false) {
|
||||
const url = getFileUrl(file, dl);
|
||||
window.open(url, "_blank");
|
||||
}
|
||||
|
||||
export function getFileUrl(file: FileItem | string, dl = false) {
|
||||
const filepath = typeof file === "string" ? file : file.path;
|
||||
const url = new URL(API_BASEURL + "/files/download" + filepath);
|
||||
url.searchParams.set("token", authStore.getState().token);
|
||||
dl && url.searchParams.set("dl", "true");
|
||||
return url.toString();
|
||||
}
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
import api from "@/lib/api";
|
||||
import { showToast } from "@/stores/toastStore";
|
||||
import { API_BASEURL } from "@/lib/constants";
|
||||
import authStore from "@/stores/authStore";
|
||||
import { FileItem } from "@/types/files";
|
||||
|
||||
export const wakePcUp = async () => {
|
||||
try {
|
||||
@@ -11,3 +14,41 @@ export const wakePcUp = async () => {
|
||||
showToast("Cannot wake up the PC!", { type: "danger" });
|
||||
}
|
||||
};
|
||||
|
||||
export function openFile(file: FileItem | string, dl = false) {
|
||||
const url = getFileUrl(file, dl);
|
||||
window.open(url, "_blank");
|
||||
}
|
||||
|
||||
export function getFileUrl(file: FileItem | string, dl = false) {
|
||||
const filepath = typeof file === "string" ? file : file.path;
|
||||
const url = new URL(API_BASEURL + "/files/download" + filepath);
|
||||
url.searchParams.set("token", authStore.getState().token);
|
||||
dl && url.searchParams.set("dl", "true");
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
export function openFullscreen(elem: any) {
|
||||
if (elem.requestFullscreen) {
|
||||
elem.requestFullscreen();
|
||||
} else if (elem.webkitRequestFullscreen) {
|
||||
/* Safari */
|
||||
elem.webkitRequestFullscreen();
|
||||
} else if (elem.msRequestFullscreen) {
|
||||
/* IE11 */
|
||||
elem.msRequestFullscreen();
|
||||
}
|
||||
}
|
||||
|
||||
/* Close fullscreen */
|
||||
export function closeFullscreen() {
|
||||
if (document.exitFullscreen) {
|
||||
document.exitFullscreen();
|
||||
} else if ((document as any).webkitExitFullscreen) {
|
||||
/* Safari */
|
||||
(document as any).webkitExitFullscreen();
|
||||
} else if ((document as any).msExitFullscreen) {
|
||||
/* IE11 */
|
||||
(document as any).msExitFullscreen();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import Box from "@ui/Box";
|
||||
import Text from "@ui/Text";
|
||||
import React, { useEffect, useRef } from "react";
|
||||
import "xterm/css/xterm.css";
|
||||
import { BASEURL } from "@/lib/constants";
|
||||
import { BASEURL, WS_BASEURL } from "@/lib/constants";
|
||||
import { useAuth } from "@/stores/authStore";
|
||||
import { Stack } from "expo-router";
|
||||
import BackButton from "@ui/BackButton";
|
||||
@@ -28,11 +28,7 @@ const TerminalPage = () => {
|
||||
const fitAddon = new FitAddon();
|
||||
term.loadAddon(fitAddon);
|
||||
|
||||
const baseUrl = BASEURL.replace("https://", "wss://").replace(
|
||||
"http://",
|
||||
"ws://"
|
||||
);
|
||||
const socket = new WebSocket(baseUrl + "/terminal?token=" + token);
|
||||
const socket = new WebSocket(WS_BASEURL + "/terminal?token=" + token);
|
||||
const attachAddon = new AttachAddon(socket);
|
||||
|
||||
// Attach the socket to term
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import { WS_BASEURL } from "@/lib/constants";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useAuth } from "@/stores/authStore";
|
||||
import BackButton from "@ui/BackButton";
|
||||
import Box from "@ui/Box";
|
||||
import Button from "@ui/Button";
|
||||
import { Ionicons } from "@ui/Icons";
|
||||
import { Stack } from "expo-router";
|
||||
import React, { useEffect, useRef } from "react";
|
||||
import { Platform } from "react-native";
|
||||
import { VncScreen, VncScreenHandle } from "react-vnc";
|
||||
import { openFullscreen } from "./lib";
|
||||
|
||||
const VncPage = () => {
|
||||
const containerRef = useRef<HTMLDivElement>(null!);
|
||||
const vncRef = useRef<VncScreenHandle>(null!);
|
||||
const { token } = useAuth();
|
||||
|
||||
const onFullscreen = () => {
|
||||
openFullscreen(containerRef.current);
|
||||
};
|
||||
|
||||
if (Platform.OS !== "web") {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: "VNC",
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () => (
|
||||
<Button
|
||||
icon={<Ionicons name="expand-outline" />}
|
||||
variant="ghost"
|
||||
className="h-14 w-14"
|
||||
iconClassName="text-black text-2xl"
|
||||
onPress={onFullscreen}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
|
||||
{token ? (
|
||||
<div ref={containerRef} style={cn("w-full h-full")}>
|
||||
<VncScreen
|
||||
url={WS_BASEURL + "/vnc?token=" + token}
|
||||
resizeSession
|
||||
scaleViewport
|
||||
background="#000000"
|
||||
style={cn("w-full h-full")}
|
||||
ref={vncRef}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default VncPage;
|
||||
@@ -7,7 +7,7 @@ import Text from "@ui/Text";
|
||||
import React, { useRef } from "react";
|
||||
import Modal from "react-native-modal";
|
||||
import { Video, ResizeMode } from "expo-av";
|
||||
import { getFileUrl, openFile } from "@/app/apps/files/utils";
|
||||
import { getFileUrl, openFile } from "@/app/apps/lib";
|
||||
import { Image } from "react-native";
|
||||
import AudioPlayer from "@/components/containers/AudioPlayer";
|
||||
import { FileItem } from "@/types/files";
|
||||
|
||||
@@ -4,11 +4,10 @@ import { FileItem } from "@/types/files";
|
||||
import Text from "@ui/Text";
|
||||
import List from "@ui/List";
|
||||
import { Ionicons } from "@ui/Icons";
|
||||
import { cn } from "@/lib/utils";
|
||||
import ActionSheet from "@ui/ActionSheet";
|
||||
import { HStack } from "@ui/Stack";
|
||||
import Button from "@ui/Button";
|
||||
import { openFile } from "@/app/apps/files/utils";
|
||||
import { openFile } from "@/app/apps/lib";
|
||||
|
||||
type Store = {
|
||||
isVisible: boolean;
|
||||
|
||||
@@ -17,13 +17,18 @@ const Apps = (props: Props) => {
|
||||
{
|
||||
name: "Files",
|
||||
icon: <Ionicons name="folder" />,
|
||||
path: "files/index",
|
||||
path: "files",
|
||||
},
|
||||
{
|
||||
name: "Terminal",
|
||||
icon: <Ionicons name="terminal" />,
|
||||
path: "terminal",
|
||||
},
|
||||
{
|
||||
name: "VNC",
|
||||
icon: <Ionicons name="eye" />,
|
||||
path: "vnc",
|
||||
},
|
||||
{
|
||||
name: "Turn on PC",
|
||||
icon: <Ionicons name="desktop" />,
|
||||
|
||||
@@ -3,3 +3,7 @@ export const BASEURL = __DEV__
|
||||
: location.protocol + "//" + location.host;
|
||||
|
||||
export const API_BASEURL = BASEURL + "/api";
|
||||
export const WS_BASEURL = BASEURL.replace("https://", "wss://").replace(
|
||||
"http://",
|
||||
"ws://"
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user