refactor(claude-desktop): trim duplication in proxy and switch flows

- services/proxy.rs: collapse 10 repeated `OpenCode | OpenClaw | Hermes |
  ClaudeDesktop` match arms into `_` fallthroughs.
- claude_desktop_config.rs: extract a `with_rollback` closure shared by
  apply_provider_to_paths and restore_official_at_paths.
- useProviderActions.ts: replace the triple-nested ternary picking the
  switch-success toast message with a flat let/if/else block.

Net -36 lines. No behavior change; cargo test and pnpm typecheck pass.
This commit is contained in:
Jason
2026-05-08 12:26:46 +08:00
Unverified
parent 8b3ad9caf9
commit 83f4e1d0ad
3 changed files with 48 additions and 84 deletions
+23 -28
View File
@@ -599,6 +599,8 @@ pub fn map_proxy_request_model(mut body: Value, provider: &Provider) -> Result<V
}
pub fn proxy_gateway_base_url_from_db(db: &Database) -> Result<String, AppError> {
// get_proxy_config is async-tagged but its body is fully synchronous (rusqlite
// under a Mutex), so block_on cannot deadlock the calling thread.
let config = futures::executor::block_on(db.get_proxy_config())?;
Ok(format!(
"{}{}",
@@ -617,39 +619,32 @@ fn apply_provider_to_paths(
}
validate_provider(provider)?;
let snapshots = snapshot_files(paths)?;
if let Err(err) = apply_provider_to_paths_inner(db, provider, paths) {
if let Err(rollback_err) = restore_snapshots(&snapshots) {
log::error!(
"Failed to rollback Claude Desktop config after apply error: {rollback_err}"
);
return Err(AppError::Message(format!(
"{err}; rollback failed: {rollback_err}"
)));
}
return Err(err);
}
Ok(())
with_rollback(paths, |paths| apply_provider_to_paths_inner(db, provider, paths))
}
fn restore_official_at_paths(paths: &ClaudeDesktopPaths) -> Result<(), AppError> {
with_rollback(paths, restore_official_at_paths_inner)
}
fn with_rollback<F>(paths: &ClaudeDesktopPaths, op: F) -> Result<(), AppError>
where
F: FnOnce(&ClaudeDesktopPaths) -> Result<(), AppError>,
{
let snapshots = snapshot_files(paths)?;
if let Err(err) = restore_official_at_paths_inner(paths) {
if let Err(rollback_err) = restore_snapshots(&snapshots) {
log::error!(
"Failed to rollback Claude Desktop config after restore error: {rollback_err}"
);
return Err(AppError::Message(format!(
"{err}; rollback failed: {rollback_err}"
)));
}
return Err(err);
match op(paths) {
Ok(()) => Ok(()),
Err(err) => match restore_snapshots(&snapshots) {
Ok(()) => Err(err),
Err(rollback_err) => {
log::error!(
"Failed to rollback Claude Desktop config after error: {rollback_err}"
);
Err(AppError::Message(format!(
"{err}; rollback failed: {rollback_err}"
)))
}
},
}
Ok(())
}
fn apply_provider_to_paths_inner(
+10 -36
View File
@@ -466,10 +466,7 @@ impl ProxyService {
AppType::Claude => self.read_claude_live()?,
AppType::Codex => self.read_codex_live()?,
AppType::Gemini => self.read_gemini_live()?,
AppType::OpenCode | AppType::OpenClaw | AppType::Hermes | AppType::ClaudeDesktop => {
// These apps don't support proxy features
return Err("该应用不支持代理功能".to_string());
}
_ => return Err("该应用不支持代理功能".to_string()),
};
self.sync_live_config_to_provider(app_type, &live_config)
@@ -683,9 +680,7 @@ impl ProxyService {
}
}
}
AppType::OpenCode | AppType::OpenClaw | AppType::Hermes | AppType::ClaudeDesktop => {
// These apps don't support proxy features, skip silently
}
_ => {}
}
Ok(())
@@ -864,10 +859,7 @@ impl ProxyService {
AppType::Claude => ("claude", self.read_claude_live()?),
AppType::Codex => ("codex", self.read_codex_live()?),
AppType::Gemini => ("gemini", self.read_gemini_live()?),
AppType::OpenCode | AppType::OpenClaw | AppType::Hermes | AppType::ClaudeDesktop => {
// These apps don't support proxy features
return Err("该应用不支持代理功能".to_string());
}
_ => return Err("该应用不支持代理功能".to_string()),
};
let json_str = serde_json::to_string(&config)
@@ -1008,10 +1000,7 @@ impl ProxyService {
self.write_gemini_live(&live_config)?;
log::info!("Gemini Live 配置已接管,代理地址: {proxy_url}");
}
AppType::OpenCode | AppType::OpenClaw | AppType::Hermes | AppType::ClaudeDesktop => {
// These apps don't support proxy features
return Err("该应用不支持代理功能".to_string());
}
_ => return Err("该应用不支持代理功能".to_string()),
}
Ok(())
@@ -1061,9 +1050,7 @@ impl ProxyService {
let _ = self.write_gemini_live(&live_config);
}
}
AppType::OpenCode | AppType::OpenClaw | AppType::Hermes | AppType::ClaudeDesktop => {
// These apps don't support proxy features, skip silently
}
_ => {}
}
Ok(())
@@ -1101,9 +1088,7 @@ impl ProxyService {
log::info!("Gemini Live 配置已恢复");
}
}
AppType::OpenCode | AppType::OpenClaw | AppType::Hermes | AppType::ClaudeDesktop => {
// These apps don't support proxy features, skip silently
}
_ => {}
}
Ok(())
@@ -1192,10 +1177,7 @@ impl ProxyService {
AppType::Claude => self.write_claude_live(config),
AppType::Codex => self.write_codex_live(config),
AppType::Gemini => self.write_gemini_live(config),
AppType::OpenCode | AppType::OpenClaw | AppType::Hermes | AppType::ClaudeDesktop => {
// These apps don't support proxy features
Err("该应用不支持代理功能".to_string())
}
_ => Err("该应用不支持代理功能".to_string()),
}
}
@@ -1213,10 +1195,7 @@ impl ProxyService {
Ok(config) => Self::is_gemini_live_taken_over(&config),
Err(_) => false,
},
AppType::OpenCode | AppType::OpenClaw | AppType::Hermes | AppType::ClaudeDesktop => {
// These apps don't support proxy takeover
false
}
_ => false,
}
}
@@ -1256,10 +1235,7 @@ impl ProxyService {
AppType::Claude => self.cleanup_claude_takeover_placeholders_in_live(),
AppType::Codex => self.cleanup_codex_takeover_placeholders_in_live(),
AppType::Gemini => self.cleanup_gemini_takeover_placeholders_in_live(),
AppType::OpenCode | AppType::OpenClaw | AppType::Hermes | AppType::ClaudeDesktop => {
// These apps don't support proxy features
Ok(())
}
_ => Ok(()),
}
}
@@ -1520,9 +1496,7 @@ impl ProxyService {
serde_json::to_string(&env_backup)
.map_err(|e| format!("序列化 Gemini 配置失败: {e}"))?
}
AppType::OpenCode | AppType::OpenClaw | AppType::Hermes | AppType::ClaudeDesktop => {
return Err(format!("未知的应用类型: {app_type}"));
}
_ => return Err(format!("未知的应用类型: {app_type}")),
};
self.db
+15 -20
View File
@@ -227,26 +227,21 @@ export function useProviderActions(
// 若已弹过 proxyRequired 警告则不再弹 success
if (!proxyRequiredReason) {
// OpenCode/OpenClaw: show "added to config" message instead of "switched"
const isMultiProviderApp =
activeApp === "opencode" || activeApp === "openclaw";
const messageKey =
activeApp === "claude-desktop"
? provider.meta?.claudeDesktopMode === "proxy"
? "notifications.claudeDesktopProxyRestartRequired"
: "notifications.claudeDesktopRestartRequired"
: isMultiProviderApp
? "notifications.addToConfigSuccess"
: "notifications.switchSuccess";
const defaultMessage =
activeApp === "claude-desktop"
? provider.meta?.claudeDesktopMode === "proxy"
? "切换成功,请保持 CC Switch 运行,并重启 Claude Desktop 后生效"
: "切换成功,重启 Claude Desktop 后生效"
: isMultiProviderApp
? "已添加到配置"
: "切换成功!";
let messageKey = "notifications.switchSuccess";
let defaultMessage = "切换成功!";
if (activeApp === "claude-desktop") {
if (provider.meta?.claudeDesktopMode === "proxy") {
messageKey = "notifications.claudeDesktopProxyRestartRequired";
defaultMessage =
"切换成功,请保持 CC Switch 运行,并重启 Claude Desktop 后生效";
} else {
messageKey = "notifications.claudeDesktopRestartRequired";
defaultMessage = "切换成功,重启 Claude Desktop 后生效";
}
} else if (activeApp === "opencode" || activeApp === "openclaw") {
messageKey = "notifications.addToConfigSuccess";
defaultMessage = "已添加到配置";
}
toast.success(t(messageKey, { defaultValue: defaultMessage }), {
closeButton: true,
});