fix(ssh): resolve saved connection by ID when testing with ID-only config#1431
fix(ssh): resolve saved connection by ID when testing with ID-only config#1431dhunganapramod9 wants to merge 1 commit intogeneralaction:mainfrom
Conversation
…nfig - Detect ID-only sshTestConnection calls (id set, host/username empty) - Load saved connection from DB and hydrate credentials from keytar - Run connection test against resolved config so SshConnectionTestButton works - Update SshConnectionTestButton comment to document main-process behavior
|
@dhunganapramod9 is attempting to deploy a commit to the General Action Team on Vercel. A member of the Team first needs to authorize it. |
Greptile SummaryThis PR fixes the SSH "Test Connection" flow for saved connections: when Key points:
Confidence Score: 4/5
|
| Filename | Overview |
|---|---|
| src/main/ipc/sshIpc.ts | Adds ID-only resolution in the TEST_CONNECTION handler by detecting an empty host/username, querying the DB, and hydrating credentials from keytar before running the test. Logic is sound and mirrors the existing CONNECT pattern; minor: the outer if (isIdOnly && config.id) guard redundantly re-checks config.id which is already part of the isIdOnly expression. |
| src/renderer/components/ssh/SshConnectionTestButton.tsx | Comment-only change removing the TODO and documenting that the main process now resolves the connection by ID. The IPC payload retains a hardcoded authType: 'password' dummy field that is harmless but slightly misleading. |
Sequence Diagram
sequenceDiagram
participant R as SshConnectionTestButton
participant IPC as TEST_CONNECTION Handler
participant DB as SQLite via Drizzle
participant KT as keytar CredentialService
participant SSH as ssh2 Client
R->>IPC: sshTestConnection with id-only payload
IPC->>IPC: Detect isIdOnly (id set, host and username empty)
alt isIdOnly true (new path)
IPC->>DB: Query sshConnections by id
DB-->>IPC: Return saved row
IPC->>IPC: mapRowToConfig(row) into resolvedConfig
alt authType is password
IPC->>KT: getPassword(id)
KT-->>IPC: stored credential
else authType is key
IPC->>KT: getPassphrase(id)
KT-->>IPC: stored credential
end
else isIdOnly false (existing path)
IPC->>IPC: resolvedConfig stays as original config
end
IPC->>SSH: connect(resolvedConfig)
SSH-->>IPC: ready or error event
IPC-->>R: ConnectionTestResult
Comments Outside Diff (1)
-
src/renderer/components/ssh/SshConnectionTestButton.tsx, line 39-46 (link)Hardcoded
authType: 'password'is misleadingThe
authType: 'password'value sent in the ID-only payload is a dummy placeholder that has no effect when the main process correctly resolves the config from the DB. However, any reader unfamiliar with the server-side logic may infer that this component only tests password-based connections, which is incorrect — it supports all auth types via the DB lookup.Consider using a sentinel string (e.g.
authType: 'unknown' as any) or adding an inline comment explaining the field is ignored, to make the intent clearer:Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Last reviewed commit: 14c73ca
| const isIdOnly = | ||
| config.id && | ||
| (typeof config.host !== 'string' || !config.host.trim()) && | ||
| (typeof config.username !== 'string' || !config.username.trim()); | ||
|
|
||
| let resolvedConfig: SshConfig & { password?: string; passphrase?: string } = config; | ||
| if (isIdOnly && config.id) { |
There was a problem hiding this comment.
Redundant config.id guard in the if condition
isIdOnly already short-circuits to a falsy value whenever config.id is falsy (it's the first operand in the && chain). The outer if (isIdOnly && config.id) therefore re-checks the same thing twice and can never be true unless config.id is already truthy.
The extra guard does help TypeScript narrow config.id to string inside the block, but that could be achieved more explicitly with a type assertion or by restructuring the guard. As written it looks like a logical mistake rather than an intentional narrowing hint.
| const isIdOnly = | |
| config.id && | |
| (typeof config.host !== 'string' || !config.host.trim()) && | |
| (typeof config.username !== 'string' || !config.username.trim()); | |
| let resolvedConfig: SshConfig & { password?: string; passphrase?: string } = config; | |
| if (isIdOnly && config.id) { | |
| if (isIdOnly) { | |
| const id = config.id!; | |
| const { db } = await getDrizzleClient(); | |
| const rows = await db | |
| .select({ | |
| id: sshConnectionsTable.id, | |
| name: sshConnectionsTable.name, | |
| host: sshConnectionsTable.host, | |
| port: sshConnectionsTable.port, | |
| username: sshConnectionsTable.username, | |
| authType: sshConnectionsTable.authType, | |
| privateKeyPath: sshConnectionsTable.privateKeyPath, | |
| useAgent: sshConnectionsTable.useAgent, | |
| }) | |
| .from(sshConnectionsTable) | |
| .where(eq(sshConnectionsTable.id, id)) | |
| .limit(1); |
Description
When the renderer calls
sshTestConnectionwith only a connection ID and empty host/username (e.g. fromSshConnectionTestButton), the main process previously used those empty values and the test could not succeed. This change detects that "ID-only" call shape, loads the saved connection from the DB, hydrates credentials from keytar, and runs the test against the resolved config.Rationale
SshConnectionTestButtononly has access toconnectionIdand was intentionally calling the IPC with ID + empty fields and a TODO for the main process to handle it. The main process did not handle that case; this fix adds the same resolve-by-ID pattern already used by theCONNECThandler.Changes
src/main/ipc/sshIpc.ts): In theTEST_CONNECTIONhandler, detect ID-only calls (config hasidbuthostorusernameempty). For those, load the saved connection from the DB, hydrate password/passphrase from keytar, and run the test using the resolved config.src/renderer/components/ssh/SshConnectionTestButton.tsx): Comment updated to state that the main process accepts ID-only and resolves from DB/keytar.Testing
CONNECThandler (resolve by ID from DB + keytar).SshConnectionTestButton.Pre-PR Checklist
pnpm run d(orpnpm run dev) starts cleanly.pnpm run format.pnpm run lint.pnpm run type-check.pnpm exec vitest run.