From 30a441d9ec94ce175585f02c92de9c687de1cc5c Mon Sep 17 00:00:00 2001 From: Jason Date: Thu, 4 Sep 2025 23:00:16 +0800 Subject: [PATCH] refactor(migration): dedupe by (name + raw key) without hashing\n\n- Compare API key strings directly for Claude/Codex during migration\n- Remove sha2/hex deps and hashing helpers\n- Keep O(N^2) matching acceptable for small provider sets --- src-tauri/src/migration.rs | 104 +++++++++++++++++++++++++------------ 1 file changed, 72 insertions(+), 32 deletions(-) diff --git a/src-tauri/src/migration.rs b/src-tauri/src/migration.rs index fc5e728..7b28237 100644 --- a/src-tauri/src/migration.rs +++ b/src-tauri/src/migration.rs @@ -36,6 +36,24 @@ fn next_unique_id(existing: &HashSet, base: &str) -> String { format!("{}-dup", base) } +fn extract_claude_api_key(value: &Value) -> Option { + value + .get("env") + .and_then(|env| env.get("ANTHROPIC_AUTH_TOKEN")) + .and_then(|v| v.as_str()) + .map(|s| s.to_string()) +} + +fn extract_codex_api_key(value: &Value) -> Option { + value + .get("auth") + .and_then(|auth| auth.get("OPENAI_API_KEY").or_else(|| auth.get("openai_api_key"))) + .and_then(|v| v.as_str()) + .map(|s| s.to_string()) +} + +// 去重策略:name + 原始 key 直接比较(不做哈希) + fn scan_claude_copies() -> Vec<(String, PathBuf, Value)> { let mut items = Vec::new(); let dir = get_claude_config_dir(); @@ -153,21 +171,27 @@ pub fn migrate_copies_into_config(config: &mut MultiAppConfig) -> Result = manager.providers.keys().cloned().collect(); let mut live_claude_id: Option = None; if let Some((name, value)) = &live_claude { - if let Some((id, prov)) = manager + let cand_key = extract_claude_api_key(value); + let exist_id = manager .providers - .iter_mut() - .find(|(_, p)| p.name == *name) - { - log::info!("覆盖 Claude 供应商 '{}' 来自 live settings.json", name); - prov.settings_config = value.clone(); - live_claude_id = Some(id.clone()); + .iter() + .find_map(|(id, p)| { + let pk = extract_claude_api_key(&p.settings_config); + if p.name == *name && pk == cand_key { Some(id.clone()) } else { None } + }); + if let Some(exist_id) = exist_id { + if let Some(prov) = manager.providers.get_mut(&exist_id) { + log::info!("合并到已存在 Claude 供应商 '{}' (by name+key)", name); + prov.settings_config = value.clone(); + live_claude_id = Some(exist_id); + } } else { let id = next_unique_id(&ids, name); ids.insert(id.clone()); @@ -182,20 +206,24 @@ pub fn migrate_copies_into_config(config: &mut MultiAppConfig) -> Result Result = manager.providers.keys().cloned().collect(); let mut live_codex_id: Option = None; if let Some((name, value)) = &live_codex { - if let Some((id, prov)) = manager + let cand_key = extract_codex_api_key(value); + let exist_id = manager .providers - .iter_mut() - .find(|(_, p)| p.name == *name) - { - log::info!("覆盖 Codex 供应商 '{}' 来自 live auth/config", name); - prov.settings_config = value.clone(); - live_codex_id = Some(id.clone()); + .iter() + .find_map(|(id, p)| { + let pk = extract_codex_api_key(&p.settings_config); + if p.name == *name && pk == cand_key { Some(id.clone()) } else { None } + }); + if let Some(exist_id) = exist_id { + if let Some(prov) = manager.providers.get_mut(&exist_id) { + log::info!("合并到已存在 Codex 供应商 '{}' (by name+key)", name); + prov.settings_config = value.clone(); + live_codex_id = Some(exist_id); + } } else { let id = next_unique_id(&ids, name); ids.insert(id.clone()); @@ -270,18 +304,24 @@ pub fn migrate_copies_into_config(config: &mut MultiAppConfig) -> Result