From c315ea9c96bb81dcb26ea06cf530e5e2e66bec75 Mon Sep 17 00:00:00 2001 From: Syngnat Date: Tue, 2 Jun 2026 15:31:00 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat(elasticsearch):=20=E8=A1=A5?= =?UTF-8?q?=E9=BD=90=E6=96=B0=E5=BB=BA=E8=BF=9E=E6=8E=A5=E5=85=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 前端连接弹窗新增 Elasticsearch 入口、默认端口、URI 示例和默认索引配置 - 补齐 Elasticsearch 图标、数据源能力、SQL dialect 和只读查询策略 - 后端驱动管理注册 Elasticsearch 版本、模块路径、构建标签和默认安装入口 - 增加连接展示、能力识别和驱动定义测试覆盖 --- .../ConnectionModal.edit-password.test.tsx | 14 ++++ frontend/src/components/ConnectionModal.tsx | 80 +++++++++++++++++- .../src/components/DatabaseIcons.test.tsx | 6 ++ frontend/src/components/DatabaseIcons.tsx | 9 +- frontend/src/store.ts | 4 + .../utils/connectionModalPresentation.test.ts | 10 +++ .../src/utils/connectionModalPresentation.ts | 16 ++++ .../src/utils/dataSourceCapabilities.test.ts | 18 ++++ frontend/src/utils/dataSourceCapabilities.ts | 5 +- frontend/src/utils/sqlDialect.test.ts | 2 + frontend/src/utils/sqlDialect.ts | 5 ++ internal/app/methods_driver.go | 84 ++++++++++--------- internal/app/methods_driver_version_test.go | 33 ++++++++ 13 files changed, 240 insertions(+), 46 deletions(-) diff --git a/frontend/src/components/ConnectionModal.edit-password.test.tsx b/frontend/src/components/ConnectionModal.edit-password.test.tsx index 55b50d8..7300adf 100644 --- a/frontend/src/components/ConnectionModal.edit-password.test.tsx +++ b/frontend/src/components/ConnectionModal.edit-password.test.tsx @@ -16,3 +16,17 @@ describe('ConnectionModal edit password behavior', () => { expect(source).toContain('String(config.password || "") === ""'); }); }); + +describe('ConnectionModal data source registry', () => { + it('exposes Elasticsearch in the create-connection picker with HTTP defaults', () => { + expect(source).toContain('case "elasticsearch":\n return 9200;'); + expect(source).toContain('elasticsearch: ["http", "https"]'); + expect(source).toContain('key: "elasticsearch"'); + expect(source).toContain('name: "Elasticsearch"'); + expect(source).toContain('getDbIcon("elasticsearch", undefined, 36)'); + expect(source).toContain('type === "elasticsearch"'); + expect(source).toContain('"http://elastic:pass@127.0.0.1:9200/logs-*"'); + expect(source).toContain('label="默认索引(可选)"'); + expect(source).toContain('"显示索引 (留空显示全部)"'); + }); +}); diff --git a/frontend/src/components/ConnectionModal.tsx b/frontend/src/components/ConnectionModal.tsx index 3f5b4a3..0baad1a 100644 --- a/frontend/src/components/ConnectionModal.tsx +++ b/frontend/src/components/ConnectionModal.tsx @@ -259,6 +259,8 @@ const getDefaultPortByType = (type: string) => { return 1972; case "mongodb": return 27017; + case "elasticsearch": + return 9200; case "highgo": return 5866; case "mariadb": @@ -282,6 +284,7 @@ const singleHostUriSchemesByType: Record = { sqlserver: ["sqlserver"], iris: ["iris", "intersystems"], redis: ["redis"], + elasticsearch: ["http", "https"], tdengine: ["tdengine"], dameng: ["dameng", "dm"], kingbase: ["kingbase"], @@ -308,6 +311,7 @@ const sslSupportedTypes = new Set([ "opengauss", "mongodb", "redis", + "elasticsearch", "tdengine", ]); @@ -334,6 +338,7 @@ const sslCAPathSupportedTypes = new Set([ "opengauss", "mongodb", "redis", + "elasticsearch", ]); const sslClientCertificateSupportedTypes = new Set([ @@ -352,6 +357,7 @@ const sslClientCertificateSupportedTypes = new Set([ "opengauss", "mongodb", "redis", + "elasticsearch", ]); const supportsSSLCAPathForType = (type: string) => @@ -405,6 +411,7 @@ const supportsConnectionParamsForType = (type: string) => type === "iris" || type === "clickhouse" || type === "mongodb" || + type === "elasticsearch" || type === "dameng" || type === "tdengine"; @@ -1967,6 +1974,15 @@ const ConnectionModal: React.FC<{ parsedValues.useSSL = false; parsedValues.sslMode = "disable"; } + } else if (type === "elasticsearch") { + const isHTTPS = trimmedUri.toLowerCase().startsWith("https://"); + const skipVerify = normalizeBool(parsed.params.get("skip_verify")); + parsedValues.useSSL = isHTTPS; + parsedValues.sslMode = isHTTPS + ? skipVerify + ? "skip-verify" + : "required" + : "disable"; } } return parsedValues; @@ -2032,6 +2048,9 @@ const ConnectionModal: React.FC<{ if (dbType === "redis") { return "redis://:pass@127.0.0.1:6379,127.0.0.2:6379/0?topology=cluster"; } + if (dbType === "elasticsearch") { + return "http://elastic:pass@127.0.0.1:9200/logs-*"; + } if (dbType === "oracle") { return "oracle://user:pass@127.0.0.1:1521/ORCLPDB1"; } @@ -2251,6 +2270,10 @@ const ConnectionModal: React.FC<{ ? values.useSSL ? "https" : "http" + : type === "elasticsearch" + ? values.useSSL + ? "https" + : "http" : type; const dbPath = database ? `/${encodeURIComponent(database)}` : ""; const params = new URLSearchParams(); @@ -2297,6 +2320,11 @@ const ConnectionModal: React.FC<{ if (mode === "skip-verify" || mode === "preferred") { params.set("skip_verify", "true"); } + } else if (type === "elasticsearch") { + if (mode === "skip-verify" || mode === "preferred") { + params.set("skip_verify", "true"); + } + appendSSLPathParamsForUri(params, type, values); } } else if (supportsSSLForType(type)) { if (isPostgresCompatibleSSLType(type)) { @@ -3813,7 +3841,13 @@ const ConnectionModal: React.FC<{ }); } else if (type !== "custom") { const defaultUser = - type === "clickhouse" ? "default" : type === "redis" ? "" : "root"; + type === "clickhouse" + ? "default" + : type === "redis" + ? "" + : type === "elasticsearch" + ? "elastic" + : "root"; const sslCapableType = supportsSSLForType(type); setUseSSL(false); setUseHttpTunnel(false); @@ -4005,6 +4039,11 @@ const ConnectionModal: React.FC<{ name: "Redis", icon: getDbIcon("redis", undefined, 36), }, + { + key: "elasticsearch", + name: "Elasticsearch", + icon: getDbIcon("elasticsearch", undefined, 36), + }, ], }, { @@ -4045,6 +4084,8 @@ const ConnectionModal: React.FC<{ return "单机 / 集群"; case "mongodb": return "单机 / 副本集"; + case "elasticsearch": + return "索引 / JSON DSL"; case "oceanbase": return "MySQL / Oracle 租户"; case "sqlite": @@ -5087,6 +5128,25 @@ const ConnectionModal: React.FC<{ ), })} + {dbType === "elasticsearch" && + renderConfigSectionCard({ + sectionKey: "service", + icon: , + children: ( + + + + ), + })} + {(dbType === "oracle" || isOceanBaseOracle) && renderConfigSectionCard({ sectionKey: "service", @@ -5703,13 +5763,25 @@ const ConnectionModal: React.FC<{ children: (