feat(sqlserver): support legacy TLS compatibility

This commit is contained in:
zipg authored and GitHub committed 2026-07-09 22:45:54 +08:00
1 parent 63ef26b25a
commit 67edb94b1d
30 files changed
+738 -117

No files matched your search

@@ -42,6 +42,7 @@ import { appendConnectionErrorHints } from "@/lib/connection/connectionErrorHint
import { normalizeKafkaBootstrapServers } from "@/lib/connection/kafkaBootstrapServers";
import { detectMqUiAuthKind, isMqAuthKindAllowedForSystem, type MqUiAuthKind } from "@/lib/connection/mqAuth";
import { driverInstallProgressPercent, type DriverInstallProgress } from "@/lib/connection/driverInstallProgressUi";
import { isSqlServerLegacyCompatibilityMode, requiresSqlServerLegacyCompatibilityComponent, setSqlServerLegacyCompatibilityMode, SQLSERVER_LEGACY_COMPATIBILITY_DRIVER_KEY } from "@/lib/connection/sqlServerLegacyCompatibility";
import { ArrowLeft, ArrowDown, ArrowUp, CheckSquare, ChevronRight, CircleHelp, Copy, ExternalLink, FilePlus2, FolderOpen, GripVertical, Grid3X3, KeyRound, Link2, List, ListFilter, Loader2, Pencil, Pipette, Plus, Search, ShieldCheck, Square, Trash2 } from "@lucide/vue";
import { buildDraftVisibleDatabasesConnectionId, connectionCanChooseVisibleDatabases, initialVisibleDatabaseSelection, visibleDatabaseSelectionIsStale } from "@/lib/connection/connectionVisibleDatabases";
import { canSaveVisibleDatabaseSelection, connectionUsesVisibleSchemaFilter, filterDatabaseNamesForVisiblePicker, isSystemDatabaseName, normalizeVisibleDatabaseSelection, buildDraftVisibleSchemasConnectionId, normalizeVisibleSchemaSelection } from "@/lib/database/visibleDatabases";
@@ -1070,6 +1071,10 @@ function formatInstallSize(bytes: number): string {
}
async function ensureRequiredAgentDriverInstalled(config: ConnectionConfig): Promise<void> {
if (requiresSqlServerLegacyCompatibilityComponent(config)) {
await installSqlServerLegacyCompatibilityComponentIfNeeded();
}
const driverKey = agentDriverInstallKey(config.db_type, config.driver_profile);
if (!driverKey) return;
@@ -1094,29 +1099,42 @@ async function ensureRequiredAgentDriverInstalled(config: ConnectionConfig): Pro
}
}
function isSqlServerLegacyUnencryptedMode(params: string | undefined): boolean {
const normalized = (params || "").trim().replace(/^\?/, "").replace(/;/g, "&");
if (!normalized) return false;
const parsed = new URLSearchParams(normalized);
for (const [key, value] of parsed.entries()) {
const normalizedKey = key.trim().toLowerCase();
if (normalizedKey === "sqlserverencryption" || normalizedKey === "encrypt") {
// Accept JDBC-style `encrypt=false` from imported SQL Server URLs as the same opt-in.
if (["disabled", "disable", "false", "0", "off", "no"].includes(value.trim().toLowerCase())) return true;
}
async function installSqlServerLegacyCompatibilityComponentIfNeeded(): Promise<boolean> {
if (await api.isAgentInstalled(SQLSERVER_LEGACY_COMPATIBILITY_DRIVER_KEY)) return true;
const label = t("connection.sqlServerLegacyCompatibilityComponent");
testResult.value = { ok: true, message: t("connection.sqlServerLegacyCompatibilityComponentInstalling") };
beginAgentDriverInstall(SQLSERVER_LEGACY_COMPATIBILITY_DRIVER_KEY, label);
try {
await api.installAgent(SQLSERVER_LEGACY_COMPATIBILITY_DRIVER_KEY);
await refreshLocalAgentDrivers();
finishAgentDriverInstall();
} catch (error) {
testResult.value = { ok: false, message: errorMessage(error) };
failAgentDriverInstall(error);
throw error;
}
return false;
return true;
}
function setSqlServerLegacyUnencryptedMode(params: string | undefined, enabled: boolean): string {
const normalized = (params || "").trim().replace(/^\?/, "").replace(/;/g, "&");
const parsed = new URLSearchParams(normalized);
for (const key of Array.from(parsed.keys())) {
const normalizedKey = key.trim().toLowerCase();
if (normalizedKey === "sqlserverencryption" || normalizedKey === "encrypt") parsed.delete(key);
async function onSqlServerLegacyCompatibilityModeChange(event: Event) {
if (form.value.db_type !== "sqlserver") return;
const input = event.target instanceof HTMLInputElement ? event.target : null;
const enabled = input?.checked === true;
if (!enabled) {
form.value.url_params = setSqlServerLegacyCompatibilityMode(form.value.url_params, false);
return;
}
if (input) input.checked = false;
try {
await installSqlServerLegacyCompatibilityComponentIfNeeded();
form.value.url_params = setSqlServerLegacyCompatibilityMode(form.value.url_params, true);
testResult.value = { ok: true, message: t("connection.sqlServerLegacyCompatibilityModeEnabled") };
} catch {
form.value.url_params = setSqlServerLegacyCompatibilityMode(form.value.url_params, false);
if (input) input.checked = false;
}
if (enabled) parsed.set("sqlserverEncryption", "disabled");
return parsed.toString();
}
function isSqlServerTlsHandshakeFailure(message: string): boolean {
@@ -1917,13 +1935,7 @@ const agentInstallProgressLabel = computed(() => {
return `${label} ${formatInstallSize(progress.downloaded ?? 0)} / ${formatInstallSize(progress.total)} (${agentInstallPercent.value ?? 0}%)`;
});
const canCloseAgentInstallDialog = computed(() => !agentInstallRunning.value || !!agentInstallError.value);
const sqlServerLegacyUnencryptedModeEnabled = computed({
get: () => form.value.db_type === "sqlserver" && isSqlServerLegacyUnencryptedMode(form.value.url_params),
set: (enabled: boolean) => {
if (form.value.db_type !== "sqlserver") return;
form.value.url_params = setSqlServerLegacyUnencryptedMode(form.value.url_params, enabled);
},
});
const sqlServerLegacyCompatibilityModeEnabled = computed(() => form.value.db_type === "sqlserver" && isSqlServerLegacyCompatibilityMode(form.value.url_params));
const shouldUseWideConnectionDialog = computed(() => dialogStep.value === "config" && (canChooseVisibleDatabases.value || (canChooseVisibleSchemas.value && !visibleFilterUsesSchemas.value)));
const connectionDialogContentClass = computed(() => {
if (dialogStep.value === "select") return "sm:max-w-[760px]";
@@ -2019,7 +2031,7 @@ async function testConnection() {
const message = config ? connectionErrorWithDriverUpdateHint(config, rawMessage) : rawMessage;
const fallbackMessage = config ? await tryNacosDockerConsoleFallback(config, message, runId) : null;
if (runId !== testRunId) return;
const shouldShowSqlServerLegacyMode = !fallbackMessage && config?.db_type === "sqlserver" && !isSqlServerLegacyUnencryptedMode(config.url_params) && isSqlServerTlsHandshakeFailure(message);
const shouldShowSqlServerLegacyMode = !fallbackMessage && config?.db_type === "sqlserver" && !isSqlServerLegacyCompatibilityMode(config.url_params) && isSqlServerTlsHandshakeFailure(message);
if (shouldShowSqlServerLegacyMode) {
configTab.value = "advanced";
}
@@ -5027,14 +5039,14 @@ function openExternalUrl(url: string) {
</label>
</div>
<div v-show="form.db_type === 'sqlserver'" class="grid grid-cols-4 items-start gap-4">
<Label :class="connectionLabelSmallClass">{{ t("connection.sqlServerLegacyUnencryptedMode") }}</Label>
<Label :class="connectionLabelSmallClass">{{ t("connection.sqlServerLegacyCompatibilityMode") }}</Label>
<div class="col-span-3 flex flex-col gap-1">
<label class="flex h-5 cursor-pointer items-center gap-2">
<input type="checkbox" v-model="sqlServerLegacyUnencryptedModeEnabled" class="mr-0" />
<span class="text-xs text-foreground">{{ t("connection.sqlServerLegacyUnencryptedModeEnable") }}</span>
<input type="checkbox" :checked="sqlServerLegacyCompatibilityModeEnabled" :disabled="agentInstallRunning" class="mr-0" @change="onSqlServerLegacyCompatibilityModeChange" />
<span class="text-xs text-foreground">{{ t("connection.sqlServerLegacyCompatibilityModeEnable") }}</span>
</label>
<p class="m-0 whitespace-pre-line text-xs leading-5 text-muted-foreground">
{{ t("connection.sqlServerLegacyUnencryptedModeHint") }}
{{ t("connection.sqlServerLegacyCompatibilityModeHint") }}
</p>
</div>
</div>