fix: improve terminal robustness with exponential backoff and better keepalive
Deploy Dashboard (Dev) / deploy-dev (push) Successful in 3m11s

- Backend: Add 10s connection timeout, improve SSH keepalive (15s × 8 retries)
- Backend: Add detailed error logging for debugging
- Frontend: Implement exponential backoff (1s → 30s max) instead of fixed 2s
- Frontend: Reduce heartbeat to 12s (within 15s SSH keepalive window)
- Frontend: Reset reconnect counter on successful connection
- Frontend: Fix race condition in auth recovery after manual close
This commit is contained in:
Gan, Jimmy
2026-03-11 00:24:17 +08:00
parent 4201917e17
commit cc3e9dc6fa
2 changed files with 32 additions and 14 deletions
+22 -9
View File
@@ -125,13 +125,22 @@ async def ws_endpoint(websocket: WebSocket, host: str = Query("nas"), token: str
h = HOSTS[host]
try:
conn = await asyncssh.connect(
h["host"], username=h["user"],
client_keys=[h["key"]], known_hosts=_known_hosts,
keepalive_interval=30,
keepalive_count_max=3,
conn = await asyncio.wait_for(
asyncssh.connect(
h["host"], username=h["user"],
client_keys=[h["key"]], known_hosts=_known_hosts,
keepalive_interval=15,
keepalive_count_max=8,
),
timeout=10.0
)
except Exception:
except asyncio.TimeoutError:
logger.error("SSH connection to %s timed out after 10s", host)
await _release_session(session_id)
await websocket.close(code=1011, reason="SSH connection timeout")
return
except Exception as e:
logger.error("SSH connection to %s failed: %s", host, e)
await _release_session(session_id)
await websocket.close(code=1011, reason="SSH connection failed")
return
@@ -149,8 +158,12 @@ async def ws_endpoint(websocket: WebSocket, host: str = Query("nas"), token: str
if not data:
break
await websocket.send_bytes(data)
except (asyncssh.Error, asyncio.CancelledError):
except asyncssh.Error as e:
logger.warning("SSH read error for %s: %s", host, e)
except asyncio.CancelledError:
pass
except Exception as e:
logger.error("Unexpected error reading SSH for %s: %s", host, e)
read_task = asyncio.create_task(read_ssh())
@@ -169,8 +182,8 @@ async def ws_endpoint(websocket: WebSocket, host: str = Query("nas"), token: str
if msg["text"] == "__ping__":
continue
process.stdin.write(msg["text"].encode())
except Exception:
pass
except Exception as e:
logger.info("WebSocket receive loop ended for %s: %s", host, e)
finally:
read_task.cancel()
process.close()