fix: ssh connect flow never fired + 120s host-key prompt budget
- Workspace: saved-credential connections now call connect() directly; ConnectFlow only rendered a connecting spinner without ever firing openSession, leaving attempts hung forever (root cause of the stuck '连接中' modal) - Host-key prompt timeout raised 30s -> 120s (a security decision needs time; 30s aborted while users compare fingerprints) - main: unhandledRejection/uncaughtException loggers; ssh diagnostics on error paths
This commit is contained in:
1 parent
bf3b2a7c30
commit
0be65370f6
3 files changed
+31
-6
No files matched your search
@@ -41,6 +41,13 @@ function createWindow(): void {
|
||||
}
|
||||
}
|
||||
|
||||
process.on('unhandledRejection', (reason) => {
|
||||
console.error('[main] unhandledRejection:', reason)
|
||||
})
|
||||
process.on('uncaughtException', (err) => {
|
||||
console.error('[main] uncaughtException:', err)
|
||||
})
|
||||
|
||||
app.whenReady().then(() => {
|
||||
registerIpc()
|
||||
// OS-level effects (login item, sleep blocker) must apply even if the
|
||||
|
||||
+9
-3
@@ -53,7 +53,9 @@ export interface SshServiceDeps {
|
||||
timeoutMs?: { prompt: number; connect: number }
|
||||
}
|
||||
|
||||
const DEFAULT_TIMEOUTS = { prompt: 30_000, connect: 15_000 }
|
||||
// Host-key confirmation is a security decision — users may take time to
|
||||
// compare fingerprints. 30s timed out on people who merely screenshot it.
|
||||
const DEFAULT_TIMEOUTS = { prompt: 120_000, connect: 15_000 }
|
||||
|
||||
/**
|
||||
* Open an ssh session (connect + auth + open shell) for `conn`.
|
||||
@@ -107,7 +109,8 @@ export async function connectSsh(
|
||||
try {
|
||||
fingerprint = fingerprintOf(hostKey)
|
||||
status = deps.knownHosts.check(conn.host, conn.port, hostKey).status
|
||||
} catch {
|
||||
} catch (err) {
|
||||
console.error(`[ssh] knownHosts.check threw: ${(err as Error).message}`)
|
||||
fingerprint = fingerprintOf(hostKey)
|
||||
status = 'new'
|
||||
}
|
||||
@@ -287,7 +290,10 @@ function promptUser(
|
||||
*/
|
||||
export function resolveHostKey(promptId: string, action: 'accept' | 'reject'): void {
|
||||
const pending = pendingPrompts.get(promptId)
|
||||
if (!pending) return
|
||||
if (!pending) {
|
||||
console.error(`[ssh] resolveHostKey: no pending prompt ${promptId}`)
|
||||
return
|
||||
}
|
||||
clearTimeout(pending.timer)
|
||||
pendingPrompts.delete(promptId)
|
||||
pending.resolve(action === 'accept')
|
||||
|
||||
@@ -248,9 +248,21 @@ export default function Workspace({ onOpenSettings }: WorkspaceProps): React.JSX
|
||||
[message]
|
||||
)
|
||||
|
||||
const handleConnectRequest = useCallback((conn: SshConnection): void => {
|
||||
setRequestedConn(conn)
|
||||
}, [])
|
||||
const handleConnectRequest = useCallback(
|
||||
(conn: SshConnection): void => {
|
||||
// Ask-at-connect connections go through the secret prompt first; saved
|
||||
// credentials must connect IMMEDIATELY — routing them through ConnectFlow
|
||||
// would only ever *render* a "connecting" spinner without firing openSession.
|
||||
const needsPassword = conn.auth === 'password' && conn.askPasswordAtConnect
|
||||
const needsPassphrase = conn.auth === 'privateKey' && conn.askPassphraseAtConnect
|
||||
if (needsPassword || needsPassphrase) {
|
||||
setRequestedConn(conn)
|
||||
return
|
||||
}
|
||||
void connect(conn, {})
|
||||
},
|
||||
[connect]
|
||||
)
|
||||
|
||||
const handleConnectCancel = useCallback((): void => {
|
||||
setRequestedConn(null)
|
||||
|
||||
Reference in new issue
Block a user