mirror of
https://github.com/t8y2/dbx.git
synced 2026-11-02 08:30:35 +00:00
fix: add TLS connection settings
This commit is contained in:
@@ -38,7 +38,7 @@ type DbOption = { value: string; label: string };
|
||||
type DbCategory = { key: string; title: string; options: DbOption[] };
|
||||
type DialogStep = "select" | "config";
|
||||
type DbPickerView = "icon" | "list";
|
||||
type ConfigTab = "connection" | "ssh" | "proxy";
|
||||
type ConfigTab = "connection" | "tls" | "ssh" | "proxy";
|
||||
|
||||
const { t } = useI18n();
|
||||
const { toast } = useToast();
|
||||
@@ -550,6 +550,18 @@ const filePathPlaceholder = computed(() => {
|
||||
return "/path/to/database.db or :memory:";
|
||||
});
|
||||
const supportsMemoryDatabasePath = computed(() => form.value.db_type === "sqlite" || form.value.db_type === "duckdb");
|
||||
const tlsCapableDatabaseTypes = new Set<DatabaseType>([
|
||||
"mysql",
|
||||
"postgres",
|
||||
"redshift",
|
||||
"gaussdb",
|
||||
"opengauss",
|
||||
"redis",
|
||||
"clickhouse",
|
||||
"elasticsearch",
|
||||
]);
|
||||
const supportsTlsToggle = computed(() => tlsCapableDatabaseTypes.has(form.value.db_type));
|
||||
const supportsCaCertificatePath = computed(() => form.value.db_type === "mysql" || form.value.db_type === "clickhouse");
|
||||
const canUseSsh = computed(() => form.value.db_type !== "sqlite" && form.value.db_type !== "access");
|
||||
const canUseProxy = computed(
|
||||
() => form.value.db_type !== "sqlite" && form.value.db_type !== "duckdb" && form.value.db_type !== "access",
|
||||
@@ -821,6 +833,12 @@ watch(canUseProxy, (value) => {
|
||||
}
|
||||
});
|
||||
|
||||
watch(supportsTlsToggle, (value) => {
|
||||
if (!value && configTab.value === "tls") {
|
||||
configTab.value = "connection";
|
||||
}
|
||||
});
|
||||
|
||||
async function save() {
|
||||
if (!ensureConnectionHostResolvedFromUrl()) return;
|
||||
if (isSaving.value) return;
|
||||
@@ -1117,9 +1135,13 @@ function openExternalUrl(url: string) {
|
||||
<template v-else>
|
||||
<div class="space-y-3">
|
||||
<Tabs v-model="configTab" class="min-h-0">
|
||||
<div v-if="canUseSsh || canUseProxy" class="flex items-center justify-between border-b pb-2">
|
||||
<div
|
||||
v-if="supportsTlsToggle || canUseSsh || canUseProxy"
|
||||
class="flex items-center justify-between border-b pb-2"
|
||||
>
|
||||
<TabsList>
|
||||
<TabsTrigger value="connection">{{ t("connection.basicTab") }}</TabsTrigger>
|
||||
<TabsTrigger v-if="supportsTlsToggle" value="tls">{{ t("connection.tlsTab") }}</TabsTrigger>
|
||||
<TabsTrigger v-if="canUseSsh" value="ssh">{{ t("connection.sshTunnel") }}</TabsTrigger>
|
||||
<TabsTrigger v-if="canUseProxy" value="proxy">{{ t("connection.proxy") }}</TabsTrigger>
|
||||
</TabsList>
|
||||
@@ -1339,13 +1361,6 @@ function openExternalUrl(url: string) {
|
||||
:placeholder="t('connection.databasePlaceholder')"
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label class="text-right text-xs">SSL/TLS</Label>
|
||||
<div class="col-span-3">
|
||||
<input type="checkbox" v-model="form.ssl" class="mr-2" />
|
||||
<span class="text-xs text-muted-foreground">{{ t("connection.sshEnable") }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- MongoDB: URL or form -->
|
||||
@@ -1454,33 +1469,6 @@ function openExternalUrl(url: string) {
|
||||
<Input v-model="form.database" class="col-span-3" :placeholder="databasePlaceholder" />
|
||||
</div>
|
||||
|
||||
<div v-if="form.db_type === 'clickhouse'" class="grid grid-cols-4 items-center gap-4">
|
||||
<Label class="text-right text-xs">SSL/TLS</Label>
|
||||
<label class="col-span-3 flex items-center gap-2 cursor-pointer">
|
||||
<input type="checkbox" v-model="form.ssl" class="mr-0" />
|
||||
<span class="text-xs text-muted-foreground">{{ t("connection.sslEnable") }}</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div v-if="form.db_type === 'clickhouse'" class="grid grid-cols-4 items-center gap-4">
|
||||
<Label class="text-right text-xs">{{ t("connection.caCertPath") }}</Label>
|
||||
<div class="col-span-3 flex items-center gap-1">
|
||||
<Input
|
||||
v-model="form.ca_cert_path"
|
||||
class="flex-1"
|
||||
:placeholder="t('connection.caCertPathPlaceholder')"
|
||||
/>
|
||||
<Tooltip v-if="isDesktop">
|
||||
<TooltipTrigger as-child>
|
||||
<Button variant="outline" size="icon" class="h-9 w-9 shrink-0" @click="browseCaCertPath">
|
||||
<FolderOpen class="h-4 w-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{{ t("connection.caCertPathBrowse") }}</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="form.db_type === 'oracle'" class="grid grid-cols-4 items-center gap-4">
|
||||
<Label class="text-right text-xs">连接方式</Label>
|
||||
<div
|
||||
@@ -1590,6 +1578,44 @@ function openExternalUrl(url: string) {
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent v-if="supportsTlsToggle" value="tls" class="m-0">
|
||||
<div class="grid gap-4 py-4 pr-2 max-h-[65vh] overflow-y-auto">
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label class="text-right text-xs">SSL/TLS</Label>
|
||||
<label class="col-span-3 flex items-center gap-2 cursor-pointer">
|
||||
<input type="checkbox" v-model="form.ssl" class="mr-0" />
|
||||
<span class="text-xs text-muted-foreground">{{ t("connection.sslEnable") }}</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div v-if="supportsCaCertificatePath" class="grid grid-cols-4 items-center gap-4">
|
||||
<Label class="text-right text-xs">{{ t("connection.caCertPath") }}</Label>
|
||||
<div class="col-span-3 flex items-center gap-1">
|
||||
<Input
|
||||
v-model="form.ca_cert_path"
|
||||
class="flex-1"
|
||||
:placeholder="t('connection.caCertPathPlaceholder')"
|
||||
:disabled="!form.ssl"
|
||||
/>
|
||||
<Tooltip v-if="isDesktop">
|
||||
<TooltipTrigger as-child>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
class="h-9 w-9 shrink-0"
|
||||
:disabled="!form.ssl"
|
||||
@click="browseCaCertPath"
|
||||
>
|
||||
<FolderOpen class="h-4 w-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{{ t("connection.caCertPathBrowse") }}</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent v-if="canUseSsh" value="ssh" class="m-0">
|
||||
<div class="grid gap-4 py-4 pr-2 max-h-[65vh] overflow-y-auto">
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
|
||||
@@ -137,6 +137,7 @@ export default {
|
||||
next: "Next",
|
||||
back: "Previous",
|
||||
basicTab: "Connection",
|
||||
tlsTab: "TLS/SSL",
|
||||
test: "Test",
|
||||
testing: "Testing...",
|
||||
copyTestResult: "Copy test result",
|
||||
|
||||
@@ -127,6 +127,7 @@ export default {
|
||||
next: "Siguiente",
|
||||
back: "Anterior",
|
||||
basicTab: "Conexión",
|
||||
tlsTab: "TLS/SSL",
|
||||
test: "Probar",
|
||||
testing: "Probando...",
|
||||
copyTestResult: "Copiar resultado de prueba",
|
||||
|
||||
@@ -136,6 +136,7 @@ export default {
|
||||
next: "下一步",
|
||||
back: "上一步",
|
||||
basicTab: "连接信息",
|
||||
tlsTab: "TLS/SSL",
|
||||
test: "测试",
|
||||
testing: "测试中...",
|
||||
copyTestResult: "复制测试结果",
|
||||
|
||||
@@ -66,6 +66,35 @@ function databaseFromPath(pathname: string): string | undefined {
|
||||
return decodeUrlPart(value.split("/")[0]);
|
||||
}
|
||||
|
||||
function queryParamValue(params: string, key: string): string | undefined {
|
||||
for (const part of params.split(/[&;]/)) {
|
||||
if (!part) continue;
|
||||
const [rawKey, ...rest] = part.split("=");
|
||||
if (decodeUrlPart(rawKey).toLowerCase() === key.toLowerCase()) {
|
||||
return decodeUrlPart(rest.join("=")).trim();
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function urlParamsRequireTls(dbType: DatabaseType, params: string): boolean {
|
||||
if (dbType === "mysql") {
|
||||
const requireSsl = queryParamValue(params, "require_ssl")?.toLowerCase();
|
||||
if (requireSsl === "true" || requireSsl === "1" || requireSsl === "yes") return true;
|
||||
const sslMode = (queryParamValue(params, "ssl-mode") || queryParamValue(params, "sslmode") || "")
|
||||
.toLowerCase()
|
||||
.replace("-", "_");
|
||||
return sslMode === "required" || sslMode === "require" || sslMode === "verify_ca" || sslMode === "verify_identity";
|
||||
}
|
||||
|
||||
if (dbType === "postgres" || dbType === "redshift") {
|
||||
const sslMode = (queryParamValue(params, "sslmode") || "").toLowerCase();
|
||||
return sslMode === "require" || sslMode === "verify-ca" || sslMode === "verify-full";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function connectionProfileForScheme(scheme: string, preferredProfile?: string): ConnectionProfile | undefined {
|
||||
if ((scheme === "http" || scheme === "https") && preferredProfile) {
|
||||
return HTTP_SELECTED_PROFILES[preferredProfile];
|
||||
@@ -226,7 +255,7 @@ export function parseConnectionUrl(value: string, preferredProfile?: string): Pa
|
||||
password: decodeUrlPart(parsed.password),
|
||||
database: databaseFromPath(parsed.pathname),
|
||||
urlParams,
|
||||
ssl: scheme === "rediss" || scheme === "https",
|
||||
ssl: scheme === "rediss" || scheme === "https" || urlParamsRequireTls(profile.type, urlParams),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user