refactor(agent): share driver service logic

This commit is contained in:
t8y2
2026-05-18 22:08:10 +08:00
parent 8a81a01fac
commit 94eeb38d63
5 changed files with 233 additions and 277 deletions
+6 -153
View File
@@ -1,133 +1,16 @@
use std::path::PathBuf;
use std::sync::Arc;
use tauri::{Emitter, State};
use tokio::sync::Mutex;
use dbx_core::agent_manager::{
AgentDriverInfo, AgentManager, AgentRegistry, InstalledDriver, JavaRuntimeConfig, JavaRuntimeMode, DEFAULT_JRE_KEY,
AgentDriverInfo, AgentManager, InstalledDriver, JavaRuntimeConfig, JavaRuntimeMode, DEFAULT_JRE_KEY,
};
use dbx_core::agent_service::{
build_agent_list, fetch_registry, find_local_agent_jar, github_url_to_r2_path, install_local_agent,
invalidate_registry_cache,
};
use dbx_core::connection::AppState;
const REGISTRY_PATH: &str = "https://github.com/t8y2/dbx-agents/releases/latest/download/agent-registry.json";
const REGISTRY_R2_PATH: &str = "agents/agent-registry.json";
static REGISTRY_CACHE: std::sync::LazyLock<Mutex<Option<(std::time::Instant, AgentRegistry)>>> =
std::sync::LazyLock::new(|| Mutex::new(None));
const AGENT_TYPES: &[(&str, &str)] = &[
("dameng", "达梦 DM8"),
("kingbase", "人大金仓 KingbaseES"),
("highgo", "瀚高 HighGo"),
("vastbase", "Vastbase"),
("goldendb", "GoldenDB"),
("access", "Microsoft Access"),
("oracle", "Oracle"),
("oracle-10g", "Oracle 10g"),
("h2", "H2"),
("snowflake", "Snowflake"),
("trino", "Trino (Presto)"),
("hive", "Apache Hive"),
("db2", "IBM DB2"),
("informix", "IBM Informix"),
("neo4j", "Neo4j"),
("cassandra", "Apache Cassandra"),
("bigquery", "Google BigQuery"),
("kylin", "Apache Kylin"),
("sundb", "SunDB"),
("gaussdb", "GaussDB"),
("yashandb", "崖山 YashanDB"),
("tdengine", "TDengine"),
("mongodb", "MongoDB (Legacy)"),
];
fn build_agent_list(am: &AgentManager, registry: Option<&AgentRegistry>) -> Vec<AgentDriverInfo> {
let local_state = am.load_state();
AGENT_TYPES
.iter()
.map(|(key, label)| {
let installed = am.is_driver_installed(key);
let local = local_state.installed_drivers.get(*key);
let remote = registry.and_then(|r| r.drivers.get(*key));
let jre_key = remote
.map(|r| r.jre.clone())
.or_else(|| local.map(|l| l.jre.clone()))
.unwrap_or_else(|| DEFAULT_JRE_KEY.to_string());
AgentDriverInfo {
db_type: key.to_string(),
label: label.to_string(),
version: remote.map(|r| r.version.clone()).unwrap_or_default(),
size: remote.map(|r| r.jar.size).unwrap_or(0),
installed,
installed_version: local.map(|l| l.version.clone()),
update_available: match (local, remote) {
(Some(l), Some(r)) => l.version != r.version,
_ => false,
},
jre: jre_key.clone(),
jre_installed: am.is_jre_installed(&jre_key),
}
})
.collect()
}
fn local_agent_jar_candidates(db_type: &str) -> Vec<PathBuf> {
let jar_name = format!("dbx-agent-{db_type}.jar");
let relative = PathBuf::from("..").join("dbx-agents").join(db_type).join("build").join("libs").join(&jar_name);
let nested = PathBuf::from("dbx-agents").join(db_type).join("build").join("libs").join(&jar_name);
vec![relative, nested]
}
fn find_local_agent_jar(db_type: &str) -> Option<PathBuf> {
local_agent_jar_candidates(db_type).into_iter().find(|path| path.exists())
}
fn install_local_agent(am: &AgentManager, db_type: &str, source: PathBuf) -> Result<(), String> {
let jar_path = am.driver_jar_path(db_type);
let parent = jar_path.parent().ok_or_else(|| format!("Invalid driver path: {}", jar_path.display()))?;
std::fs::create_dir_all(parent).map_err(|e| e.to_string())?;
std::fs::copy(&source, &jar_path).map_err(|e| format!("Failed to copy local agent jar: {e}"))?;
let mut local_state = am.load_state();
local_state.installed_drivers.insert(
db_type.to_string(),
InstalledDriver {
version: "0.1.0-local".to_string(),
installed_at: chrono::Utc::now().to_rfc3339(),
jre: DEFAULT_JRE_KEY.to_string(),
},
);
am.save_state(&local_state)
}
#[cfg(test)]
mod tests {
use super::build_agent_list;
use dbx_core::agent_manager::AgentManager;
#[test]
fn built_in_agent_list_includes_access_and_tdengine() {
let dir = std::env::temp_dir().join(format!("dbx-agent-list-test-{}", uuid::Uuid::new_v4()));
let manager = AgentManager::new_with_base_dir(dir.clone());
let agents = build_agent_list(&manager, None);
assert!(agents.iter().any(|agent| agent.db_type == "tdengine" && agent.label == "TDengine"));
assert!(agents.iter().any(|agent| agent.db_type == "yashandb" && agent.label == "崖山 YashanDB"));
assert!(agents.iter().any(|agent| agent.db_type == "access" && agent.label == "Microsoft Access"));
let _ = std::fs::remove_dir_all(dir);
}
#[test]
fn local_agent_jar_candidates_include_sibling_dbx_agents_build_output() {
let candidates = super::local_agent_jar_candidates("tdengine");
assert!(candidates
.iter()
.any(|path| { path.ends_with("dbx-agents/tdengine/build/libs/dbx-agent-tdengine.jar") }));
}
}
#[tauri::command]
pub async fn list_installed_agents_local(state: State<'_, Arc<AppState>>) -> Result<Vec<AgentDriverInfo>, String> {
Ok(build_agent_list(&state.agent_manager, None))
@@ -401,7 +284,7 @@ pub async fn uninstall_jre(state: State<'_, Arc<AppState>>, jre_key: String) ->
#[tauri::command]
pub async fn invalidate_agent_registry_cache() -> Result<(), String> {
*REGISTRY_CACHE.lock().await = None;
invalidate_registry_cache().await;
Ok(())
}
@@ -441,36 +324,6 @@ pub async fn reinstall_jre(
Ok(())
}
async fn fetch_registry() -> Result<AgentRegistry, String> {
{
let cache = REGISTRY_CACHE.lock().await;
if let Some((ts, reg)) = cache.as_ref() {
if ts.elapsed() < std::time::Duration::from_secs(300) {
return Ok(reg.clone());
}
}
}
let client = reqwest::Client::builder()
.timeout(std::time::Duration::from_secs(10))
.build()
.map_err(|e| format!("Failed to create HTTP client: {e}"))?;
let resp = dbx_core::race_download(&client, REGISTRY_PATH, REGISTRY_R2_PATH, "dbx-agent-manager")
.await
.map_err(|e| format!("Failed to fetch agent registry: {e}"))?;
let reg: AgentRegistry = resp.json().await.map_err(|e| format!("Failed to parse registry: {e}"))?;
*REGISTRY_CACHE.lock().await = Some((std::time::Instant::now(), reg.clone()));
Ok(reg)
}
fn github_url_to_r2_path(github_url: &str, category: &str) -> String {
let filename = github_url.rsplit('/').next().unwrap_or(github_url);
match category {
"jre" => format!("agents/jre/{filename}"),
"driver" => format!("agents/drivers/{filename}"),
_ => format!("agents/{filename}"),
}
}
async fn download_with_progress(
app: &tauri::AppHandle,
step: &str,