feat(cassandra): add TLS store support

This commit is contained in:
以谶 authored and GitHub committed 2026-09-10 00:28:58 +08:00
1 parent e2240d4ed1
commit 38be5ba35c
26 files changed
+1203 -36

No files matched your search

@@ -62,6 +62,7 @@ import { SQLITE_DATABASE_FILE_EXTENSIONS } from "@/lib/database/databaseFileDete
import { connectionAttemptOriginalErrorMessage, connectionAttemptTimeoutMessage, connectionAttemptTimeoutMs } from "@/lib/connection/connectionAttemptTimeout";
import { consulAgentAddressesMatch } from "@/lib/consul/agentTarget";
import { appendConnectionErrorHints, isJdbcMissingRuntimeDependencyError } from "@/lib/connection/connectionErrorHints";
import { buildCassandraExternalConfig, cassandraTlsConfigFromExternalConfig, type CassandraTlsConfig } from "@/lib/connection/cassandraTlsOptions";
import { preventDialogDocumentSelectAll } from "@/lib/connection/dialogTextSelection";
import { postgresLegacyTlsEnabled, postgresTlsModeForForm, setPostgresLegacyTlsEnabled } from "@/lib/connection/postgresTlsMode";
import { buildMqKafkaConnectionExtra, mqKafkaConnectionTarget, resolveMqKafkaConnectionSource, type MqKafkaConnectionSource } from "@/lib/connection/mqKafkaConnection";
@@ -396,6 +397,12 @@ const defaultForm = (): ConnectionForm => ({
save_password: true,
});
const cassandraTls = reactive<CassandraTlsConfig>(cassandraTlsConfigFromExternalConfig(undefined));
function resetCassandraTlsFields(externalConfig: unknown) {
Object.assign(cassandraTls, cassandraTlsConfigFromExternalConfig(externalConfig));
}
const elasticsearchConnectionMode = ref<ElasticsearchConnectionMode>("direct");
const elasticsearchKibanaBasePath = ref("");
const elasticsearchConnectivityCheckPath = ref("");
@@ -2333,6 +2340,9 @@ function applyProfile(val: string, preserveConnectionFields = false) {
if (profile.type !== "elasticsearch" || previousDatabaseType !== "elasticsearch") {
resetElasticsearchProxyFields();
}
if (profile.type !== "cassandra" || previousDatabaseType !== "cassandra") {
resetCassandraTlsFields(undefined);
}
if (!preserveConnectionFields) {
oracleTnsAdminPath.value = "";
form.value.port = profile.port;
@@ -2572,6 +2582,7 @@ watch(
} else {
resetMqFields();
}
resetCassandraTlsFields(config.db_type === "cassandra" ? config.external_config : undefined);
if (config.db_type === "nacos") {
hydrateNacosFields(config.external_config);
} else {
@@ -2643,6 +2654,7 @@ watch(
selectedType.value = "mysql";
customDriverName.value = "";
resetMqFields();
resetCassandraTlsFields(undefined);
resetNacosFields();
resetInfluxDbFields();
resetElasticsearchProxyFields();
@@ -3012,6 +3024,7 @@ const tlsCapableDatabaseTypes = new Set<DatabaseType>([
"chromadb",
"influxdb",
"victoriametrics",
"cassandra",
]);
const supportsTlsToggle = computed(() => tlsCapableDatabaseTypes.has(form.value.db_type));
const supportsCaCertificatePath = computed(() => form.value.db_type === "clickhouse" || form.value.db_type === "victoriametrics");
@@ -3922,6 +3935,20 @@ function connectionConfigForSubmit(id: string, generatedName = ""): ConnectionCo
config.database = undefined;
config.connection_string = undefined;
config.url_params = "";
} else if (config.db_type === "cassandra") {
// Go 侧在配置了 truststore/keystore 时会自动启用 TLS,前端保持一致:有 store 配置就不丢弃 external_config。
const cassandraHasTlsStore = Boolean(cassandraTls.truststore_path.trim() || cassandraTls.truststore_password) || Boolean(cassandraTls.keystore_path.trim() || cassandraTls.keystore_password);
if (!config.ssl && !cassandraHasTlsStore) {
config.external_config = undefined;
} else {
if (cassandraTls.truststore_password && !cassandraTls.truststore_path.trim()) {
throw new Error(t("connection.cassandraTruststorePasswordRequiresPath"));
}
if (cassandraTls.keystore_password && !cassandraTls.keystore_path.trim()) {
throw new Error(t("connection.cassandraKeystorePasswordRequiresPath"));
}
config.external_config = buildCassandraExternalConfig(cassandraTls);
}
} else if (config.db_type === "nacos") {
const nacosConfig = buildNacosAdminConfig();
config.external_config = nacosConfig;
@@ -5476,6 +5503,23 @@ async function browseCaCertPath() {
}
}
async function browseCassandraStore(target: "truststore" | "keystore") {
if (!isTauriRuntime()) return;
const { open } = await import("@tauri-apps/plugin-dialog");
const selected = await open({
title: target === "truststore" ? t("connection.cassandraTruststoreBrowse") : t("connection.cassandraKeystoreBrowse"),
multiple: false,
filters: [
{ name: "Java KeyStore / PKCS#12", extensions: ["jks", "p12", "pfx", "truststore", "keystore"] },
{ name: "All Files", extensions: ["*"] },
],
});
if (typeof selected === "string") {
if (target === "truststore") cassandraTls.truststore_path = selected;
else cassandraTls.keystore_path = selected;
}
}
async function browseDamengSslFilesPath() {
if (isTauriRuntime()) {
const { open } = await import("@tauri-apps/plugin-dialog");
@@ -7913,6 +7957,56 @@ function openExternalUrl(url: string) {
</div>
</template>
<template v-if="form.db_type === 'cassandra'">
<div class="grid grid-cols-4 items-start gap-4">
<Label :class="connectionLabelSmallPaddedClass">
<span class="inline-flex items-center justify-end gap-1">
<ShieldCheck class="h-3.5 w-3.5" />
{{ t("connection.cassandraTruststore") }}
</span>
</Label>
<div class="col-span-3 grid gap-2">
<div class="flex items-center gap-1">
<Input v-model="cassandraTls.truststore_path" class="flex-1" :placeholder="t('connection.cassandraTruststorePlaceholder')" :disabled="!tlsEnabled" />
<Tooltip v-if="isDesktop">
<TooltipTrigger as-child>
<Button variant="outline" size="icon" class="h-9 w-9 shrink-0" :disabled="!tlsEnabled" @click="browseCassandraStore('truststore')">
<FolderOpen class="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent>{{ t("connection.cassandraTruststoreBrowse") }}</TooltipContent>
</Tooltip>
</div>
<PasswordInput v-model="cassandraTls.truststore_password" :placeholder="t('connection.cassandraTruststorePassword')" :disabled="!tlsEnabled" />
<p class="text-[11px] leading-4 text-muted-foreground">{{ t("connection.cassandraTruststoreHint") }}</p>
</div>
</div>
<div class="grid grid-cols-4 items-start gap-4">
<Label :class="connectionLabelSmallPaddedClass">
<span class="inline-flex items-center justify-end gap-1">
<KeyRound class="h-3.5 w-3.5" />
{{ t("connection.cassandraKeystore") }}
</span>
</Label>
<div class="col-span-3 grid gap-2">
<div class="flex items-center gap-1">
<Input v-model="cassandraTls.keystore_path" class="flex-1" :placeholder="t('connection.cassandraKeystorePlaceholder')" :disabled="!tlsEnabled" />
<Tooltip v-if="isDesktop">
<TooltipTrigger as-child>
<Button variant="outline" size="icon" class="h-9 w-9 shrink-0" :disabled="!tlsEnabled" @click="browseCassandraStore('keystore')">
<FolderOpen class="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent>{{ t("connection.cassandraKeystoreBrowse") }}</TooltipContent>
</Tooltip>
</div>
<PasswordInput v-model="cassandraTls.keystore_password" :placeholder="t('connection.cassandraKeystorePassword')" :disabled="!tlsEnabled" />
<p class="text-[11px] leading-4 text-muted-foreground">{{ t("connection.cassandraKeystoreHint") }}</p>
</div>
</div>
</template>
<div v-if="form.db_type === 'redis'" class="grid grid-cols-4 items-start gap-4">
<Label :class="connectionLabelSmallClass">{{ t("connection.redisTlsInsecure") }}</Label>
<label class="col-span-3 flex items-start gap-2 cursor-pointer">