Feature:支持 SQL Server 旧版兼容模式 (#2181)

* fix: 支持 SQL Server 旧版非加密重试

* fix: 优化 SQL Server 旧版兼容模式入口

---------

Co-authored-by: staff <staff@qimaos-MacBook-Pro.local>
This commit is contained in:
zipgandstaff authored and GitHub committed 2026-06-30 19:52:07 +08:00
1 parent 8747a0e8f9
commit d04463ebeb
7 files changed
+136 -14

No files matched your search

@@ -867,6 +867,28 @@ function connectionErrorWithDriverUpdateHint(config: ConnectionConfig, message:
return appendAgentDriverUpdateHint(message, t("connection.agentDriverUpdateConnectionHint"));
}
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()) {
if (key.trim().toLowerCase() === "sqlserverencryption") {
return ["disabled", "disable", "false", "0", "off"].includes(value.trim().toLowerCase());
}
}
return false;
}
function setSqlServerLegacyUnencryptedMode(params: string | undefined, enabled: boolean): string {
const normalized = (params || "").trim().replace(/^\?/, "").replace(/;/g, "&");
return setUrlParam(normalized, "sqlserverEncryption", enabled ? "disabled" : "");
}
function isSqlServerTlsHandshakeFailure(message: string): boolean {
const text = message.toLowerCase();
return text.includes("sql server") && text.includes("tls") && (text.includes("handshake") || text.includes("eof") || text.includes("performing i/o"));
}
async function testConnectionWithTimeout(config: ConnectionConfig, runId: number): Promise<string> {
const timeoutMs = connectionAttemptTimeoutMs(config);
const timeoutMessage = connectionAttemptTimeoutMessage(timeoutMs);
@@ -1608,6 +1630,13 @@ const testResultMessage = computed(() => {
if (!testResult.value) return "";
return testResult.value.ok ? t("connection.testSuccess") : translateBackendError(t, testResult.value.message);
});
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 shouldUseWideConnectionDialog = computed(() => dialogStep.value === "config" && (canChooseVisibleDatabases.value || (canChooseVisibleSchemas.value && !visibleFilterUsesSchemas.value)));
const connectionDialogContentClass = computed(() => {
if (dialogStep.value === "select") return "sm:max-w-[760px]";
@@ -1685,6 +1714,10 @@ async function testConnection() {
const message = connectionErrorWithDriverUpdateHint(config, mongodbAuthFailureHint(String(e)));
const fallbackMessage = await tryNacosDockerConsoleFallback(config, message, runId);
if (runId !== testRunId) return;
const shouldShowSqlServerLegacyMode = !fallbackMessage && config.db_type === "sqlserver" && !isSqlServerLegacyUnencryptedMode(config.url_params) && isSqlServerTlsHandshakeFailure(message);
if (shouldShowSqlServerLegacyMode) {
configTab.value = "advanced";
}
testResult.value = fallbackMessage ? { ok: true, message: fallbackMessage } : { ok: false, message };
} finally {
if (runId === testRunId) {
@@ -4297,6 +4330,18 @@ function openExternalUrl(url: string) {
<span class="text-xs text-muted-foreground">{{ t("connection.readOnlyHint") }}</span>
</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>
<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>
</label>
<p class="m-0 whitespace-pre-line text-xs leading-5 text-muted-foreground">
{{ t("connection.sqlServerLegacyUnencryptedModeHint") }}
</p>
</div>
</div>
<div v-show="form.db_type === 'redis'" class="grid grid-cols-4 items-center gap-4">
<Label :class="connectionLabelSmallClass">{{ t("settings.redisScanPageSize") }}</Label>
<div class="col-span-3 flex flex-col gap-1">
@@ -4318,7 +4363,7 @@ function openExternalUrl(url: string) {
<TabsContent v-if="canUseTransportLayers" value="transport" class="m-0">
<div class="connection-form-body grid gap-4 py-4 pr-2 max-h-[65vh] overflow-y-auto">
<div class="grid grid-cols-4 items-start gap-4">
<div class="connection-label-wide-grid grid grid-cols-4 items-start gap-4">
<Label :class="connectionLabelSmallPaddedClass">{{ t("connection.sshHops") }}</Label>
<div class="col-span-3 grid gap-3">
<div class="flex flex-wrap items-center gap-1 text-[11px] text-muted-foreground">
@@ -4629,4 +4674,8 @@ function openExternalUrl(url: string) {
width: min(100%, 36rem);
margin-inline: auto;
}
.connection-dialog-content .grid.grid-cols-4.connection-label-wide-grid {
grid-template-columns: 7.75rem repeat(3, minmax(0, 1fr));
}
</style>