From 2e4a49feb64e788841bbb2d03a4040329987858b Mon Sep 17 00:00:00 2001 From: Alex P Date: Thu, 23 Oct 2025 01:37:30 +0300 Subject: [PATCH] [WIP] Optimizations: code readiness optimizations --- session_manager.go | 26 +- ui/src/routes/devices.$id.tsx | 488 +++++++++++++++++++--------------- 2 files changed, 281 insertions(+), 233 deletions(-) diff --git a/session_manager.go b/session_manager.go index 2b3958c7..a4460729 100644 --- a/session_manager.go +++ b/session_manager.go @@ -167,13 +167,11 @@ func NewSessionManager(logger *zerolog.Logger) *SessionManager { } func (sm *SessionManager) AddSession(session *Session, clientSettings *SessionSettings) error { - // Basic input validation if session == nil { sm.logger.Error().Msg("AddSession: session is nil") return errors.New("session cannot be nil") } - // Validate nickname if provided (matching frontend validation) if session.Nickname != "" { if len(session.Nickname) < minNicknameLength { return fmt.Errorf("nickname must be at least %d characters", minNicknameLength) @@ -181,7 +179,6 @@ func (sm *SessionManager) AddSession(session *Session, clientSettings *SessionSe if len(session.Nickname) > maxNicknameLength { return fmt.Errorf("nickname must be %d characters or less", maxNicknameLength) } - // Note: Pattern validation is done in RPC layer, not here for performance } if len(session.Identity) > maxIdentityLength { return fmt.Errorf("identity too long (max %d characters)", maxIdentityLength) @@ -225,30 +222,25 @@ func (sm *SessionManager) AddSession(session *Session, clientSettings *SessionSe } } - // Check if a session with this ID already exists (reconnection) if existing, exists := sm.sessions[session.ID]; exists { if existing.Identity != session.Identity || existing.Source != session.Source { return fmt.Errorf("session ID already in use by different user (identity mismatch)") } - // Close old connection to prevent multiple active connections for same session ID if existing.peerConnection != nil { existing.peerConnection.Close() } - // Update the existing session with new connection details existing.peerConnection = session.peerConnection existing.VideoTrack = session.VideoTrack existing.ControlChannel = session.ControlChannel existing.RPCChannel = session.RPCChannel existing.HidChannel = session.HidChannel existing.flushCandidates = session.flushCandidates - // Preserve mode and nickname session.Mode = existing.Mode session.Nickname = existing.Nickname session.CreatedAt = existing.CreatedAt - // Ensure session has auto-generated nickname if needed sm.ensureNickname(session) if !nicknameReserved && session.Nickname != "" { @@ -257,17 +249,15 @@ func (sm *SessionManager) AddSession(session *Session, clientSettings *SessionSe sm.sessions[session.ID] = session - // If this was the primary, try to restore primary status if existing.Mode == SessionModePrimary { isBlacklisted := sm.isSessionBlacklisted(session.ID) - // SECURITY: Prevent dual-primary window - only restore if no other primary exists + // SECURITY: Prevent dual-primary - only restore if no other primary exists primaryExists := sm.primarySessionID != "" && sm.sessions[sm.primarySessionID] != nil if sm.lastPrimaryID == session.ID && !isBlacklisted && !primaryExists { sm.primarySessionID = session.ID sm.lastPrimaryID = "" delete(sm.reconnectGrace, session.ID) } else { - // Grace period expired, another session took over, or primary already exists session.Mode = SessionModeObserver } } @@ -280,22 +270,18 @@ func (sm *SessionManager) AddSession(session *Session, clientSettings *SessionSe return ErrMaxSessionsReached } - // Generate ID if not set if session.ID == "" { session.ID = uuid.New().String() } - // Set nickname from client settings if provided if clientSettings != nil && clientSettings.Nickname != "" { session.Nickname = clientSettings.Nickname } - // Use global settings for requirements (not client-provided) globalSettings := currentSessionSettings primaryExists := sm.primarySessionID != "" && sm.sessions[sm.primarySessionID] != nil - // Check if there's an active grace period for a primary session (different from this session) hasActivePrimaryGracePeriod := false if sm.lastPrimaryID != "" && sm.lastPrimaryID != session.ID { if graceTime, exists := sm.reconnectGrace[sm.lastPrimaryID]; exists { @@ -312,7 +298,6 @@ func (sm *SessionManager) AddSession(session *Session, clientSettings *SessionSe isBlacklisted := sm.isSessionBlacklisted(session.ID) isOnlySession := len(sm.sessions) == 0 - // Determine if this session should become primary canBecomePrimary := !primaryExists && !hasActivePrimaryGracePeriod isReconnectingPrimary := wasWithinGracePeriod && wasPreviouslyPrimary isNewEligibleSession := !wasWithinGracePeriod && (!isBlacklisted || isOnlySession) @@ -325,7 +310,7 @@ func (sm *SessionManager) AddSession(session *Session, clientSettings *SessionSe sm.primarySessionID = session.ID sm.lastPrimaryID = "" - // Clear all existing grace periods when a new primary is established + // Clear grace periods when new primary is established for oldSessionID := range sm.reconnectGrace { delete(sm.reconnectGrace, oldSessionID) } @@ -347,7 +332,6 @@ func (sm *SessionManager) AddSession(session *Session, clientSettings *SessionSe requiresNickname := globalSettings.RequireNickname hasNickname := session.Nickname != "" && len(session.Nickname) > 0 - // Only send approval request if nickname is not required OR already provided if !requiresNickname || hasNickname { go func() { writeJSONRPCEvent("newSessionPending", map[string]interface{}{ @@ -358,12 +342,8 @@ func (sm *SessionManager) AddSession(session *Session, clientSettings *SessionSe }, primary) }() } - // If nickname is required and missing, the approval request will be sent - // later when updateSessionNickname is called (see jsonrpc.go:232-242) } } else { - // No primary exists and approval is required, OR approval is not required - // In either case, this session becomes an observer session.Mode = SessionModeObserver } @@ -625,12 +605,10 @@ func (sm *SessionManager) SetPrimarySession(sessionID string) error { // Sessions in pending state cannot receive video // Sessions that require nickname but don't have one also cannot receive video (if enforced) func (sm *SessionManager) CanReceiveVideo(session *Session, settings *SessionSettings) bool { - // Check if session has video view permission if !session.HasPermission(PermissionVideoView) { return false } - // If nickname is required and session doesn't have one, block video if settings != nil && settings.RequireNickname && session.Nickname == "" { return false } diff --git a/ui/src/routes/devices.$id.tsx b/ui/src/routes/devices.$id.tsx index 238715da..62d70ec2 100644 --- a/ui/src/routes/devices.$id.tsx +++ b/ui/src/routes/devices.$id.tsx @@ -42,11 +42,18 @@ import NicknameModal from "@components/NicknameModal"; import AccessDeniedOverlay from "@components/AccessDeniedOverlay"; import PendingApprovalOverlay from "@components/PendingApprovalOverlay"; import DashboardNavbar from "@components/Header"; -const ConnectionStatsSidebar = lazy(() => import('@/components/sidebar/connectionStats')); -const Terminal = lazy(() => import('@components/Terminal')); -const UpdateInProgressStatusCard = lazy(() => import("@/components/UpdateInProgressStatusCard")); +const ConnectionStatsSidebar = lazy(() => import("@/components/sidebar/connectionStats")); +const Terminal = lazy(() => import("@components/Terminal")); +const UpdateInProgressStatusCard = lazy( + () => import("@/components/UpdateInProgressStatusCard"), +); import Modal from "@/components/Modal"; -import { JsonRpcRequest, JsonRpcResponse, RpcMethodNotFound, useJsonRpc } from "@/hooks/useJsonRpc"; +import { + JsonRpcRequest, + JsonRpcResponse, + RpcMethodNotFound, + useJsonRpc, +} from "@/hooks/useJsonRpc"; import { ConnectionFailedOverlay, LoadingConnectionOverlay, @@ -135,15 +142,25 @@ export default function KvmIdRoute() { const authMode = "authMode" in loaderResp ? loaderResp.authMode : null; const params = useParams() as { id: string }; - const { sidebarView, setSidebarView, disableVideoFocusTrap, setDisableVideoFocusTrap, rebootState, setRebootState } = useUiStore(); + const { + sidebarView, + setSidebarView, + disableVideoFocusTrap, + setDisableVideoFocusTrap, + rebootState, + setRebootState, + } = useUiStore(); const [queryParams, setQueryParams] = useSearchParams(); const { - peerConnection, setPeerConnection, - peerConnectionState, setPeerConnectionState, + peerConnection, + setPeerConnection, + peerConnectionState, + setPeerConnectionState, setMediaStream, setRpcDataChannel, - isTurnServerInUse, setTurnServerInUse, + isTurnServerInUse, + setTurnServerInUse, rpcDataChannel, setTransceiver, setRpcHidChannel, @@ -162,12 +179,14 @@ export default function KvmIdRoute() { const { currentSessionId, currentMode, setCurrentSession } = useSessionStore(); const { nickname, setNickname } = useSharedSessionStore(); const { setRequireSessionApproval, setRequireSessionNickname } = useSettingsStore(); - const [globalSessionSettings, setGlobalSessionSettings] = useState<{requireApproval: boolean, requireNickname: boolean} | null>(null); + const [globalSessionSettings, setGlobalSessionSettings] = useState<{ + requireApproval: boolean; + requireNickname: boolean; + } | null>(null); const [loadingMessage, setLoadingMessage] = useState("Connecting to device..."); const cleanupAndStopReconnecting = useCallback( function cleanupAndStopReconnecting() { - setConnectionFailed(true); if (peerConnection) { setPeerConnectionState(peerConnection.connectionState); @@ -264,7 +283,7 @@ export default function KvmIdRoute() { }, onClose(_event) { - // We don't want to close everything down, we wait for the reconnect to stop instead + // Handled by onReconnectStop instead }, onError(event) { @@ -309,7 +328,7 @@ export default function KvmIdRoute() { if (sessionSettings) { setGlobalSessionSettings({ requireNickname: sessionSettings.requireNickname || false, - requireApproval: sessionSettings.requireApproval || false + requireApproval: sessionSettings.requireApproval || false, }); // Also update the settings store for approval handling setRequireSessionApproval(sessionSettings.requireApproval || false); @@ -318,7 +337,6 @@ export default function KvmIdRoute() { // If the device version is not set, we can assume the device is using the legacy signaling if (!deviceVersion) { - // Now we don't need the websocket connection anymore, as we've established that we need to use the legacy signaling // which does everything over HTTP(at least from the perspective of the client) isLegacySignalingEnabled.current = true; @@ -342,7 +360,10 @@ export default function KvmIdRoute() { } if (!peerConnection) { - console.warn("[Websocket] Ignoring message because peerConnection is not ready:", parsedMessage.type); + console.warn( + "[Websocket] Ignoring message because peerConnection is not ready:", + parsedMessage.type, + ); return; } if (parsedMessage.type === "answer") { @@ -366,15 +387,18 @@ export default function KvmIdRoute() { if (parsedMessage.sessionId && parsedMessage.mode) { handleSessionResponse({ sessionId: parsedMessage.sessionId, - mode: parsedMessage.mode + mode: parsedMessage.mode, }); // Store sessionId via zustand (persists to sessionStorage for per-tab isolation) setCurrentSession(parsedMessage.sessionId, parsedMessage.mode); - if (parsedMessage.requireNickname !== undefined && parsedMessage.requireApproval !== undefined) { + if ( + parsedMessage.requireNickname !== undefined && + parsedMessage.requireApproval !== undefined + ) { setGlobalSessionSettings({ requireNickname: parsedMessage.requireNickname, - requireApproval: parsedMessage.requireApproval + requireApproval: parsedMessage.requireApproval, }); // Also update the settings store for approval handling setRequireSessionApproval(parsedMessage.requireApproval); @@ -385,8 +409,10 @@ export default function KvmIdRoute() { // 1. Nickname is required by backend settings // 2. We don't already have a nickname // This happens even for pending sessions so the nickname is included in approval - const hasNickname = parsedMessage.nickname && parsedMessage.nickname.length > 0; - const requiresNickname = parsedMessage.requireNickname || globalSessionSettings?.requireNickname; + const hasNickname = + parsedMessage.nickname && parsedMessage.nickname.length > 0; + const requiresNickname = + parsedMessage.requireNickname || globalSessionSettings?.requireNickname; if (requiresNickname && !hasNickname) { setShowNicknameModal(true); @@ -427,18 +453,22 @@ export default function KvmIdRoute() { peerConnection?.iceConnectionState === "connected"; if (!isConnectionHealthy) { - console.log(`[Websocket] Mode changed to ${newMode}, connection unhealthy, reconnecting...`); + console.log( + `[Websocket] Mode changed to ${newMode}, connection unhealthy, reconnecting...`, + ); setTimeout(() => { peerConnection?.close(); setupPeerConnection(); }, 500); } else { - console.log(`[Websocket] Mode changed to ${newMode}, connection healthy, skipping reconnect`); + console.log( + `[Websocket] Mode changed to ${newMode}, connection healthy, skipping reconnect`, + ); } } } }, - } + }, ); const sendWebRTCSignal = useCallback( @@ -540,8 +570,8 @@ export default function KvmIdRoute() { sessionId: storeSessionId || undefined, userAgent: navigator.userAgent, sessionSettings: { - nickname: storeNickname || undefined - } + nickname: storeNickname || undefined, + }, }); } } catch (e) { @@ -605,10 +635,13 @@ export default function KvmIdRoute() { setRpcHidUnreliableChannel(rpcHidUnreliableChannel); }; - const rpcHidUnreliableNonOrderedChannel = pc.createDataChannel("hidrpc-unreliable-nonordered", { - ordered: false, - maxRetransmits: 0, - }); + const rpcHidUnreliableNonOrderedChannel = pc.createDataChannel( + "hidrpc-unreliable-nonordered", + { + ordered: false, + maxRetransmits: 0, + }, + ); rpcHidUnreliableNonOrderedChannel.binaryType = "arraybuffer"; rpcHidUnreliableNonOrderedChannel.onopen = () => { setRpcHidUnreliableNonOrderedChannel(rpcHidUnreliableNonOrderedChannel); @@ -699,19 +732,24 @@ export default function KvmIdRoute() { } // Fire and forget - api.POST(`${CLOUD_API}/webrtc/turn_activity`, { - bytesReceived: bytesReceivedDelta, - bytesSent: bytesSentDelta, - }).catch(() => { - // we don't care about errors here, but we don't want unhandled promise rejections - }); + api + .POST(`${CLOUD_API}/webrtc/turn_activity`, { + bytesReceived: bytesReceivedDelta, + bytesSent: bytesSentDelta, + }) + .catch(() => { + // we don't care about errors here, but we don't want unhandled promise rejections + }); }, 10000); const { setNetworkState } = useNetworkStateStore(); const { setHdmiState } = useVideoStore(); const { - keyboardLedState, setKeyboardLedState, - keysDownState, setKeysDownState, setUsbState, + keyboardLedState, + setKeyboardLedState, + keysDownState, + setKeysDownState, + setUsbState, } = useHidStore(); const setHidRpcDisabled = useRTCStore(state => state.setHidRpcDisabled); @@ -720,15 +758,17 @@ export default function KvmIdRoute() { function onJsonRpcRequest(resp: JsonRpcRequest) { // Handle session-related events - if (resp.method === "sessionsUpdated" || - resp.method === "modeChanged" || - resp.method === "connectionModeChanged" || - resp.method === "otherSessionConnected" || - resp.method === "primaryControlRequested" || - resp.method === "primaryControlApproved" || - resp.method === "primaryControlDenied" || - resp.method === "newSessionPending" || - resp.method === "sessionAccessDenied") { + if ( + resp.method === "sessionsUpdated" || + resp.method === "modeChanged" || + resp.method === "connectionModeChanged" || + resp.method === "otherSessionConnected" || + resp.method === "primaryControlRequested" || + resp.method === "primaryControlApproved" || + resp.method === "primaryControlDenied" || + resp.method === "newSessionPending" || + resp.method === "sessionAccessDenied" + ) { handleRpcEvent(resp.method, resp.params); // Show access denied overlay if our session was denied @@ -809,7 +849,7 @@ export default function KvmIdRoute() { newSessionRequest, handleApproveNewSession, handleDenyNewSession, - closeNewSessionRequest + closeNewSessionRequest, } = useSessionManagement(send); const { hasPermission, isLoading: isLoadingPermissions } = usePermissions(); @@ -823,7 +863,13 @@ export default function KvmIdRoute() { const hdmiState = resp.result as Parameters[0]; setHdmiState(hdmiState); }); - }, [rpcDataChannel?.readyState, hasPermission, isLoadingPermissions, send, setHdmiState]); + }, [ + rpcDataChannel?.readyState, + hasPermission, + isLoadingPermissions, + send, + setHdmiState, + ]); const [needLedState, setNeedLedState] = useState(true); @@ -842,7 +888,15 @@ export default function KvmIdRoute() { } setNeedLedState(false); }); - }, [rpcDataChannel?.readyState, send, setKeyboardLedState, keyboardLedState, needLedState, hasPermission, isLoadingPermissions]); + }, [ + rpcDataChannel?.readyState, + send, + setKeyboardLedState, + keyboardLedState, + needLedState, + hasPermission, + isLoadingPermissions, + ]); const [needKeyDownState, setNeedKeyDownState] = useState(true); @@ -854,7 +908,10 @@ export default function KvmIdRoute() { send("getKeyDownState", {}, (resp: JsonRpcResponse) => { if ("error" in resp) { if (resp.error.code === RpcMethodNotFound) { - console.warn("Failed to get key down state, switching to old-school", resp.error); + console.warn( + "Failed to get key down state, switching to old-school", + resp.error, + ); setHidRpcDisabled(true); } else { console.error("Failed to get key down state", resp.error); @@ -865,7 +922,16 @@ export default function KvmIdRoute() { } setNeedKeyDownState(false); }); - }, [keysDownState, needKeyDownState, rpcDataChannel?.readyState, send, setKeysDownState, setHidRpcDisabled, hasPermission, isLoadingPermissions]); + }, [ + keysDownState, + needKeyDownState, + rpcDataChannel?.readyState, + send, + setKeysDownState, + setHidRpcDisabled, + hasPermission, + isLoadingPermissions, + ]); // When the update is successful, we need to refresh the client javascript and show a success modal useEffect(() => { @@ -910,7 +976,9 @@ export default function KvmIdRoute() { // Rebooting takes priority over connection status if (rebootState?.isRebooting) { - return ; + return ( + + ); } const hasConnectionFailed = @@ -937,184 +1005,186 @@ export default function KvmIdRoute() { } return null; - }, [location.pathname, rebootState?.isRebooting, rebootState?.postRebootAction, connectionFailed, peerConnectionState, peerConnection, setupPeerConnection, loadingMessage]); + }, [ + location.pathname, + rebootState?.isRebooting, + rebootState?.postRebootAction, + connectionFailed, + peerConnectionState, + peerConnection, + setupPeerConnection, + loadingMessage, + ]); return ( {!outlet && otaState.updating && ( - - + + + + + )} +
+ - - - - )} -
- -
-
-
+
+
+ -
- +
+ - -
- {/* Only show video feed if nickname is set (when required) and not pending approval */} - {(!showNicknameModal && currentMode !== "pending") ? ( - <> - -
-
- {!!ConnectionStatusElement && ConnectionStatusElement} +
+ {/* Only show video feed if nickname is set (when required) and not pending approval */} + {!showNicknameModal && currentMode !== "pending" ? ( + <> + +
+
+ {!!ConnectionStatusElement && ConnectionStatusElement} +
+
+ + ) : ( +
+
+ {showNicknameModal &&

Please set your nickname to continue

} + {currentMode === "pending" &&

Waiting for session approval...

}
- - ) : ( -
-
- {showNicknameModal &&

Please set your nickname to continue

} - {currentMode === "pending" &&

Waiting for session approval...

} -
-
- )} - + )} + +
-
-
e.stopPropagation()} - onMouseUp={e => e.stopPropagation()} - onMouseDown={e => e.stopPropagation()} - onKeyUp={e => e.stopPropagation()} - onKeyDown={e => { - e.stopPropagation(); - if (e.key === "Escape") navigateTo("/"); - }} - > - - {/* The 'used by other session' modal needs to have access to the connectWebRTC function */} - - +
e.stopPropagation()} + onMouseUp={e => e.stopPropagation()} + onMouseDown={e => e.stopPropagation()} + onKeyUp={e => e.stopPropagation()} + onKeyDown={e => { + e.stopPropagation(); + if (e.key === "Escape") navigateTo("/"); + }} + > + + {/* The 'used by other session' modal needs to have access to the connectWebRTC function */} + + - { - setNickname(nickname); - setShowNicknameModal(false); - setDisableVideoFocusTrap(false); + { + setNickname(nickname); + setShowNicknameModal(false); + setDisableVideoFocusTrap(false); - if (currentSessionId && send) { - try { - await sessionApi.updateNickname(send, currentSessionId, nickname); - } catch (error) { - console.error("Failed to update nickname:", error); + if (currentSessionId && send) { + try { + await sessionApi.updateNickname(send, currentSessionId, nickname); + } catch (error) { + console.error("Failed to update nickname:", error); + } } + }} + onSkip={() => { + setShowNicknameModal(false); + setDisableVideoFocusTrap(false); + }} + /> +
+ + {kvmTerminal && ( + + )} + + {serialConsole && ( + + )} + + {/* Unified Session Request Dialog */} + {(primaryControlRequest || newSessionRequest) && ( + + )} + + { + if (!send) return; + try { + await sessionApi.requestSessionApproval(send); + setAccessDenied(false); + } catch (error) { + console.error("Failed to re-request approval:", error); } }} - onSkip={() => { - setShowNicknameModal(false); - setDisableVideoFocusTrap(false); - }} /> -
- {kvmTerminal && ( - - )} - - {serialConsole && ( - - )} - - {/* Unified Session Request Dialog */} - {(primaryControlRequest || newSessionRequest) && ( - - )} - - { - if (!send) return; - try { - await sessionApi.requestSessionApproval(send); - setAccessDenied(false); - } catch (error) { - console.error("Failed to re-request approval:", error); - } - }} - /> - - + );