perf: fast relay connect on mobile and desktop + connect splash + edit-safe instances
Relay connect used to serialize a dead LAN probe (up to 8s per stale address on mobile, 2-4s on desktop) in front of the relay attempt, then paid a second WebSocket connect + E2EE handshake because the probe tunnel was thrown away. - mobile probeConnectionCandidates: race the relay probe against the direct chain with a 1.5s direct headstart; a live LAN still wins, a dead one no longer delays startup - relay probes adopt their tunnel as the runtime tunnel (adoptRelayTunnel) instead of dialing a fresh one — applies to auto-connect, pairing redeem, password login, and the desktop host switcher's relay fallback - relay probe drops the /health round-trip: the E2EE handshake already proves the server identity, /auth/session alone proves liveness and auth - desktop restoreDesktopRelayRuntime: same headstart race; a late direct success hot-switches back (stable runtimeKey); startup probe now passes expectedServerId so a re-leased LAN address never sees the token - launch splash shows 'Connecting to device: <label>' with animated dots under the (still centered) logo, translated in all locales - editing a saved instance no longer rebuilds it from the URL field alone: the id is passed through, relay/https candidates are preserved, and a token-key change migrates the Keychain token instead of orphaning it
This commit is contained in:
@@ -32,6 +32,8 @@ import {
|
||||
type HostProbeResult,
|
||||
} from '@/lib/desktopHosts';
|
||||
import { scheduleDesktopHostCandidateRefresh } from '@/lib/desktopRelayRestore';
|
||||
import { adoptRelayTunnel } from '@/lib/relay/runtime-tunnel';
|
||||
import { createRelayTunnelClient } from '@/lib/relay/tunnel-client';
|
||||
import { getRuntimeApiBaseUrl, getRuntimeKey, subscribeRuntimeEndpointChanged, switchRuntimeEndpoint } from '@/lib/runtime-switch';
|
||||
import {
|
||||
desktopSshConnect,
|
||||
@@ -513,7 +515,13 @@ export function DesktopHostSwitcherDialog({
|
||||
// Relay legs ride the E2EE tunnel activated in-renderer via
|
||||
// switchRuntimeEndpoint({ relay }); the runtime fetch/socket layers route
|
||||
// through the tunnel from the singleton registry.
|
||||
const activateRelay = (relay: NonNullable<DesktopHost['relay']>) => {
|
||||
const activateRelay = (relay: NonNullable<DesktopHost['relay']>, liveTunnel?: ReturnType<typeof createRelayTunnelClient>) => {
|
||||
// Adopt the probe's live tunnel (when it kept one) BEFORE the switch: the
|
||||
// activate call inside switchRuntimeEndpoint sees an equal descriptor and
|
||||
// reuses it — no second WebSocket connect + E2EE handshake.
|
||||
if (liveTunnel) {
|
||||
adoptRelayTunnel({ relayUrl: relay.relayUrl, serverId: relay.serverId, hostEncPubJwk: relay.hostEncPubJwk }, liveTunnel);
|
||||
}
|
||||
switchRuntimeEndpoint({
|
||||
apiBaseUrl: typeof window !== 'undefined' ? window.location.origin : '',
|
||||
clientToken: host.clientToken || null,
|
||||
@@ -562,11 +570,14 @@ export function DesktopHostSwitcherDialog({
|
||||
finalStatus = { status: probe.status, latencyMs: probe.latencyMs };
|
||||
if (!isBlockedHostStatus(probe.status)) transport = 'direct';
|
||||
}
|
||||
let relayProbeTunnel: ReturnType<typeof createRelayTunnelClient> | undefined;
|
||||
if (!transport && host.relay) {
|
||||
const probe = await probeRelayDesktopHost(host.relay).catch((): HostProbeResult => ({ status: 'unreachable', latencyMs: 0 }));
|
||||
const probe = await probeRelayDesktopHost(host.relay, { keepTunnel: true })
|
||||
.catch((): HostProbeResult => ({ status: 'unreachable', latencyMs: 0 }));
|
||||
if (probe.status === 'ok') {
|
||||
finalStatus = { status: probe.status, latencyMs: probe.latencyMs, via: 'relay' };
|
||||
transport = 'relay';
|
||||
relayProbeTunnel = 'tunnel' in probe ? probe.tunnel : undefined;
|
||||
}
|
||||
}
|
||||
setStatusById((prev) => ({ ...prev, [host.id]: finalStatus }));
|
||||
@@ -577,7 +588,7 @@ export function DesktopHostSwitcherDialog({
|
||||
return;
|
||||
}
|
||||
if (transport === 'relay' && host.relay) {
|
||||
activateRelay(host.relay);
|
||||
activateRelay(host.relay, relayProbeTunnel);
|
||||
} else {
|
||||
switchRuntimeEndpoint({ apiBaseUrl: apiOrigin, clientToken: clientToken || null, requestHeaders: host.requestHeaders || null, runtimeKey: runtimeKeyForHost(host) });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user