fix: improve mongodb legacy connection params

This commit is contained in:
SuLea-IT 2026-05-06 13:10:46 +08:00
parent 746a4c491a
commit 8c1a75d3d5
4 changed files with 109 additions and 6 deletions

View File

@ -121,7 +121,8 @@ impl ConnectionConfig {
if let Some(cs) = self.connection_string.as_deref().filter(|s| !s.is_empty()) {
return cs.to_string();
}
format!("mongodb://{host}:{port}{db_part}")
let suffix = if params.is_empty() { String::new() } else { format!("?{params}") };
format!("mongodb://{host}:{port}{db_part}{suffix}")
}
DatabaseType::Oracle => format!("oracle://{host}:{port}{db_part}"),
DatabaseType::Elasticsearch => format!("http://{host}:{port}"),
@ -172,10 +173,11 @@ impl ConnectionConfig {
if let Some(cs) = self.connection_string.as_deref().filter(|s| !s.is_empty()) {
return cs.to_string();
}
let suffix = if params.is_empty() { String::new() } else { format!("?{params}") };
if self.username.is_empty() {
format!("mongodb://{host}:{port}{db_part}")
format!("mongodb://{host}:{port}{db_part}{suffix}")
} else {
format!("mongodb://{username}:{password}@{host}:{port}{db_part}")
format!("mongodb://{username}:{password}@{host}:{port}{db_part}{suffix}")
}
}
DatabaseType::Oracle => {
@ -233,9 +235,33 @@ impl ConnectionConfig {
}
}
DatabaseType::Postgres | DatabaseType::Redshift => value.trim_start_matches('?').to_string(),
DatabaseType::MongoDb => self.normalized_mongodb_url_params(value),
_ => value.trim_start_matches('?').to_string(),
}
}
fn normalized_mongodb_url_params(&self, value: &str) -> String {
let mut params: Vec<String> = value
.trim_start_matches('?')
.split('&')
.filter(|param| !param.trim().is_empty())
.map(str::to_string)
.collect();
push_query_param_if_missing(&mut params, "directConnection", "directConnection=true".to_string());
if !self.username.is_empty() {
let auth_source = self.database.as_deref().filter(|db| !db.is_empty()).unwrap_or("admin");
push_query_param_if_missing(
&mut params,
"authSource",
format!("authSource={}", encode_url_part(auth_source)),
);
push_query_param_if_missing(&mut params, "authMechanism", "authMechanism=SCRAM-SHA-1".to_string());
}
params.join("&")
}
}
fn encode_url_part(value: &str) -> String {
@ -250,6 +276,15 @@ fn bracket_ipv6(host: &str) -> String {
}
}
fn push_query_param_if_missing(params: &mut Vec<String>, key: &str, value: String) {
if !params
.iter()
.any(|param| param.split_once('=').map(|(name, _)| name).unwrap_or(param.as_str()).eq_ignore_ascii_case(key))
{
params.push(value);
}
}
#[cfg(test)]
mod tests {
use super::{ConnectionConfig, DatabaseType};
@ -281,6 +316,13 @@ mod tests {
}
}
fn mongodb_config(username: &str, password: &str, database: Option<&str>) -> ConnectionConfig {
let mut config = mysql_config(username, password, database);
config.db_type = DatabaseType::MongoDb;
config.port = 17000;
config
}
#[test]
fn mysql_url_encodes_oceanbase_username() {
let config = mysql_config("user@tenant#cluster", "secret", None);
@ -321,6 +363,27 @@ mod tests {
assert_eq!(config.connection_url(), "postgres://postgres:secret@10.1.2.3:2883/test?sslmode=disable");
}
#[test]
fn mongodb_form_url_adds_legacy_direct_connection_params() {
let config = mongodb_config("root", "secret", Some("admin"));
assert_eq!(
config.connection_url(),
"mongodb://root:secret@10.1.2.3:17000/admin?directConnection=true&authSource=admin&authMechanism=SCRAM-SHA-1"
);
}
#[test]
fn mongodb_form_url_respects_custom_auth_params() {
let mut config = mongodb_config("root", "secret", Some("app"));
config.url_params = Some("authSource=admin&authMechanism=SCRAM-SHA-256&retryWrites=false".to_string());
assert_eq!(
config.connection_url(),
"mongodb://root:secret@10.1.2.3:17000/app?authSource=admin&authMechanism=SCRAM-SHA-256&retryWrites=false&directConnection=true"
);
}
#[test]
fn redacted_mysql_url_omits_credentials() {
let config = mysql_config("user@tenant#cluster", "p@ss:word#1", Some("db/name"));
@ -357,4 +420,18 @@ mod tests {
assert!(!url.contains("default"));
assert!(!url.contains("redis-secret"));
}
#[test]
fn redacted_mongodb_url_keeps_compatibility_params_without_credentials() {
let config = mongodb_config("root", "secret", Some("admin"));
let url = config.redacted_connection_url();
assert_eq!(
url,
"mongodb://10.1.2.3:17000/admin?directConnection=true&authSource=admin&authMechanism=SCRAM-SHA-1"
);
assert!(!url.contains("root"));
assert!(!url.contains("secret"));
}
}

View File

@ -401,7 +401,7 @@ async function testConnection() {
isTesting.value = true;
testResult.value = null;
try {
const config: ConnectionConfig = { ...form.value, id: editingId.value || uuid() };
const config = connectionConfigForSubmit(editingId.value || uuid());
const msg = await api.testConnection(config);
if (runId !== testRunId) return;
testResult.value = { ok: true, message: msg };
@ -415,6 +415,14 @@ async function testConnection() {
}
}
function connectionConfigForSubmit(id: string): ConnectionConfig {
const config: ConnectionConfig = { ...form.value, id };
if (config.db_type === "mongodb" && !mongoUseUrl.value) {
config.connection_string = undefined;
}
return config;
}
function resetTestState() {
testRunId += 1;
isTesting.value = false;
@ -462,11 +470,11 @@ async function save() {
resetTestState();
try {
if (editingId.value) {
const updated: ConnectionConfig = { ...form.value, id: editingId.value };
const updated = connectionConfigForSubmit(editingId.value);
await store.updateConnection(updated);
store.stopEditing();
} else {
const config: ConnectionConfig = { ...form.value, id: uuid() };
const config = connectionConfigForSubmit(uuid());
await store.addConnection(config);
open.value = false;
await nextTick();
@ -757,6 +765,20 @@ async function browseSshKeyPath() {
:placeholder="t('connection.databasePlaceholder')"
/>
</div>
<div class="grid grid-cols-4 items-center gap-4">
<Label class="text-right">{{ t("connection.urlParams") }}</Label>
<Input
v-model="form.url_params"
class="col-span-3"
placeholder="authSource=admin&authMechanism=SCRAM-SHA-1"
/>
</div>
<div class="grid grid-cols-4 items-start gap-4">
<span />
<p class="col-span-3 text-xs text-muted-foreground">
{{ t("connection.mongoLegacyHint") }}
</p>
</div>
</template>
</template>

View File

@ -112,6 +112,8 @@ export default {
sshKeyPathBrowse: "Browse",
sshExposeLan: "Expose tunnel to LAN",
dmCompatHint: "Requires DM PG compatibility mode (set COMPATIBLE_MODE=7 in dm.ini and restart)",
mongoLegacyHint:
"MongoDB form connections add directConnection automatically; authenticated connections default to the selected database as auth source (admin when blank) and SCRAM-SHA-1, which you can override here.",
compatible: "Compatible",
mainstream: "Popular",
color: "Color",

View File

@ -111,6 +111,8 @@ export default {
sshKeyPathBrowse: "浏览",
sshExposeLan: "允许局域网访问隧道",
dmCompatHint: "需要开启达梦 PG 兼容模式dm.ini 中设置 COMPATIBLE_MODE=7 并重启服务)",
mongoLegacyHint:
"MongoDB 表单连接会自动补充 directConnection带用户名时默认使用当前数据库认证源留空为 admin和 SCRAM-SHA-1可在此覆盖。",
compatible: "兼容",
mainstream: "主流",
color: "颜色",