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: (