Fixed Connect Button in Remote Shell

This commit is contained in:
2025-12-07 17:36:48 -07:00
parent 8bbd6b86ed
commit d6b701efca
2 changed files with 31 additions and 25 deletions

View File

@@ -668,7 +668,23 @@ class ReverseTunnelService:
server.close(code=code, reason=reason) server.close(code=code, reason=reason)
except Exception: except Exception:
self.logger.debug("protocol server close failed tunnel_id=%s", tunnel_id, exc_info=True) self.logger.debug("protocol server close failed tunnel_id=%s", tunnel_id, exc_info=True)
if tunnel_id in self._protocol_servers:
try:
self._protocol_servers.pop(tunnel_id, None)
except Exception:
pass
self._push_stop_to_agent(lease, reason=reason) self._push_stop_to_agent(lease, reason=reason)
websocket = self._agent_sockets.pop(tunnel_id, None)
if websocket is not None:
try:
self.lease_manager.mark_agent_disconnected(tunnel_id)
except Exception:
pass
try:
if self._loop:
self._loop.call_soon_threadsafe(asyncio.create_task, websocket.close())
except Exception:
self.logger.debug("agent websocket close failed tunnel_id=%s", tunnel_id, exc_info=True)
self.release_bridge(tunnel_id, reason=reason) self.release_bridge(tunnel_id, reason=reason)
return True return True

View File

@@ -107,6 +107,15 @@ function highlightPs(code) {
} }
} }
const INITIAL_MILESTONES = {
requested: false,
leaseIssued: false,
operatorJoined: false,
channelOpened: false,
ack: false,
active: false,
};
export default function ReverseTunnelPowershell({ device }) { export default function ReverseTunnelPowershell({ device }) {
const [connectionType, setConnectionType] = useState("ps"); const [connectionType, setConnectionType] = useState("ps");
const [tunnel, setTunnel] = useState(null); const [tunnel, setTunnel] = useState(null);
@@ -119,14 +128,7 @@ export default function ReverseTunnelPowershell({ device }) {
const [, setPolling] = useState(false); const [, setPolling] = useState(false);
const [psStatus, setPsStatus] = useState({}); const [psStatus, setPsStatus] = useState({});
const [statusLine, setStatusLine] = useState("Idle"); const [statusLine, setStatusLine] = useState("Idle");
const [milestones, setMilestones] = useState({ const [milestones, setMilestones] = useState(() => ({ ...INITIAL_MILESTONES }));
requested: false,
leaseIssued: false,
operatorJoined: false,
channelOpened: false,
ack: false,
active: false,
});
const socketRef = useRef(null); const socketRef = useRef(null);
const pollTimerRef = useRef(null); const pollTimerRef = useRef(null);
const resizeTimerRef = useRef(null); const resizeTimerRef = useRef(null);
@@ -142,12 +144,6 @@ export default function ReverseTunnelPowershell({ device }) {
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
useEffect(() => {
debugLog("component mount", { hostname: device?.hostname, agentId });
return () => debugLog("component unmount");
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const hostname = useMemo(() => { const hostname = useMemo(() => {
return ( return (
normalizeText(device?.hostname) || normalizeText(device?.hostname) ||
@@ -179,14 +175,7 @@ export default function ReverseTunnelPowershell({ device }) {
setOutput(""); setOutput("");
setInput(""); setInput("");
setPsStatus({}); setPsStatus({});
setMilestones({ setMilestones({ ...INITIAL_MILESTONES });
requested: false,
leaseIssued: false,
operatorJoined: false,
channelOpened: false,
ack: false,
active: false,
});
setStatusLine("Idle"); setStatusLine("Idle");
}, []); }, []);
@@ -204,7 +193,6 @@ export default function ReverseTunnelPowershell({ device }) {
}, []); }, []);
const stopPolling = useCallback(() => { const stopPolling = useCallback(() => {
setStatusLine("Stopping poll loop…");
if (pollTimerRef.current) { if (pollTimerRef.current) {
clearTimeout(pollTimerRef.current); clearTimeout(pollTimerRef.current);
pollTimerRef.current = null; pollTimerRef.current = null;
@@ -328,6 +316,7 @@ export default function ReverseTunnelPowershell({ device }) {
setStatusLine(`Tunnel ${tunnelId} reported closed`); setStatusLine(`Tunnel ${tunnelId} reported closed`);
setSessionState("closed"); setSessionState("closed");
setTunnel(null); setTunnel(null);
setMilestones({ ...INITIAL_MILESTONES });
stopPolling(); stopPolling();
return; return;
} }
@@ -351,6 +340,7 @@ export default function ReverseTunnelPowershell({ device }) {
async (reason = "operator_disconnect") => { async (reason = "operator_disconnect") => {
debugLog("handleDisconnect begin", { reason, tunnelId: tunnel?.tunnel_id, psStatus, sessionState }); debugLog("handleDisconnect begin", { reason, tunnelId: tunnel?.tunnel_id, psStatus, sessionState });
setStatusLine(`Disconnect requested (${reason})`); setStatusLine(`Disconnect requested (${reason})`);
setPsStatus({});
const socket = socketRef.current; const socket = socketRef.current;
const tunnelId = tunnel?.tunnel_id; const tunnelId = tunnel?.tunnel_id;
if (joinRetryRef.current) { if (joinRetryRef.current) {
@@ -369,7 +359,7 @@ export default function ReverseTunnelPowershell({ device }) {
disconnectSocket(); disconnectSocket();
setTunnel(null); setTunnel(null);
setSessionState("closed"); setSessionState("closed");
setMilestones((prev) => ({ ...prev, active: false })); setMilestones({ ...INITIAL_MILESTONES });
setStatusLine("Disconnected"); setStatusLine("Disconnected");
debugLog("handleDisconnect finished", { tunnelId }); debugLog("handleDisconnect finished", { tunnelId });
}, },
@@ -561,7 +551,7 @@ export default function ReverseTunnelPowershell({ device }) {
[appendOutput, emitAsync] [appendOutput, emitAsync]
); );
const isConnected = sessionState === "connected" || psStatus?.ack; const isConnected = sessionState === "connected" || (psStatus?.ack && !psStatus?.closed);
const isClosed = sessionState === "closed" || psStatus?.closed; const isClosed = sessionState === "closed" || psStatus?.closed;
const isBusy = const isBusy =
sessionState === "requesting" || sessionState === "requesting" ||