forked from mirrors/kingfisher
72 lines
2.6 KiB
Rust
72 lines
2.6 KiB
Rust
// src/validation/mongodb.rs
|
||
use std::time::Duration;
|
||
|
||
use anyhow::Result;
|
||
use bson::doc;
|
||
use mongodb::{options::ClientOptions, Client};
|
||
|
||
pub fn looks_like_mongodb_uri(uri: &str) -> bool {
|
||
// quick scheme check first
|
||
if !(uri.starts_with("mongodb://") || uri.starts_with("mongodb+srv://")) {
|
||
return false;
|
||
}
|
||
// pure string-level parse – no network, even for +srv
|
||
mongodb::options::ConnectionString::parse(uri).is_ok()
|
||
}
|
||
|
||
const FAST_CONNECT_MS: u64 = 700; // direct single-host URIs
|
||
const FAST_SELECT_MS: u64 = 300;
|
||
const SRV_CONNECT_MS: u64 = 15_000; // gives Atlas a fighting chance
|
||
const SRV_SELECT_MS: u64 = 15_000;
|
||
|
||
/// Validates a MongoDB URI in ≤ 2 s. Returns `(bool, String)` where the
|
||
/// boolean indicates success and the string provides a status message.
|
||
pub async fn validate_mongodb(uri: &str) -> Result<(bool, String)> {
|
||
// ---- quick reject without touching the network
|
||
if !looks_like_mongodb_uri(uri) {
|
||
return Ok((false, "Invalid MongoDB URI".to_string()));
|
||
}
|
||
|
||
let is_srv = uri.starts_with("mongodb+srv://");
|
||
|
||
if is_srv {
|
||
// Skip SRV URIs to avoid slow DNS lookups and topology discovery.
|
||
return Ok((
|
||
false,
|
||
"Validation skipped for mongodb+srv:// URI (performance reasons)".to_string(),
|
||
));
|
||
}
|
||
|
||
// ---- build client opts
|
||
let mut opts = ClientOptions::parse(uri).await?;
|
||
if !is_srv {
|
||
// one socket, skip cluster discovery for plain 'mongodb://'
|
||
opts.direct_connection = Some(true);
|
||
opts.connect_timeout = Some(Duration::from_millis(FAST_CONNECT_MS));
|
||
opts.server_selection_timeout = Some(Duration::from_millis(FAST_SELECT_MS));
|
||
} else {
|
||
// SRV needs DNS and replica-set discovery; give it a couple seconds
|
||
opts.connect_timeout = Some(Duration::from_millis(SRV_CONNECT_MS));
|
||
opts.server_selection_timeout = Some(Duration::from_millis(SRV_SELECT_MS));
|
||
// leave direct_connection = None (driver decides)
|
||
}
|
||
opts.max_pool_size = Some(1);
|
||
opts.min_pool_size = Some(0);
|
||
|
||
// ---- dial and ping
|
||
let client = Client::with_options(opts)?;
|
||
let ok = client.database("admin").run_command(doc! { "ping": 1 }).await.is_ok();
|
||
let msg = if ok {
|
||
"MongoDB connection is valid.".to_string()
|
||
} else {
|
||
"MongoDB connection failed.".to_string()
|
||
};
|
||
Ok((ok, msg))
|
||
}
|
||
|
||
// pub fn generate_mongodb_cache_key(mongodb_uri: &str) -> String {
|
||
// use sha1::{Digest, Sha1};
|
||
// let mut hasher = Sha1::new();
|
||
// hasher.update(mongodb_uri.as_bytes());
|
||
// format!("MongoDB:{:x}", hasher.finalize())
|
||
// }
|