Compare commits

..

1 Commits

Author SHA1 Message Date
dependabot[bot] 3ad8442836
Merge 118e03301f into 1ce63664c0 2025-10-29 16:11:44 +01:00
12 changed files with 62 additions and 102 deletions

25
main.go
View File

@ -14,7 +14,6 @@ import (
var appCtx context.Context var appCtx context.Context
func Main() { func Main() {
logger.Log().Msg("JetKVM Starting Up")
LoadConfig() LoadConfig()
var cancel context.CancelFunc var cancel context.CancelFunc
@ -80,16 +79,16 @@ func Main() {
startVideoSleepModeTicker() startVideoSleepModeTicker()
go func() { go func() {
// wait for 15 minutes before starting auto-update checks
// this is to avoid interfering with initial setup processes
// and to ensure the system is stable before checking for updates
time.Sleep(15 * time.Minute) time.Sleep(15 * time.Minute)
for { for {
logger.Info().Bool("auto_update_enabled", config.AutoUpdateEnabled).Msg("auto-update check") logger.Debug().Bool("auto_update_enabled", config.AutoUpdateEnabled).Msg("UPDATING")
if !config.AutoUpdateEnabled { if !config.AutoUpdateEnabled {
logger.Debug().Msg("auto-update disabled") return
time.Sleep(5 * time.Minute) // we'll check if auto-updates are enabled in five minutes }
if isTimeSyncNeeded() || !timeSync.IsSyncSuccess() {
logger.Debug().Msg("system time is not synced, will retry in 30 seconds")
time.Sleep(30 * time.Second)
continue continue
} }
@ -99,12 +98,6 @@ func Main() {
continue continue
} }
if isTimeSyncNeeded() || !timeSync.IsSyncSuccess() {
logger.Debug().Msg("system time is not synced, will retry in 30 seconds")
time.Sleep(30 * time.Second)
continue
}
includePreRelease := config.IncludePreRelease includePreRelease := config.IncludePreRelease
err = TryUpdate(context.Background(), GetDeviceID(), includePreRelease) err = TryUpdate(context.Background(), GetDeviceID(), includePreRelease)
if err != nil { if err != nil {
@ -114,7 +107,6 @@ func Main() {
time.Sleep(1 * time.Hour) time.Sleep(1 * time.Hour)
} }
}() }()
//go RunFuseServer() //go RunFuseServer()
go RunWebServer() go RunWebServer()
@ -131,8 +123,7 @@ func Main() {
sigs := make(chan os.Signal, 1) sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
<-sigs <-sigs
logger.Info().Msg("JetKVM Shutting Down")
logger.Log().Msg("JetKVM Shutting Down")
//if fuseServer != nil { //if fuseServer != nil {
// err := setMassStorageImage(" ") // err := setMassStorageImage(" ")
// if err != nil { // if err != nil {

40
ota.go
View File

@ -176,7 +176,7 @@ func downloadFile(ctx context.Context, path string, url string, downloadProgress
if nr > 0 { if nr > 0 {
nw, ew := file.Write(buf[0:nr]) nw, ew := file.Write(buf[0:nr])
if nw < nr { if nw < nr {
return fmt.Errorf("short file write: %d < %d", nw, nr) return fmt.Errorf("short write: %d < %d", nw, nr)
} }
written += int64(nw) written += int64(nw)
if ew != nil { if ew != nil {
@ -240,7 +240,7 @@ func verifyFile(path string, expectedHash string, verifyProgress *float32, scope
if nr > 0 { if nr > 0 {
nw, ew := hash.Write(buf[0:nr]) nw, ew := hash.Write(buf[0:nr])
if nw < nr { if nw < nr {
return fmt.Errorf("short hash write: %d < %d", nw, nr) return fmt.Errorf("short write: %d < %d", nw, nr)
} }
verified += int64(nw) verified += int64(nw)
if ew != nil { if ew != nil {
@ -260,16 +260,11 @@ func verifyFile(path string, expectedHash string, verifyProgress *float32, scope
} }
} }
// close the file so we can rename below hashSum := hash.Sum(nil)
if err := fileToHash.Close(); err != nil { scopedLogger.Info().Str("path", path).Str("hash", hex.EncodeToString(hashSum)).Msg("SHA256 hash of")
return fmt.Errorf("error closing file: %w", err)
}
hashSum := hex.EncodeToString(hash.Sum(nil)) if hex.EncodeToString(hashSum) != expectedHash {
scopedLogger.Info().Str("path", path).Str("hash", hashSum).Msg("SHA256 hash of") return fmt.Errorf("hash mismatch: %x != %s", hashSum, expectedHash)
if hashSum != expectedHash {
return fmt.Errorf("hash mismatch: %s != %s", hashSum, expectedHash)
} }
if err := os.Rename(unverifiedPath, path); err != nil { if err := os.Rename(unverifiedPath, path); err != nil {
@ -318,7 +313,7 @@ func triggerOTAStateUpdate() {
func TryUpdate(ctx context.Context, deviceId string, includePreRelease bool) error { func TryUpdate(ctx context.Context, deviceId string, includePreRelease bool) error {
scopedLogger := otaLogger.With(). scopedLogger := otaLogger.With().
Str("deviceId", deviceId). Str("deviceId", deviceId).
Bool("includePreRelease", includePreRelease). Str("includePreRelease", fmt.Sprintf("%v", includePreRelease)).
Logger() Logger()
scopedLogger.Info().Msg("Trying to update...") scopedLogger.Info().Msg("Trying to update...")
@ -367,9 +362,8 @@ func TryUpdate(ctx context.Context, deviceId string, includePreRelease bool) err
otaState.Error = fmt.Sprintf("Error downloading app update: %v", err) otaState.Error = fmt.Sprintf("Error downloading app update: %v", err)
scopedLogger.Error().Err(err).Msg("Error downloading app update") scopedLogger.Error().Err(err).Msg("Error downloading app update")
triggerOTAStateUpdate() triggerOTAStateUpdate()
return fmt.Errorf("error downloading app update: %w", err) return err
} }
downloadFinished := time.Now() downloadFinished := time.Now()
otaState.AppDownloadFinishedAt = &downloadFinished otaState.AppDownloadFinishedAt = &downloadFinished
otaState.AppDownloadProgress = 1 otaState.AppDownloadProgress = 1
@ -385,21 +379,17 @@ func TryUpdate(ctx context.Context, deviceId string, includePreRelease bool) err
otaState.Error = fmt.Sprintf("Error verifying app update hash: %v", err) otaState.Error = fmt.Sprintf("Error verifying app update hash: %v", err)
scopedLogger.Error().Err(err).Msg("Error verifying app update hash") scopedLogger.Error().Err(err).Msg("Error verifying app update hash")
triggerOTAStateUpdate() triggerOTAStateUpdate()
return fmt.Errorf("error verifying app update: %w", err) return err
} }
verifyFinished := time.Now() verifyFinished := time.Now()
otaState.AppVerifiedAt = &verifyFinished otaState.AppVerifiedAt = &verifyFinished
otaState.AppVerificationProgress = 1 otaState.AppVerificationProgress = 1
triggerOTAStateUpdate()
otaState.AppUpdatedAt = &verifyFinished otaState.AppUpdatedAt = &verifyFinished
otaState.AppUpdateProgress = 1 otaState.AppUpdateProgress = 1
triggerOTAStateUpdate() triggerOTAStateUpdate()
scopedLogger.Info().Msg("App update downloaded") scopedLogger.Info().Msg("App update downloaded")
rebootNeeded = true rebootNeeded = true
triggerOTAStateUpdate()
} else { } else {
scopedLogger.Info().Msg("App is up to date") scopedLogger.Info().Msg("App is up to date")
} }
@ -415,9 +405,8 @@ func TryUpdate(ctx context.Context, deviceId string, includePreRelease bool) err
otaState.Error = fmt.Sprintf("Error downloading system update: %v", err) otaState.Error = fmt.Sprintf("Error downloading system update: %v", err)
scopedLogger.Error().Err(err).Msg("Error downloading system update") scopedLogger.Error().Err(err).Msg("Error downloading system update")
triggerOTAStateUpdate() triggerOTAStateUpdate()
return fmt.Errorf("error downloading system update: %w", err) return err
} }
downloadFinished := time.Now() downloadFinished := time.Now()
otaState.SystemDownloadFinishedAt = &downloadFinished otaState.SystemDownloadFinishedAt = &downloadFinished
otaState.SystemDownloadProgress = 1 otaState.SystemDownloadProgress = 1
@ -433,9 +422,8 @@ func TryUpdate(ctx context.Context, deviceId string, includePreRelease bool) err
otaState.Error = fmt.Sprintf("Error verifying system update hash: %v", err) otaState.Error = fmt.Sprintf("Error verifying system update hash: %v", err)
scopedLogger.Error().Err(err).Msg("Error verifying system update hash") scopedLogger.Error().Err(err).Msg("Error verifying system update hash")
triggerOTAStateUpdate() triggerOTAStateUpdate()
return fmt.Errorf("error verifying system update: %w", err) return err
} }
scopedLogger.Info().Msg("System update downloaded") scopedLogger.Info().Msg("System update downloaded")
verifyFinished := time.Now() verifyFinished := time.Now()
otaState.SystemVerifiedAt = &verifyFinished otaState.SystemVerifiedAt = &verifyFinished
@ -451,10 +439,8 @@ func TryUpdate(ctx context.Context, deviceId string, includePreRelease bool) err
if err != nil { if err != nil {
otaState.Error = fmt.Sprintf("Error starting rk_ota command: %v", err) otaState.Error = fmt.Sprintf("Error starting rk_ota command: %v", err)
scopedLogger.Error().Err(err).Msg("Error starting rk_ota command") scopedLogger.Error().Err(err).Msg("Error starting rk_ota command")
triggerOTAStateUpdate()
return fmt.Errorf("error starting rk_ota command: %w", err) return fmt.Errorf("error starting rk_ota command: %w", err)
} }
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
@ -489,15 +475,13 @@ func TryUpdate(ctx context.Context, deviceId string, includePreRelease bool) err
Str("output", output). Str("output", output).
Int("exitCode", cmd.ProcessState.ExitCode()). Int("exitCode", cmd.ProcessState.ExitCode()).
Msg("Error executing rk_ota command") Msg("Error executing rk_ota command")
triggerOTAStateUpdate()
return fmt.Errorf("error executing rk_ota command: %w\nOutput: %s", err, output) return fmt.Errorf("error executing rk_ota command: %w\nOutput: %s", err, output)
} }
scopedLogger.Info().Str("output", output).Msg("rk_ota success") scopedLogger.Info().Str("output", output).Msg("rk_ota success")
otaState.SystemUpdateProgress = 1 otaState.SystemUpdateProgress = 1
otaState.SystemUpdatedAt = &verifyFinished otaState.SystemUpdatedAt = &verifyFinished
rebootNeeded = true
triggerOTAStateUpdate() triggerOTAStateUpdate()
rebootNeeded = true
} else { } else {
scopedLogger.Info().Msg("System is up to date") scopedLogger.Info().Msg("System is up to date")
} }

View File

@ -212,7 +212,7 @@ export const Button = React.forwardRef<HTMLButtonElement, ButtonPropsType>(
Button.displayName = "Button"; Button.displayName = "Button";
type LinkPropsType = Pick<LinkProps, "to"> & type LinkPropsType = Pick<LinkProps, "to"> &
React.ComponentProps<typeof ButtonContent> & { disabled?: boolean, reloadDocument?: boolean }; React.ComponentProps<typeof ButtonContent> & { disabled?: boolean };
export const LinkButton = ({ to, ...props }: LinkPropsType) => { export const LinkButton = ({ to, ...props }: LinkPropsType) => {
const classes = cx( const classes = cx(
"group outline-hidden", "group outline-hidden",
@ -230,7 +230,7 @@ export const LinkButton = ({ to, ...props }: LinkPropsType) => {
); );
} else { } else {
return ( return (
<Link to={to} reloadDocument={props.reloadDocument} className={classes}> <Link to={to} className={classes}>
<ButtonContent {...props} /> <ButtonContent {...props} />
</Link> </Link>
); );

View File

@ -573,18 +573,14 @@ export interface OtaState {
export interface UpdateState { export interface UpdateState {
isUpdatePending: boolean; isUpdatePending: boolean;
setIsUpdatePending: (isPending: boolean) => void; setIsUpdatePending: (isPending: boolean) => void;
updateDialogHasBeenMinimized: boolean; updateDialogHasBeenMinimized: boolean;
setUpdateDialogHasBeenMinimized: (hasBeenMinimized: boolean) => void;
otaState: OtaState; otaState: OtaState;
setOtaState: (state: OtaState) => void; setOtaState: (state: OtaState) => void;
setUpdateDialogHasBeenMinimized: (hasBeenMinimized: boolean) => void;
modalView: UpdateModalViews modalView: UpdateModalViews
setModalView: (view: UpdateModalViews) => void; setModalView: (view: UpdateModalViews) => void;
updateErrorMessage: string | null;
setUpdateErrorMessage: (errorMessage: string) => void; setUpdateErrorMessage: (errorMessage: string) => void;
updateErrorMessage: string | null;
} }
export const useUpdateStore = create<UpdateState>(set => ({ export const useUpdateStore = create<UpdateState>(set => ({
@ -615,10 +611,8 @@ export const useUpdateStore = create<UpdateState>(set => ({
updateDialogHasBeenMinimized: false, updateDialogHasBeenMinimized: false,
setUpdateDialogHasBeenMinimized: (hasBeenMinimized: boolean) => setUpdateDialogHasBeenMinimized: (hasBeenMinimized: boolean) =>
set({ updateDialogHasBeenMinimized: hasBeenMinimized }), set({ updateDialogHasBeenMinimized: hasBeenMinimized }),
modalView: "loading", modalView: "loading",
setModalView: (view: UpdateModalViews) => set({ modalView: view }), setModalView: (view: UpdateModalViews) => set({ modalView: view }),
updateErrorMessage: null, updateErrorMessage: null,
setUpdateErrorMessage: (errorMessage: string) => set({ updateErrorMessage: errorMessage }), setUpdateErrorMessage: (errorMessage: string) => set({ updateErrorMessage: errorMessage }),
})); }));

View File

@ -73,10 +73,10 @@ export async function checkDeviceAuth() {
.GET(`${DEVICE_API}/device/status`) .GET(`${DEVICE_API}/device/status`)
.then(res => res.json() as Promise<DeviceStatus>); .then(res => res.json() as Promise<DeviceStatus>);
if (!res.isSetup) throw redirect("/welcome"); if (!res.isSetup) return redirect("/welcome");
const deviceRes = await api.GET(`${DEVICE_API}/device`); const deviceRes = await api.GET(`${DEVICE_API}/device`);
if (deviceRes.status === 401) throw redirect("/login-local"); if (deviceRes.status === 401) return redirect("/login-local");
if (deviceRes.ok) { if (deviceRes.ok) {
const device = (await deviceRes.json()) as LocalDevice; const device = (await deviceRes.json()) as LocalDevice;
return { authMode: device.authMode }; return { authMode: device.authMode };
@ -86,7 +86,7 @@ export async function checkDeviceAuth() {
} }
export async function checkAuth() { export async function checkAuth() {
return isOnDevice ? checkDeviceAuth() : checkCloudAuth(); return import.meta.env.MODE === "device" ? checkDeviceAuth() : checkCloudAuth();
} }
let router; let router;

View File

@ -58,7 +58,7 @@ const loader: LoaderFunction = async ({ params }: LoaderFunctionArgs) => {
return { device, user }; return { device, user };
} catch (e) { } catch (e) {
console.error(e); console.error(e);
return { user }; return { devices: [] };
} }
}; };

View File

@ -54,7 +54,7 @@ const loader: LoaderFunction = async ({ params }: LoaderFunctionArgs) => {
return { device, user }; return { device, user };
} catch (e) { } catch (e) {
console.error(e); console.error(e);
return { user }; return { devices: [] };
} }
}; };

View File

@ -98,7 +98,7 @@ export default function SettingsAccessIndexRoute() {
} }
getCloudState(); getCloudState();
// In cloud mode, we need to navigate to the device overview page, as we don't have a connection anymore // In cloud mode, we need to navigate to the device overview page, as we don't a connection anymore
if (!isOnDevice) navigate("/"); if (!isOnDevice) navigate("/");
return; return;
}); });

View File

@ -8,18 +8,12 @@ import { m } from "@localizations/messages.js";
export default function SettingsGeneralRebootRoute() { export default function SettingsGeneralRebootRoute() {
const navigate = useNavigate(); const navigate = useNavigate();
const { send } = useJsonRpc(); const { send } = useJsonRpc();
const onClose = useCallback(() => {
navigate(".."); // back to the devices.$id.settings page
window.location.reload(); // force a full reload to ensure the current device/cloud UI version is loaded
}, [navigate]);
const onConfirmUpdate = useCallback(() => { const onConfirmUpdate = useCallback(() => {
send("reboot", { force: true}); send("reboot", { force: true});
}, [send]); }, [send]);
return <Dialog onClose={onClose} onConfirmUpdate={onConfirmUpdate} />; return <Dialog onClose={() => navigate("..")} onConfirmUpdate={onConfirmUpdate} />;
} }
export function Dialog({ export function Dialog({

View File

@ -21,11 +21,6 @@ export default function SettingsGeneralUpdateRoute() {
const { setModalView, otaState } = useUpdateStore(); const { setModalView, otaState } = useUpdateStore();
const { send } = useJsonRpc(); const { send } = useJsonRpc();
const onClose = useCallback(() => {
navigate(".."); // back to the devices.$id.settings page
window.location.reload(); // force a full reload to ensure the current device/cloud UI version is loaded
}, [navigate]);
const onConfirmUpdate = useCallback(() => { const onConfirmUpdate = useCallback(() => {
send("tryUpdate", {}); send("tryUpdate", {});
setModalView("updating"); setModalView("updating");
@ -41,9 +36,9 @@ export default function SettingsGeneralUpdateRoute() {
} else { } else {
setModalView("loading"); setModalView("loading");
} }
}, [otaState.error, otaState.updating, setModalView, updateSuccess]); }, [otaState.updating, otaState.error, setModalView, updateSuccess]);
return <Dialog onClose={onClose} onConfirmUpdate={onConfirmUpdate} />; return <Dialog onClose={() => navigate("..")} onConfirmUpdate={onConfirmUpdate} />;
} }
export function Dialog({ export function Dialog({

View File

@ -1,6 +1,7 @@
import { lazy, useCallback, useEffect, useMemo, useRef, useState } from "react"; import { lazy, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { import {
Outlet, Outlet,
redirect,
useLoaderData, useLoaderData,
useLocation, useLocation,
useNavigate, useNavigate,
@ -15,7 +16,7 @@ import { motion, AnimatePresence } from "framer-motion";
import useWebSocket from "react-use-websocket"; import useWebSocket from "react-use-websocket";
import { cx } from "@/cva.config"; import { cx } from "@/cva.config";
import { CLOUD_API } from "@/ui.config"; import { CLOUD_API, DEVICE_API } from "@/ui.config";
import api from "@/api"; import api from "@/api";
import { checkAuth, isInCloud, isOnDevice } from "@/main"; import { checkAuth, isInCloud, isOnDevice } from "@/main";
import { import {
@ -50,14 +51,9 @@ import {
RebootingOverlay, RebootingOverlay,
} from "@components/VideoOverlay"; } from "@components/VideoOverlay";
import { FeatureFlagProvider } from "@providers/FeatureFlagProvider"; import { FeatureFlagProvider } from "@providers/FeatureFlagProvider";
import { DeviceStatus } from "@routes/welcome-local";
import { m } from "@localizations/messages.js"; import { m } from "@localizations/messages.js";
export type AuthMode = "password" | "noPassword" | null;
interface LocalLoaderResp {
authMode: AuthMode;
}
interface CloudLoaderResp { interface CloudLoaderResp {
deviceName: string; deviceName: string;
user: User | null; user: User | null;
@ -66,20 +62,35 @@ interface CloudLoaderResp {
} | null; } | null;
} }
export type AuthMode = "password" | "noPassword" | null;
export interface LocalDevice { export interface LocalDevice {
authMode: AuthMode; authMode: AuthMode;
deviceId: string; deviceId: string;
} }
const deviceLoader = async () => { const deviceLoader = async () => {
const device = await checkAuth(); const res = await api
return { authMode: device.authMode } as LocalLoaderResp; .GET(`${DEVICE_API}/device/status`)
.then(res => res.json() as Promise<DeviceStatus>);
if (!res.isSetup) return redirect("/welcome");
const deviceRes = await api.GET(`${DEVICE_API}/device`);
if (deviceRes.status === 401) return redirect("/login-local");
if (deviceRes.ok) {
const device = (await deviceRes.json()) as LocalDevice;
return { authMode: device.authMode };
}
throw new Error("Error fetching device");
}; };
const cloudLoader = async (params: Params<string>): Promise<CloudLoaderResp> => { const cloudLoader = async (params: Params<string>): Promise<CloudLoaderResp> => {
const user = await checkAuth(); const user = await checkAuth();
const iceResp = await api.POST(`${CLOUD_API}/webrtc/ice_config`); const iceResp = await api.POST(`${CLOUD_API}/webrtc/ice_config`);
const iceConfig = await iceResp.json(); const iceConfig = await iceResp.json();
const deviceResp = await api.GET(`${CLOUD_API}/devices/${params.id}`); const deviceResp = await api.GET(`${CLOUD_API}/devices/${params.id}`);
if (!deviceResp.ok) { if (!deviceResp.ok) {
@ -94,11 +105,11 @@ const cloudLoader = async (params: Params<string>): Promise<CloudLoaderResp> =>
device: { id: string; name: string; user: { googleId: string } }; device: { id: string; name: string; user: { googleId: string } };
}; };
return { user, iceConfig, deviceName: device.name || device.id } as CloudLoaderResp; return { user, iceConfig, deviceName: device.name || device.id };
}; };
const loader: LoaderFunction = ({ params }: LoaderFunctionArgs) => { const loader: LoaderFunction = ({ params }: LoaderFunctionArgs) => {
return isOnDevice ? deviceLoader() : cloudLoader(params); return import.meta.env.MODE === "device" ? deviceLoader() : cloudLoader(params);
}; };
export default function KvmIdRoute() { export default function KvmIdRoute() {
@ -174,7 +185,7 @@ export default function KvmIdRoute() {
try { try {
await pc.setRemoteDescription(new RTCSessionDescription(remoteDescription)); await pc.setRemoteDescription(new RTCSessionDescription(remoteDescription));
console.log("[setRemoteSessionDescription] Remote description set successfully to: " + remoteDescription.sdp); console.log("[setRemoteSessionDescription] Remote description set successfully");
setLoadingMessage(m.establishing_secure_connection()); setLoadingMessage(m.establishing_secure_connection());
} catch (error) { } catch (error) {
console.error( console.error(
@ -219,13 +230,8 @@ export default function KvmIdRoute() {
const ignoreOffer = useRef(false); const ignoreOffer = useRef(false);
const isSettingRemoteAnswerPending = useRef(false); const isSettingRemoteAnswerPending = useRef(false);
const makingOffer = useRef(false); const makingOffer = useRef(false);
const reconnectAttemptsRef = useRef(2000);
const wsProtocol = window.location.protocol === "https:" ? "wss:" : "ws:";
const reconnectInterval = (attempt: number) => { const wsProtocol = window.location.protocol === "https:" ? "wss:" : "ws:";
// Exponential backoff with a max of 10 seconds between attempts
return Math.min(500 * 2 ** attempt, 10000);
}
const { sendMessage, getWebSocket } = useWebSocket( const { sendMessage, getWebSocket } = useWebSocket(
isOnDevice isOnDevice
@ -234,10 +240,10 @@ export default function KvmIdRoute() {
{ {
heartbeat: true, heartbeat: true,
retryOnError: true, retryOnError: true,
reconnectAttempts: reconnectAttemptsRef.current, reconnectAttempts: 2000,
reconnectInterval: reconnectInterval, reconnectInterval: 1000,
onReconnectStop: (numAttempts: number) => { onReconnectStop: (numAttempts: number) => {
console.debug("Reconnect stopped after ", numAttempts, "attempts"); console.debug("Reconnect stopped", numAttempts);
cleanupAndStopReconnecting(); cleanupAndStopReconnecting();
}, },
@ -255,7 +261,6 @@ export default function KvmIdRoute() {
console.error("[Websocket] onError", event); console.error("[Websocket] onError", event);
// We don't want to close everything down, we wait for the reconnect to stop instead // We don't want to close everything down, we wait for the reconnect to stop instead
}, },
onOpen() { onOpen() {
console.debug("[Websocket] onOpen"); console.debug("[Websocket] onOpen");
// We want to clear the reboot state when the websocket connection is opened // We want to clear the reboot state when the websocket connection is opened
@ -288,7 +293,6 @@ export default function KvmIdRoute() {
*/ */
const parsedMessage = JSON.parse(message.data); const parsedMessage = JSON.parse(message.data);
if (parsedMessage.type === "device-metadata") { if (parsedMessage.type === "device-metadata") {
const { deviceVersion } = parsedMessage.data; const { deviceVersion } = parsedMessage.data;
console.debug("[Websocket] Received device-metadata message"); console.debug("[Websocket] Received device-metadata message");
@ -305,12 +309,10 @@ export default function KvmIdRoute() {
console.log("[Websocket] Device is using new signaling"); console.log("[Websocket] Device is using new signaling");
isLegacySignalingEnabled.current = false; isLegacySignalingEnabled.current = false;
} }
setupPeerConnection(); setupPeerConnection();
} }
if (!peerConnection) return; if (!peerConnection) return;
if (parsedMessage.type === "answer") { if (parsedMessage.type === "answer") {
console.debug("[Websocket] Received answer"); console.debug("[Websocket] Received answer");
const readyForOffer = const readyForOffer =

View File

@ -16,7 +16,7 @@ interface LoaderData {
devices: { id: string; name: string; online: boolean; lastSeen: string }[]; devices: { id: string; name: string; online: boolean; lastSeen: string }[];
user: User; user: User;
} }
const loader: LoaderFunction = async () => { const loader: LoaderFunction = async ()=> {
const user = await checkAuth(); const user = await checkAuth();
try { try {
@ -30,7 +30,7 @@ const loader: LoaderFunction = async () => {
return { devices, user }; return { devices, user };
} catch (e) { } catch (e) {
console.error(e); console.error(e);
return { devices: [], user }; return { devices: [] };
} }
}; };