From 797c0d8d21eb8cb076fd99d8f08c99a5ab0f5a28 Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Thu, 28 May 2026 00:08:59 +0800 Subject: [PATCH] Add Redis cluster support and improve key loading --- Cargo.lock | 9 + .../connection/ConnectionDialog.vue | 59 +- .../src/components/redis/RedisKeyBrowser.vue | 27 +- apps/desktop/src/i18n/locales/en.ts | 3 + apps/desktop/src/i18n/locales/es.ts | 3 + apps/desktop/src/i18n/locales/zh-CN.ts | 3 + apps/desktop/src/types/database.ts | 3 +- crates/dbx-core/Cargo.toml | 2 +- crates/dbx-core/src/cloud_sync.rs | 1 + crates/dbx-core/src/connection.rs | 17 +- crates/dbx-core/src/connection_secrets.rs | 1 + crates/dbx-core/src/db/redis_driver.rs | 595 +++++++++++++++--- crates/dbx-core/src/models/connection.rs | 8 + crates/dbx-core/src/query.rs | 1 + crates/dbx-core/src/redis_ops.rs | 345 +++++++--- crates/dbx-core/src/transfer.rs | 1 + .../connectionDialogRedisSentinel.test.ts | 28 +- packages/app-tests/redisValueSearch.test.ts | 3 + src-tauri/Cargo.toml | 2 +- src-tauri/src/commands/connection.rs | 22 +- 20 files changed, 932 insertions(+), 201 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7e98a8574e..122904cd2a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1445,6 +1445,12 @@ dependencies = [ "libc", ] +[[package]] +name = "crc16" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "338089f42c427b86394a5ee60ff321da23a5c89c9d89514c829687b26359fcff" + [[package]] name = "crc32fast" version = "1.5.0" @@ -6002,8 +6008,11 @@ dependencies = [ "bytes", "cfg-if", "combine", + "crc16", + "futures-sink", "futures-util", "itoa", + "log", "num-bigint", "percent-encoding", "pin-project-lite", diff --git a/apps/desktop/src/components/connection/ConnectionDialog.vue b/apps/desktop/src/components/connection/ConnectionDialog.vue index 48eb3941ef..916ef9645c 100644 --- a/apps/desktop/src/components/connection/ConnectionDialog.vue +++ b/apps/desktop/src/components/connection/ConnectionDialog.vue @@ -105,6 +105,7 @@ const defaultForm = (): Omit => ({ redis_sentinel_username: "", redis_sentinel_password: "", redis_sentinel_tls: false, + redis_cluster_nodes: "", }); const form = ref(defaultForm()); @@ -362,6 +363,7 @@ watch( redis_sentinel_username: config.redis_sentinel_username || "", redis_sentinel_password: config.redis_sentinel_password || "", redis_sentinel_tls: config.redis_sentinel_tls || false, + redis_cluster_nodes: config.redis_cluster_nodes || "", }; selectedType.value = profile; mongoUseUrl.value = !!config.connection_string; @@ -753,15 +755,29 @@ function connectionConfigForSubmit(id: string): ConnectionConfig { config.redis_sentinel_username = undefined; config.redis_sentinel_password = undefined; config.redis_sentinel_tls = undefined; + config.redis_cluster_nodes = undefined; } else if (config.redis_connection_mode === "sentinel") { config.redis_sentinel_master = config.redis_sentinel_master?.trim() || ""; config.redis_sentinel_nodes = normalizeRedisSentinelNodes(config.redis_sentinel_nodes || ""); config.redis_sentinel_username = config.redis_sentinel_username?.trim() || ""; + config.redis_cluster_nodes = undefined; const firstNode = firstRedisSentinelEndpoint(config.redis_sentinel_nodes); if (firstNode) { config.host = firstNode.host; config.port = firstNode.port; } + } else if (config.redis_connection_mode === "cluster") { + config.redis_sentinel_master = undefined; + config.redis_sentinel_nodes = undefined; + config.redis_sentinel_username = undefined; + config.redis_sentinel_password = undefined; + config.redis_sentinel_tls = undefined; + config.redis_cluster_nodes = normalizeRedisClusterNodes(config.redis_cluster_nodes || ""); + const firstNode = firstRedisClusterEndpoint(config.redis_cluster_nodes); + if (firstNode) { + config.host = firstNode.host; + config.port = firstNode.port; + } } else { config.redis_connection_mode = "standalone"; config.redis_sentinel_master = undefined; @@ -769,6 +785,7 @@ function connectionConfigForSubmit(id: string): ConnectionConfig { config.redis_sentinel_username = undefined; config.redis_sentinel_password = undefined; config.redis_sentinel_tls = undefined; + config.redis_cluster_nodes = undefined; } if (config.db_type !== "mysql" && config.db_type !== "clickhouse") { config.ca_cert_path = undefined; @@ -875,6 +892,14 @@ function normalizePostgresSslMode(value: string): string { } function normalizeRedisSentinelNodes(value: string): string { + return normalizeRedisNodeList(value); +} + +function normalizeRedisClusterNodes(value: string): string { + return normalizeRedisNodeList(value); +} + +function normalizeRedisNodeList(value: string): string { return value .split(/[\n,;]+/) .map((node) => node.trim()) @@ -883,13 +908,21 @@ function normalizeRedisSentinelNodes(value: string): string { } function firstRedisSentinelEndpoint(value?: string): { host: string; port: number } | null { - const first = normalizeRedisSentinelNodes(value || "") + const first = normalizeRedisNodeList(value || "") .split("\n") .find(Boolean); if (!first) return null; return parseRedisEndpoint(first, 26379); } +function firstRedisClusterEndpoint(value?: string): { host: string; port: number } | null { + const first = normalizeRedisNodeList(value || "") + .split("\n") + .find(Boolean); + if (!first) return null; + return parseRedisEndpoint(first, 6379); +} + function parseRedisEndpoint(value: string, defaultPort: number): { host: string; port: number } { const endpoint = value .trim() @@ -1616,7 +1649,7 @@ function openExternalUrl(url: string) {
+
@@ -1669,6 +1711,17 @@ function openExternalUrl(url: string) {
+