feat: SSH tunnel support

- Add ssh2 dependency for SSH port forwarding
- NewConnectionDialog: SSH toggle + host/port/user/password/privateKey/passphrase form
- connection.ts: createSshTunnel() with local port forwarding
- Connect ioredis through SSH tunnel when sshEnabled
- Clean up SSH client + tunnel server on disconnect
- i18n keys for all SSH fields (zh-CN + en)
- Add sshPassphrase to all ConnectionConfig types
This commit is contained in:
2026-07-05 16:25:35 +08:00
parent bb32d2d647
commit 6e6f170403
9 changed files with 368 additions and 38 deletions
+17
View File
@@ -19,6 +19,7 @@ export interface ConnectionConfig {
sshUsername: string
sshPassword: string
sshPrivateKeyPath: string
sshPassphrase: string
cluster: boolean
sentinelMasterName: string
separator: string
@@ -82,6 +83,13 @@ export const useConnectionStore = defineStore('connection', () => {
password: auth || undefined,
username: conn.username || undefined,
tls: conn.tls,
sshEnabled: conn.sshEnabled,
sshHost: conn.sshHost,
sshPort: conn.sshPort,
sshUsername: conn.sshUsername,
sshPassword: conn.sshPassword,
sshPrivateKeyPath: conn.sshPrivateKeyPath,
sshPassphrase: conn.sshPassphrase,
})
if (result.success) {
activeId.value = conn.id
@@ -143,6 +151,8 @@ export const useConnectionStore = defineStore('connection', () => {
async function testConnection(opts: {
host: string; port: number; auth?: string; username?: string; tls?: boolean
sshEnabled?: boolean; sshHost?: string; sshPort?: number; sshUsername?: string
sshPassword?: string; sshPrivateKeyPath?: string; sshPassphrase?: string
}): Promise<{ success: boolean; error?: string }> {
if (!window.electronAPI) {
return { success: false, error: 'Electron API not ready' }
@@ -155,6 +165,13 @@ export const useConnectionStore = defineStore('connection', () => {
password: opts.auth || undefined,
username: opts.username || undefined,
tls: opts.tls,
sshEnabled: opts.sshEnabled,
sshHost: opts.sshHost,
sshPort: opts.sshPort,
sshUsername: opts.sshUsername,
sshPassword: opts.sshPassword,
sshPrivateKeyPath: opts.sshPrivateKeyPath,
sshPassphrase: opts.sshPassphrase,
})
if (result.success) {
await window.electronAPI.redis.disconnect(testId)