feat(elasticsearch): allow disabling connectivity checks

This commit is contained in:
JcEvoX authored and GitHub committed 2026-08-13 20:11:52 +08:00
1 parent be3336c1ec
commit cce36ae058
12 files changed
+202 -10

No files matched your search

@@ -108,7 +108,15 @@ import { oceanbaseModeConnectionPatch, oceanbaseSubModeFromConfig } from "@/lib/
import { translateBackendError } from "@/i18n/backend-errors";
import { applyHiveKerberosSubmitConfig, hiveKerberosFormConfig, type HiveKerberosAuthMode } from "@/lib/database/hiveKerberosOptions";
import { hasCloudflareD1Credentials, isCloudflareD1Connection, normalizeCloudflareD1Connection } from "@/lib/connection/cloudflareD1";
import { buildElasticsearchExternalConfig, elasticsearchConnectionModeFromConfig, elasticsearchConnectivityCheckPathFromConfig, elasticsearchIndexGroupingPatternFromConfig, elasticsearchKibanaBasePathFromConfig, type ElasticsearchConnectionMode } from "@/lib/connection/elasticsearchKibanaProxy";
import {
buildElasticsearchExternalConfig,
elasticsearchConnectionModeFromConfig,
elasticsearchConnectivityCheckDisabledFromConfig,
elasticsearchConnectivityCheckPathFromConfig,
elasticsearchIndexGroupingPatternFromConfig,
elasticsearchKibanaBasePathFromConfig,
type ElasticsearchConnectionMode,
} from "@/lib/connection/elasticsearchKibanaProxy";
import { GAUSSDB_M_JDBC_DRIVER_CLASS, gaussdbConnectionMode, gaussdbIdentifierQuoteStyle, setGaussdbConnectionMode, setGaussdbIdentifierQuoteStyle, supportsGaussdbIdentifierQuoteStyle, type GaussdbConnectionMode, type GaussdbIdentifierQuoteStyle } from "@/lib/database/jdbcDialect";
import { normalizeStoredConnectionDatabase } from "@/lib/database/sqliteNamespace";
import {
@@ -327,6 +335,7 @@ const defaultForm = (): ConnectionForm => ({
const elasticsearchConnectionMode = ref<ElasticsearchConnectionMode>("direct");
const elasticsearchKibanaBasePath = ref("");
const elasticsearchConnectivityCheckPath = ref("");
const elasticsearchConnectivityCheckDisabled = ref(false);
const elasticsearchIndexGroupingPattern = ref("");
const elasticsearchConnectionPorts = ref<Record<ElasticsearchConnectionMode, number>>({
direct: 9200,
@@ -338,6 +347,7 @@ function resetElasticsearchProxyFields(externalConfig?: unknown) {
elasticsearchConnectionMode.value = mode;
elasticsearchKibanaBasePath.value = elasticsearchKibanaBasePathFromConfig(externalConfig);
elasticsearchConnectivityCheckPath.value = elasticsearchConnectivityCheckPathFromConfig(externalConfig);
elasticsearchConnectivityCheckDisabled.value = elasticsearchConnectivityCheckDisabledFromConfig(externalConfig);
elasticsearchIndexGroupingPattern.value = elasticsearchIndexGroupingPatternFromConfig(externalConfig);
elasticsearchConnectionPorts.value = {
direct: mode === "direct" ? form.value.port : 9200,
@@ -3837,7 +3847,7 @@ function connectionConfigForSubmit(id: string, generatedName = ""): ConnectionCo
config.database = "metrics";
config.username = config.username.trim();
} else if (config.db_type === "elasticsearch") {
config.external_config = buildElasticsearchExternalConfig(elasticsearchConnectionMode.value, elasticsearchKibanaBasePath.value, elasticsearchConnectivityCheckPath.value, elasticsearchIndexGroupingPattern.value);
config.external_config = buildElasticsearchExternalConfig(elasticsearchConnectionMode.value, elasticsearchKibanaBasePath.value, elasticsearchConnectivityCheckPath.value, elasticsearchIndexGroupingPattern.value, elasticsearchConnectivityCheckDisabled.value);
} else if (config.db_type === "meilisearch") {
config.username = "";
config.password = config.password.trim();
@@ -6917,11 +6927,18 @@ function openExternalUrl(url: string) {
</div>
<div v-if="form.db_type === 'elasticsearch'" class="grid grid-cols-4 items-center gap-4">
<Label :class="connectionLabelSmallClass">{{ t("connection.elasticsearchIndexGroupingPattern") }}</Label>
<div class="col-span-3 space-y-1">
<Input v-model="elasticsearchIndexGroupingPattern" :placeholder="t('connection.elasticsearchIndexGroupingPatternPlaceholder')" @input="resetTestState" />
<p class="text-xs text-muted-foreground">{{ t("connection.elasticsearchIndexGroupingPatternHint") }}</p>
<div class="flex items-center gap-1">
<Label :class="connectionLabelSmallClass">{{ t("connection.elasticsearchIndexGroupingPattern") }}</Label>
<Tooltip>
<TooltipTrigger as-child>
<CircleHelp class="h-3.5 w-3.5 cursor-help text-muted-foreground hover:text-foreground" />
</TooltipTrigger>
<TooltipContent side="top" align="center" class="max-w-[280px] text-xs leading-relaxed">
{{ t("connection.elasticsearchIndexGroupingPatternHint") }}
</TooltipContent>
</Tooltip>
</div>
<Input v-model="elasticsearchIndexGroupingPattern" class="col-span-3" :placeholder="t('connection.elasticsearchIndexGroupingPatternPlaceholder')" @input="resetTestState" />
</div>
<div v-if="form.driver_profile === 'gbase8s'" class="grid grid-cols-4 items-center gap-4">
@@ -7582,6 +7599,23 @@ function openExternalUrl(url: string) {
<TabsContent value="advanced" class="m-0 flex min-h-0 flex-1 flex-col overflow-hidden">
<div class="connection-form-body grid min-h-0 flex-1 scroll-pb-6 gap-4 overflow-y-auto pt-4 pr-2 pb-6">
<div v-if="form.db_type === 'elasticsearch'" class="grid grid-cols-4 items-center gap-4">
<div class="flex items-center gap-1">
<Label :class="connectionLabelSmallClass">{{ t("connection.elasticsearchConnectivityCheckDisabled") }}</Label>
<Tooltip>
<TooltipTrigger as-child>
<CircleHelp class="h-3.5 w-3.5 cursor-help text-muted-foreground hover:text-foreground" />
</TooltipTrigger>
<TooltipContent side="top" align="center" class="max-w-[280px] text-xs leading-relaxed">
{{ t("connection.elasticsearchConnectivityCheckDisabledHint") }}
</TooltipContent>
</Tooltip>
</div>
<div class="col-span-3">
<Switch v-model="elasticsearchConnectivityCheckDisabled" @update:model-value="resetTestState" />
</div>
</div>
<section v-if="form.db_type === 'nacos'" data-nacos-advanced-settings class="overflow-hidden rounded-lg border">
<div class="border-b bg-muted/20 px-4 py-3">
<div class="text-sm font-medium">{{ t("nacos.nacosAdvancedTitle") }}</div>
+2
View File
@@ -477,6 +477,8 @@ export default {
elasticsearchKibanaBasePath: "Base Path",
elasticsearchConnectivityCheckPath: "Connectivity Path",
elasticsearchConnectivityCheckPathPlaceholder: "/ or /my-index/_search",
elasticsearchConnectivityCheckDisabled: "Disable connectivity check",
elasticsearchConnectivityCheckDisabledHint: "When on, connect and test skip the probe request and treat the server as reachable. Use it when the account lacks permission for the root path and any check path, or when permissions differ across clusters.",
elasticsearchIndexGroupingPattern: "Index grouping regex",
elasticsearchIndexGroupingPatternPlaceholder: "off by default; enter a regex to enable",
elasticsearchIndexGroupingPatternHint: "Off by default (naming schemes vary). Enter a regular expression to enable: matched rolling or time-series indices are aggregated into one queryable wildcard pattern, hiding per-day detail.",
+2
View File
@@ -986,6 +986,8 @@ export default withEnglishFallback({
elasticsearchKibanaBasePath: "ruta base",
elasticsearchConnectivityCheckPath: "Ruta de conectividad",
elasticsearchConnectivityCheckPathPlaceholder: "/ o /my-index/_search",
elasticsearchConnectivityCheckDisabled: "Disable connectivity check",
elasticsearchConnectivityCheckDisabledHint: "When on, connect and test skip the probe request and treat the server as reachable. Use it when the account lacks permission for the root path and any check path, or when permissions differ across clusters.",
elasticsearchIndexGroupingPattern: "Index grouping regex",
elasticsearchIndexGroupingPatternPlaceholder: "off by default; enter a regex to enable",
elasticsearchIndexGroupingPatternHint: "Off by default (naming schemes vary). Enter a regular expression to enable: matched rolling or time-series indices are aggregated into one queryable wildcard pattern, hiding per-day detail.",
+2
View File
@@ -984,6 +984,8 @@ export default withEnglishFallback({
elasticsearchKibanaBasePath: "Percorso base",
elasticsearchConnectivityCheckPath: "Percorso di connettività",
elasticsearchConnectivityCheckPathPlaceholder: "/ o /my-index/_search",
elasticsearchConnectivityCheckDisabled: "Disable connectivity check",
elasticsearchConnectivityCheckDisabledHint: "When on, connect and test skip the probe request and treat the server as reachable. Use it when the account lacks permission for the root path and any check path, or when permissions differ across clusters.",
elasticsearchIndexGroupingPattern: "Index grouping regex",
elasticsearchIndexGroupingPatternPlaceholder: "off by default; enter a regex to enable",
elasticsearchIndexGroupingPatternHint: "Off by default (naming schemes vary). Enter a regular expression to enable: matched rolling or time-series indices are aggregated into one queryable wildcard pattern, hiding per-day detail.",
+2
View File
@@ -984,6 +984,8 @@ export default withEnglishFallback({
elasticsearchKibanaBasePath: "ベースパス",
elasticsearchConnectivityCheckPath: "接続確認パス",
elasticsearchConnectivityCheckPathPlaceholder: "/ または /my-index/_search",
elasticsearchConnectivityCheckDisabled: "Disable connectivity check",
elasticsearchConnectivityCheckDisabledHint: "When on, connect and test skip the probe request and treat the server as reachable. Use it when the account lacks permission for the root path and any check path, or when permissions differ across clusters.",
elasticsearchIndexGroupingPattern: "Index grouping regex",
elasticsearchIndexGroupingPatternPlaceholder: "off by default; enter a regex to enable",
elasticsearchIndexGroupingPatternHint: "Off by default (naming schemes vary). Enter a regular expression to enable: matched rolling or time-series indices are aggregated into one queryable wildcard pattern, hiding per-day detail.",
+2
View File
@@ -471,6 +471,8 @@ export default withEnglishFallback({
elasticsearchKibanaBasePath: "기본 경로",
elasticsearchConnectivityCheckPath: "연결 확인 경로",
elasticsearchConnectivityCheckPathPlaceholder: "/ 또는 /my-index/_search",
elasticsearchConnectivityCheckDisabled: "Disable connectivity check",
elasticsearchConnectivityCheckDisabledHint: "When on, connect and test skip the probe request and treat the server as reachable. Use it when the account lacks permission for the root path and any check path, or when permissions differ across clusters.",
elasticsearchIndexGroupingPattern: "Index grouping regex",
elasticsearchIndexGroupingPatternPlaceholder: "off by default; enter a regex to enable",
elasticsearchIndexGroupingPatternHint: "Off by default (naming schemes vary). Enter a regular expression to enable: matched rolling or time-series indices are aggregated into one queryable wildcard pattern, hiding per-day detail.",
+2
View File
@@ -985,6 +985,8 @@ export default withEnglishFallback({
elasticsearchKibanaBasePath: "Caminho Base",
elasticsearchConnectivityCheckPath: "Caminho de conectividade",
elasticsearchConnectivityCheckPathPlaceholder: "/ ou /my-index/_search",
elasticsearchConnectivityCheckDisabled: "Disable connectivity check",
elasticsearchConnectivityCheckDisabledHint: "When on, connect and test skip the probe request and treat the server as reachable. Use it when the account lacks permission for the root path and any check path, or when permissions differ across clusters.",
elasticsearchIndexGroupingPattern: "Index grouping regex",
elasticsearchIndexGroupingPatternPlaceholder: "off by default; enter a regex to enable",
elasticsearchIndexGroupingPatternHint: "Off by default (naming schemes vary). Enter a regular expression to enable: matched rolling or time-series indices are aggregated into one queryable wildcard pattern, hiding per-day detail.",
+2
View File
@@ -402,6 +402,8 @@ export default withEnglishFallback({
elasticsearchKibanaBasePath: "基础路径",
elasticsearchConnectivityCheckPath: "连通性检查路径",
elasticsearchConnectivityCheckPathPlaceholder: "/ 或 /my-index/_search",
elasticsearchConnectivityCheckDisabled: "关闭连通性检查",
elasticsearchConnectivityCheckDisabledHint: "开启后连接与测试都不发探活请求,直接视为可连。用于账号对根路径和任何检查路径都无权限、或集群间权限不统一的情况。",
elasticsearchIndexGroupingPattern: "索引聚合正则",
elasticsearchIndexGroupingPatternPlaceholder: "默认关闭,填正则表达式开启",
elasticsearchIndexGroupingPatternHint: "默认关闭(命名格式因环境而异)。填入正则表达式即可开启:将匹配到的滚动或时间序列索引聚合成一个可查询的通配 pattern,隐藏每日明细。",
+2
View File
@@ -984,6 +984,8 @@ export default withEnglishFallback({
elasticsearchKibanaBasePath: "基礎路徑",
elasticsearchConnectivityCheckPath: "連通性檢查路徑",
elasticsearchConnectivityCheckPathPlaceholder: "/ 或 /my-index/_search",
elasticsearchConnectivityCheckDisabled: "關閉連通性檢查",
elasticsearchConnectivityCheckDisabledHint: "開啟後連線與測試都不發探活請求,直接視為可連。用於帳號對根路徑和任何檢查路徑都無權限、或叢集間權限不統一的情況。",
elasticsearchIndexGroupingPattern: "索引聚合正則",
elasticsearchIndexGroupingPatternPlaceholder: "預設關閉,填正則表達式開啟",
elasticsearchIndexGroupingPatternHint: "預設關閉(命名格式因環境而異)。填入正則表達式即可開啟:將比對到的滾動或時間序列索引聚合成一個可查詢的通配 pattern,隱藏每日明細。",
@@ -0,0 +1,20 @@
import { describe, expect, it } from "vitest";
import { buildElasticsearchExternalConfig, elasticsearchConnectivityCheckDisabledFromConfig } from "@/lib/connection/elasticsearchKibanaProxy";
describe("Elasticsearch connectivity-check configuration", () => {
it.each([true, "true", "TRUE", "1", "yes", "YES", "on", "ON"])("reads enabled value %j", (value) => {
expect(elasticsearchConnectivityCheckDisabledFromConfig({ connectivityCheckDisabled: value })).toBe(true);
});
it.each([false, "false", "0", "no", "off", "", 1, null])("rejects disabled value %j", (value) => {
expect(elasticsearchConnectivityCheckDisabledFromConfig({ connectivityCheckDisabled: value })).toBe(false);
});
it("normalizes a legacy string flag when an edited connection is saved", () => {
const disabled = elasticsearchConnectivityCheckDisabledFromConfig({ connectivityCheckDisabled: " yes " });
const saved = buildElasticsearchExternalConfig("direct", "", "", "", disabled);
expect(saved).toEqual({ connectivityCheckDisabled: true });
expect(elasticsearchConnectivityCheckDisabledFromConfig(saved)).toBe(true);
});
});
@@ -5,6 +5,8 @@ export interface ElasticsearchExternalConfig {
kibanaBasePath?: string;
/** GET path for connect/test/health. Empty means GET /. */
connectivityCheckPath?: string;
/** When true, skip the connectivity check entirely (test/open never probes). */
connectivityCheckDisabled?: boolean;
/** Regex collapsing rolling/time-series index suffixes into a `*` pattern. Empty → default; "off" → no grouping. */
indexGroupingPattern?: string;
}
@@ -46,19 +48,28 @@ export function elasticsearchConnectivityCheckPathFromConfig(value: unknown): st
return typeof path === "string" ? normalizeElasticsearchConnectivityCheckPath(path) : "";
}
export function elasticsearchConnectivityCheckDisabledFromConfig(value: unknown): boolean {
const disabled = externalConfigRecord(value).connectivityCheckDisabled;
if (disabled === true) return true;
if (typeof disabled !== "string") return false;
const normalized = disabled.trim().toLowerCase();
return normalized === "true" || normalized === "1" || normalized === "yes" || normalized === "on";
}
export function elasticsearchIndexGroupingPatternFromConfig(value: unknown): string {
const config = externalConfigRecord(value);
const pattern = config.indexGroupingPattern;
return typeof pattern === "string" ? pattern.trim() : "";
}
export function buildElasticsearchExternalConfig(mode: ElasticsearchConnectionMode, kibanaBasePath: string, connectivityCheckPath = "", indexGroupingPattern = ""): ElasticsearchExternalConfig | undefined {
export function buildElasticsearchExternalConfig(mode: ElasticsearchConnectionMode, kibanaBasePath: string, connectivityCheckPath = "", indexGroupingPattern = "", connectivityCheckDisabled = false): ElasticsearchExternalConfig | undefined {
const checkPath = normalizeElasticsearchConnectivityCheckPath(connectivityCheckPath);
const grouping = indexGroupingPattern.trim();
if (mode !== "kibana") {
if (!checkPath && !grouping) return undefined;
if (!checkPath && !grouping && !connectivityCheckDisabled) return undefined;
const config: ElasticsearchExternalConfig = {};
if (checkPath) config.connectivityCheckPath = checkPath;
if (connectivityCheckDisabled) config.connectivityCheckDisabled = true;
if (grouping) config.indexGroupingPattern = grouping;
return config;
}
@@ -66,6 +77,7 @@ export function buildElasticsearchExternalConfig(mode: ElasticsearchConnectionMo
const config: ElasticsearchExternalConfig = { mode: "kibana" };
if (normalizedPath) config.kibanaBasePath = normalizedPath;
if (checkPath) config.connectivityCheckPath = checkPath;
if (connectivityCheckDisabled) config.connectivityCheckDisabled = true;
if (grouping) config.indexGroupingPattern = grouping;
return config;
}