feat: TLS certificate file support
- NewConnectionDialog: TLS cert file selectors (CA/Cert/Key) + rejectUnauthorized checkbox - connection.ts: read cert files and pass to ioredis tls options - Add tlsRejectUnauthorized to all ConnectionConfig types - i18n keys for tlsCaPath/tlsCertPath/tlsKeyPath/tlsRejectUnauthorized
This commit is contained in:
@@ -18,6 +18,10 @@ const form = reactive({
|
||||
auth: '',
|
||||
username: '',
|
||||
tls: false,
|
||||
tlsCaPath: '',
|
||||
tlsCertPath: '',
|
||||
tlsKeyPath: '',
|
||||
tlsRejectUnauthorized: false,
|
||||
separator: ':',
|
||||
sshEnabled: false,
|
||||
sshHost: '',
|
||||
@@ -44,6 +48,9 @@ watch(() => props.visible, async (v) => {
|
||||
auth: decryptedAuth,
|
||||
username: props.connection.username,
|
||||
tls: props.connection.tls,
|
||||
tlsCaPath: props.connection.tlsCaPath || '',
|
||||
tlsCertPath: props.connection.tlsCertPath || '',
|
||||
tlsKeyPath: props.connection.tlsKeyPath || '',
|
||||
separator: props.connection.separator,
|
||||
sshEnabled: props.connection.sshEnabled || false,
|
||||
sshHost: props.connection.sshHost || '',
|
||||
@@ -57,7 +64,8 @@ watch(() => props.visible, async (v) => {
|
||||
isEdit.value = false
|
||||
Object.assign(form, {
|
||||
name: '', host: '127.0.0.1', port: 6379,
|
||||
auth: '', username: '', tls: false, separator: ':',
|
||||
auth: '', username: '', tls: false, tlsCaPath: '', tlsCertPath: '', tlsKeyPath: '',
|
||||
separator: ':',
|
||||
sshEnabled: false, sshHost: '', sshPort: 22,
|
||||
sshUsername: '', sshPassword: '', sshPrivateKeyPath: '', sshPassphrase: '',
|
||||
})
|
||||
@@ -79,6 +87,30 @@ async function selectFile() {
|
||||
}
|
||||
}
|
||||
|
||||
async function selectTlsCaFile() {
|
||||
const result = await window.electronAPI.dialog.openFile({
|
||||
title: t('connection.tlsCaPath'),
|
||||
filters: [{ name: 'Certificate Files', extensions: ['pem', 'crt', 'cer', 'key'] }],
|
||||
})
|
||||
if (result) { form.tlsCaPath = result.path }
|
||||
}
|
||||
|
||||
async function selectTlsCertFile() {
|
||||
const result = await window.electronAPI.dialog.openFile({
|
||||
title: t('connection.tlsCertPath'),
|
||||
filters: [{ name: 'Certificate Files', extensions: ['pem', 'crt', 'cer'] }],
|
||||
})
|
||||
if (result) { form.tlsCertPath = result.path }
|
||||
}
|
||||
|
||||
async function selectTlsKeyFile() {
|
||||
const result = await window.electronAPI.dialog.openFile({
|
||||
title: t('connection.tlsKeyPath'),
|
||||
filters: [{ name: 'Key Files', extensions: ['pem', 'key'] }],
|
||||
})
|
||||
if (result) { form.tlsKeyPath = result.path }
|
||||
}
|
||||
|
||||
async function handleTest() {
|
||||
if (!form.host.trim()) {
|
||||
ElMessage.warning('Host is required')
|
||||
@@ -92,6 +124,10 @@ async function handleTest() {
|
||||
auth: form.auth || undefined,
|
||||
username: form.username || undefined,
|
||||
tls: form.tls,
|
||||
tlsCaPath: form.tlsCaPath,
|
||||
tlsCertPath: form.tlsCertPath,
|
||||
tlsKeyPath: form.tlsKeyPath,
|
||||
tlsRejectUnauthorized: form.tlsRejectUnauthorized,
|
||||
sshEnabled: form.sshEnabled,
|
||||
sshHost: form.sshHost.trim(),
|
||||
sshPort: Number(form.sshPort) || 22,
|
||||
@@ -125,9 +161,10 @@ async function handleSave() {
|
||||
auth: form.auth,
|
||||
username: form.username.trim(),
|
||||
tls: form.tls,
|
||||
tlsCaPath: '',
|
||||
tlsCertPath: '',
|
||||
tlsKeyPath: '',
|
||||
tlsCaPath: form.tlsCaPath,
|
||||
tlsCertPath: form.tlsCertPath,
|
||||
tlsKeyPath: form.tlsKeyPath,
|
||||
tlsRejectUnauthorized: form.tlsRejectUnauthorized,
|
||||
sshEnabled: form.sshEnabled,
|
||||
sshHost: form.sshHost.trim(),
|
||||
sshPort: Number(form.sshPort) || 22,
|
||||
@@ -188,6 +225,29 @@ async function handleSave() {
|
||||
<el-form-item>
|
||||
<el-checkbox v-model="form.tls">{{ t('connection.tls') }}</el-checkbox>
|
||||
</el-form-item>
|
||||
<template v-if="form.tls">
|
||||
<el-form-item :label="t('connection.tlsCaPath')">
|
||||
<div style="display:flex;gap:8px;">
|
||||
<el-input v-model="form.tlsCaPath" placeholder="/path/to/ca.pem" />
|
||||
<el-button @click="selectTlsCaFile">{{ t('connection.selectFile') }}</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('connection.tlsCertPath')">
|
||||
<div style="display:flex;gap:8px;">
|
||||
<el-input v-model="form.tlsCertPath" placeholder="/path/to/client.pem" />
|
||||
<el-button @click="selectTlsCertFile">{{ t('connection.selectFile') }}</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('connection.tlsKeyPath')">
|
||||
<div style="display:flex;gap:8px;">
|
||||
<el-input v-model="form.tlsKeyPath" placeholder="/path/to/client-key.pem" />
|
||||
<el-button @click="selectTlsKeyFile">{{ t('connection.selectFile') }}</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-checkbox v-model="form.tlsRejectUnauthorized">{{ t('connection.tlsRejectUnauthorized') }}</el-checkbox>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<!-- SSH Tunnel -->
|
||||
<el-divider />
|
||||
|
||||
@@ -30,6 +30,10 @@ export default {
|
||||
password: 'Password',
|
||||
username: 'Username',
|
||||
tls: 'Enable TLS',
|
||||
tlsCaPath: 'CA Certificate',
|
||||
tlsCertPath: 'Client Certificate',
|
||||
tlsKeyPath: 'Client Private Key',
|
||||
tlsRejectUnauthorized: 'Verify Server Certificate',
|
||||
ssh: 'SSH Tunnel',
|
||||
sshHost: 'SSH Host',
|
||||
sshPort: 'SSH Port',
|
||||
|
||||
@@ -30,6 +30,10 @@ export default {
|
||||
password: '密码',
|
||||
username: '用户名',
|
||||
tls: '启用 TLS',
|
||||
tlsCaPath: 'CA 证书',
|
||||
tlsCertPath: '客户端证书',
|
||||
tlsKeyPath: '客户端私钥',
|
||||
tlsRejectUnauthorized: '验证服务器证书',
|
||||
ssh: 'SSH 隧道',
|
||||
sshHost: 'SSH 主机',
|
||||
sshPort: 'SSH 端口',
|
||||
|
||||
@@ -13,6 +13,7 @@ export interface ConnectionConfig {
|
||||
tlsCaPath: string
|
||||
tlsCertPath: string
|
||||
tlsKeyPath: string
|
||||
tlsRejectUnauthorized: boolean
|
||||
sshEnabled: boolean
|
||||
sshHost: string
|
||||
sshPort: number
|
||||
@@ -83,6 +84,10 @@ export const useConnectionStore = defineStore('connection', () => {
|
||||
password: auth || undefined,
|
||||
username: conn.username || undefined,
|
||||
tls: conn.tls,
|
||||
tlsCaPath: conn.tlsCaPath,
|
||||
tlsCertPath: conn.tlsCertPath,
|
||||
tlsKeyPath: conn.tlsKeyPath,
|
||||
tlsRejectUnauthorized: conn.tlsRejectUnauthorized,
|
||||
sshEnabled: conn.sshEnabled,
|
||||
sshHost: conn.sshHost,
|
||||
sshPort: conn.sshPort,
|
||||
@@ -151,6 +156,7 @@ export const useConnectionStore = defineStore('connection', () => {
|
||||
|
||||
async function testConnection(opts: {
|
||||
host: string; port: number; auth?: string; username?: string; tls?: boolean
|
||||
tlsCaPath?: string; tlsCertPath?: string; tlsKeyPath?: string
|
||||
sshEnabled?: boolean; sshHost?: string; sshPort?: number; sshUsername?: string
|
||||
sshPassword?: string; sshPrivateKeyPath?: string; sshPassphrase?: string
|
||||
}): Promise<{ success: boolean; error?: string }> {
|
||||
@@ -165,6 +171,10 @@ export const useConnectionStore = defineStore('connection', () => {
|
||||
password: opts.auth || undefined,
|
||||
username: opts.username || undefined,
|
||||
tls: opts.tls,
|
||||
tlsCaPath: opts.tlsCaPath,
|
||||
tlsCertPath: opts.tlsCertPath,
|
||||
tlsKeyPath: opts.tlsKeyPath,
|
||||
tlsRejectUnauthorized: (opts as any).tlsRejectUnauthorized,
|
||||
sshEnabled: opts.sshEnabled,
|
||||
sshHost: opts.sshHost,
|
||||
sshPort: opts.sshPort,
|
||||
|
||||
Reference in New Issue
Block a user