mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-31 16:24:33 +00:00
fix(mongodb): support replica set connection URL parsing and timeout
Frontend: add regex-based MongoDB URL parser to handle multi-host URIs that the WHATWG URL parser rejects. Backend: separate server_selection_timeout from connect_timeout for multi-host URIs to prevent topology discovery from being cancelled by tokio timeout.
This commit is contained in:
@@ -79,20 +79,71 @@ export function normalizeMongoConnectionString(value: string): string {
|
||||
const input = value.trim();
|
||||
if (!input) return input;
|
||||
|
||||
let parsed: URL;
|
||||
try {
|
||||
parsed = new URL(input);
|
||||
} catch {
|
||||
return input;
|
||||
const mongoMatch = input.match(/^(mongodb(?:\+srv)?):\/\/(?:(.+)@)?/i);
|
||||
if (!mongoMatch) return input;
|
||||
|
||||
const userinfo = mongoMatch[2];
|
||||
if (!userinfo) return input;
|
||||
|
||||
const [username, ...passwordParts] = userinfo.split(":");
|
||||
const password = passwordParts.join(":");
|
||||
const encodedUsername = encodeMongoUserInfoPart(username);
|
||||
const encodedPassword = password ? `:${encodeMongoUserInfoPart(password)}` : "";
|
||||
|
||||
return input.replace(/^(mongodb(?:\+srv)?:\/\/)(?:(.+)@)?/i, `$1${encodedUsername}${encodedPassword}@`);
|
||||
}
|
||||
|
||||
function parseMongoUrl(source: string): ParsedConnectionUrl | null {
|
||||
const match = source.match(/^(mongodb(?:\+srv)?):\/\/(?:(.+)@)?([^/]+)(\/[^?]*)?(\?.*)?$/);
|
||||
if (!match) return null;
|
||||
|
||||
const scheme = match[1].toLowerCase();
|
||||
const userinfo = match[2] || "";
|
||||
const hosts = match[3] || "";
|
||||
const pathname = match[4] || "";
|
||||
const search = match[5] || "";
|
||||
|
||||
const profile = SCHEME_PROFILES[scheme];
|
||||
if (!profile) return null;
|
||||
|
||||
const [username, ...passwordParts] = decodeUrlPart(userinfo).split(":");
|
||||
const password = passwordParts.join(":");
|
||||
|
||||
const firstHost = hosts.split(",")[0];
|
||||
let host: string;
|
||||
let port: number;
|
||||
if (firstHost.startsWith("[")) {
|
||||
const bracketEnd = firstHost.indexOf("]");
|
||||
host = firstHost.substring(1, bracketEnd);
|
||||
port = firstHost.substring(bracketEnd + 1).startsWith(":")
|
||||
? Number(firstHost.substring(bracketEnd + 2)) || profile.defaultPort
|
||||
: profile.defaultPort;
|
||||
} else if (firstHost.includes(":")) {
|
||||
const colonIdx = firstHost.lastIndexOf(":");
|
||||
host = firstHost.substring(0, colonIdx);
|
||||
port = Number(firstHost.substring(colonIdx + 1)) || profile.defaultPort;
|
||||
} else {
|
||||
host = firstHost;
|
||||
port = profile.defaultPort;
|
||||
}
|
||||
|
||||
const scheme = parsed.protocol.replace(/:$/, "").toLowerCase();
|
||||
if (scheme !== "mongodb" && scheme !== "mongodb+srv") return input;
|
||||
if (!parsed.username && !parsed.password) return input;
|
||||
const database = databaseFromPath(pathname);
|
||||
const urlParams = search.replace(/^\?/, "");
|
||||
|
||||
const username = encodeMongoUserInfoPart(parsed.username);
|
||||
const password = parsed.password ? `:${encodeMongoUserInfoPart(parsed.password)}` : "";
|
||||
return `${parsed.protocol}//${username}${password}@${parsed.host}${parsed.pathname}${parsed.search}${parsed.hash}`;
|
||||
return {
|
||||
dbType: profile.type,
|
||||
driverProfile: profile.profile,
|
||||
driverLabel: profile.label,
|
||||
host,
|
||||
port,
|
||||
username,
|
||||
password,
|
||||
database,
|
||||
urlParams,
|
||||
ssl: scheme === "mongodb+srv",
|
||||
connectionString: normalizeMongoConnectionString(source),
|
||||
useMongoUrl: true,
|
||||
};
|
||||
}
|
||||
|
||||
function databaseFromPath(pathname: string): string | undefined {
|
||||
@@ -330,6 +381,9 @@ export function parseConnectionUrl(value: string, preferredProfile?: string): Pa
|
||||
const isJdbcUrl = /^jdbc:/i.test(input);
|
||||
const source = isJdbcUrl ? input.replace(/^jdbc:/i, "") : input;
|
||||
|
||||
const mongoResult = parseMongoUrl(source);
|
||||
if (mongoResult) return mongoResult;
|
||||
|
||||
let parsed: URL;
|
||||
try {
|
||||
parsed = new URL(source);
|
||||
|
||||
Reference in New Issue
Block a user