2025-08-23 20:15:10 +08:00
|
|
|
|
use std::collections::HashMap;
|
|
|
|
|
|
use tauri::State;
|
2025-08-25 20:16:29 +08:00
|
|
|
|
use tauri_plugin_opener::OpenerExt;
|
2025-08-23 20:15:10 +08:00
|
|
|
|
|
2025-08-30 21:54:11 +08:00
|
|
|
|
use crate::app_config::AppType;
|
|
|
|
|
|
use crate::codex_config;
|
2025-08-27 11:00:53 +08:00
|
|
|
|
use crate::config::{ConfigStatus, get_claude_settings_path, import_current_config_as_default};
|
2025-08-23 21:00:50 +08:00
|
|
|
|
use crate::provider::Provider;
|
2025-08-23 20:15:10 +08:00
|
|
|
|
use crate::store::AppState;
|
|
|
|
|
|
|
|
|
|
|
|
/// 获取所有供应商
|
|
|
|
|
|
#[tauri::command]
|
2025-08-27 11:00:53 +08:00
|
|
|
|
pub async fn get_providers(
|
|
|
|
|
|
state: State<'_, AppState>,
|
2025-08-31 16:43:33 +08:00
|
|
|
|
app_type: Option<AppType>,
|
2025-08-30 21:54:11 +08:00
|
|
|
|
app: Option<String>,
|
2025-08-31 16:43:33 +08:00
|
|
|
|
appType: Option<String>,
|
2025-08-27 11:00:53 +08:00
|
|
|
|
) -> Result<HashMap<String, Provider>, String> {
|
2025-08-31 16:43:33 +08:00
|
|
|
|
let app_type = app_type
|
|
|
|
|
|
.or_else(|| app.as_deref().map(|s| s.into()))
|
|
|
|
|
|
.or_else(|| appType.as_deref().map(|s| s.into()))
|
|
|
|
|
|
.unwrap_or(AppType::Claude);
|
2025-08-30 21:54:11 +08:00
|
|
|
|
|
|
|
|
|
|
let config = state
|
|
|
|
|
|
.config
|
2025-08-27 11:00:53 +08:00
|
|
|
|
.lock()
|
2025-08-23 20:15:10 +08:00
|
|
|
|
.map_err(|e| format!("获取锁失败: {}", e))?;
|
2025-08-27 11:00:53 +08:00
|
|
|
|
|
2025-08-30 21:54:11 +08:00
|
|
|
|
let manager = config
|
|
|
|
|
|
.get_manager(&app_type)
|
|
|
|
|
|
.ok_or_else(|| format!("应用类型不存在: {:?}", app_type))?;
|
|
|
|
|
|
|
2025-08-23 20:15:10 +08:00
|
|
|
|
Ok(manager.get_all_providers().clone())
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 获取当前供应商ID
|
|
|
|
|
|
#[tauri::command]
|
2025-08-30 21:54:11 +08:00
|
|
|
|
pub async fn get_current_provider(
|
|
|
|
|
|
state: State<'_, AppState>,
|
2025-08-31 16:43:33 +08:00
|
|
|
|
app_type: Option<AppType>,
|
2025-08-30 21:54:11 +08:00
|
|
|
|
app: Option<String>,
|
2025-08-31 16:43:33 +08:00
|
|
|
|
appType: Option<String>,
|
2025-08-30 21:54:11 +08:00
|
|
|
|
) -> Result<String, String> {
|
2025-08-31 16:43:33 +08:00
|
|
|
|
let app_type = app_type
|
|
|
|
|
|
.or_else(|| app.as_deref().map(|s| s.into()))
|
|
|
|
|
|
.or_else(|| appType.as_deref().map(|s| s.into()))
|
|
|
|
|
|
.unwrap_or(AppType::Claude);
|
2025-08-30 21:54:11 +08:00
|
|
|
|
|
|
|
|
|
|
let config = state
|
|
|
|
|
|
.config
|
2025-08-27 11:00:53 +08:00
|
|
|
|
.lock()
|
2025-08-23 20:15:10 +08:00
|
|
|
|
.map_err(|e| format!("获取锁失败: {}", e))?;
|
2025-08-27 11:00:53 +08:00
|
|
|
|
|
2025-08-30 21:54:11 +08:00
|
|
|
|
let manager = config
|
|
|
|
|
|
.get_manager(&app_type)
|
|
|
|
|
|
.ok_or_else(|| format!("应用类型不存在: {:?}", app_type))?;
|
|
|
|
|
|
|
2025-08-23 20:15:10 +08:00
|
|
|
|
Ok(manager.current.clone())
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 添加供应商
|
|
|
|
|
|
#[tauri::command]
|
2025-08-30 21:54:11 +08:00
|
|
|
|
pub async fn add_provider(
|
|
|
|
|
|
state: State<'_, AppState>,
|
2025-08-31 16:43:33 +08:00
|
|
|
|
app_type: Option<AppType>,
|
2025-08-30 21:54:11 +08:00
|
|
|
|
app: Option<String>,
|
2025-08-31 16:43:33 +08:00
|
|
|
|
appType: Option<String>,
|
2025-08-30 21:54:11 +08:00
|
|
|
|
provider: Provider,
|
|
|
|
|
|
) -> Result<bool, String> {
|
2025-08-31 16:43:33 +08:00
|
|
|
|
let app_type = app_type
|
|
|
|
|
|
.or_else(|| app.as_deref().map(|s| s.into()))
|
|
|
|
|
|
.or_else(|| appType.as_deref().map(|s| s.into()))
|
|
|
|
|
|
.unwrap_or(AppType::Claude);
|
2025-08-30 21:54:11 +08:00
|
|
|
|
|
|
|
|
|
|
let mut config = state
|
|
|
|
|
|
.config
|
2025-08-27 11:00:53 +08:00
|
|
|
|
.lock()
|
2025-08-23 20:15:10 +08:00
|
|
|
|
.map_err(|e| format!("获取锁失败: {}", e))?;
|
2025-08-27 11:00:53 +08:00
|
|
|
|
|
2025-08-30 21:54:11 +08:00
|
|
|
|
let manager = config
|
|
|
|
|
|
.get_manager_mut(&app_type)
|
|
|
|
|
|
.ok_or_else(|| format!("应用类型不存在: {:?}", app_type))?;
|
|
|
|
|
|
|
|
|
|
|
|
// 根据应用类型保存配置文件
|
|
|
|
|
|
match app_type {
|
|
|
|
|
|
AppType::Codex => {
|
|
|
|
|
|
// Codex: 保存两个文件
|
|
|
|
|
|
codex_config::save_codex_provider_config(
|
|
|
|
|
|
&provider.id,
|
|
|
|
|
|
&provider.name,
|
|
|
|
|
|
&provider.settings_config,
|
|
|
|
|
|
)?;
|
|
|
|
|
|
}
|
|
|
|
|
|
AppType::Claude => {
|
|
|
|
|
|
// Claude: 使用原有逻辑
|
|
|
|
|
|
use crate::config::{get_provider_config_path, write_json_file};
|
|
|
|
|
|
let config_path = get_provider_config_path(&provider.id, Some(&provider.name));
|
|
|
|
|
|
write_json_file(&config_path, &provider.settings_config)?;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
manager.providers.insert(provider.id.clone(), provider);
|
2025-08-27 11:00:53 +08:00
|
|
|
|
|
2025-08-23 20:15:10 +08:00
|
|
|
|
// 保存配置
|
2025-08-30 21:54:11 +08:00
|
|
|
|
drop(config); // 释放锁
|
2025-08-23 20:15:10 +08:00
|
|
|
|
state.save()?;
|
2025-08-27 11:00:53 +08:00
|
|
|
|
|
2025-08-23 20:15:10 +08:00
|
|
|
|
Ok(true)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 更新供应商
|
|
|
|
|
|
#[tauri::command]
|
|
|
|
|
|
pub async fn update_provider(
|
|
|
|
|
|
state: State<'_, AppState>,
|
2025-08-31 16:43:33 +08:00
|
|
|
|
app_type: Option<AppType>,
|
2025-08-30 21:54:11 +08:00
|
|
|
|
app: Option<String>,
|
2025-08-31 16:43:33 +08:00
|
|
|
|
appType: Option<String>,
|
2025-08-23 20:15:10 +08:00
|
|
|
|
provider: Provider,
|
|
|
|
|
|
) -> Result<bool, String> {
|
2025-08-31 16:43:33 +08:00
|
|
|
|
let app_type = app_type
|
|
|
|
|
|
.or_else(|| app.as_deref().map(|s| s.into()))
|
|
|
|
|
|
.or_else(|| appType.as_deref().map(|s| s.into()))
|
|
|
|
|
|
.unwrap_or(AppType::Claude);
|
2025-08-30 21:54:11 +08:00
|
|
|
|
|
|
|
|
|
|
let mut config = state
|
|
|
|
|
|
.config
|
2025-08-27 11:00:53 +08:00
|
|
|
|
.lock()
|
2025-08-23 20:15:10 +08:00
|
|
|
|
.map_err(|e| format!("获取锁失败: {}", e))?;
|
2025-08-27 11:00:53 +08:00
|
|
|
|
|
2025-08-30 21:54:11 +08:00
|
|
|
|
let manager = config
|
|
|
|
|
|
.get_manager_mut(&app_type)
|
|
|
|
|
|
.ok_or_else(|| format!("应用类型不存在: {:?}", app_type))?;
|
|
|
|
|
|
|
|
|
|
|
|
// 检查供应商是否存在
|
|
|
|
|
|
if !manager.providers.contains_key(&provider.id) {
|
|
|
|
|
|
return Err(format!("供应商不存在: {}", provider.id));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 如果名称改变了,需要处理配置文件
|
|
|
|
|
|
if let Some(old_provider) = manager.providers.get(&provider.id) {
|
|
|
|
|
|
if old_provider.name != provider.name {
|
|
|
|
|
|
// 删除旧配置文件
|
|
|
|
|
|
match app_type {
|
|
|
|
|
|
AppType::Codex => {
|
|
|
|
|
|
codex_config::delete_codex_provider_config(&provider.id, &old_provider.name)
|
|
|
|
|
|
.ok();
|
|
|
|
|
|
}
|
|
|
|
|
|
AppType::Claude => {
|
|
|
|
|
|
use crate::config::{delete_file, get_provider_config_path};
|
|
|
|
|
|
let old_config_path =
|
|
|
|
|
|
get_provider_config_path(&provider.id, Some(&old_provider.name));
|
|
|
|
|
|
delete_file(&old_config_path).ok();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 保存新配置文件
|
|
|
|
|
|
match app_type {
|
|
|
|
|
|
AppType::Codex => {
|
|
|
|
|
|
codex_config::save_codex_provider_config(
|
|
|
|
|
|
&provider.id,
|
|
|
|
|
|
&provider.name,
|
|
|
|
|
|
&provider.settings_config,
|
|
|
|
|
|
)?;
|
|
|
|
|
|
}
|
|
|
|
|
|
AppType::Claude => {
|
|
|
|
|
|
use crate::config::{get_provider_config_path, write_json_file};
|
|
|
|
|
|
let config_path = get_provider_config_path(&provider.id, Some(&provider.name));
|
|
|
|
|
|
write_json_file(&config_path, &provider.settings_config)?;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
manager.providers.insert(provider.id.clone(), provider);
|
2025-08-27 11:00:53 +08:00
|
|
|
|
|
2025-08-23 20:15:10 +08:00
|
|
|
|
// 保存配置
|
2025-08-30 21:54:11 +08:00
|
|
|
|
drop(config); // 释放锁
|
2025-08-23 20:15:10 +08:00
|
|
|
|
state.save()?;
|
2025-08-27 11:00:53 +08:00
|
|
|
|
|
2025-08-23 20:15:10 +08:00
|
|
|
|
Ok(true)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 删除供应商
|
|
|
|
|
|
#[tauri::command]
|
2025-08-30 21:54:11 +08:00
|
|
|
|
pub async fn delete_provider(
|
|
|
|
|
|
state: State<'_, AppState>,
|
2025-08-31 16:43:33 +08:00
|
|
|
|
app_type: Option<AppType>,
|
2025-08-30 21:54:11 +08:00
|
|
|
|
app: Option<String>,
|
2025-08-31 16:43:33 +08:00
|
|
|
|
appType: Option<String>,
|
2025-08-30 21:54:11 +08:00
|
|
|
|
id: String,
|
|
|
|
|
|
) -> Result<bool, String> {
|
2025-08-31 16:43:33 +08:00
|
|
|
|
let app_type = app_type
|
|
|
|
|
|
.or_else(|| app.as_deref().map(|s| s.into()))
|
|
|
|
|
|
.or_else(|| appType.as_deref().map(|s| s.into()))
|
|
|
|
|
|
.unwrap_or(AppType::Claude);
|
2025-08-30 21:54:11 +08:00
|
|
|
|
|
|
|
|
|
|
let mut config = state
|
|
|
|
|
|
.config
|
2025-08-27 11:00:53 +08:00
|
|
|
|
.lock()
|
2025-08-23 20:15:10 +08:00
|
|
|
|
.map_err(|e| format!("获取锁失败: {}", e))?;
|
2025-08-27 11:00:53 +08:00
|
|
|
|
|
2025-08-30 21:54:11 +08:00
|
|
|
|
let manager = config
|
|
|
|
|
|
.get_manager_mut(&app_type)
|
|
|
|
|
|
.ok_or_else(|| format!("应用类型不存在: {:?}", app_type))?;
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否为当前供应商
|
|
|
|
|
|
if manager.current == id {
|
|
|
|
|
|
return Err("不能删除当前正在使用的供应商".to_string());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取供应商信息
|
|
|
|
|
|
let provider = manager
|
|
|
|
|
|
.providers
|
|
|
|
|
|
.get(&id)
|
|
|
|
|
|
.ok_or_else(|| format!("供应商不存在: {}", id))?
|
|
|
|
|
|
.clone();
|
|
|
|
|
|
|
|
|
|
|
|
// 删除配置文件
|
|
|
|
|
|
match app_type {
|
|
|
|
|
|
AppType::Codex => {
|
|
|
|
|
|
codex_config::delete_codex_provider_config(&id, &provider.name)?;
|
|
|
|
|
|
}
|
|
|
|
|
|
AppType::Claude => {
|
|
|
|
|
|
use crate::config::{delete_file, get_provider_config_path};
|
|
|
|
|
|
let config_path = get_provider_config_path(&id, Some(&provider.name));
|
|
|
|
|
|
delete_file(&config_path)?;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 从管理器删除
|
|
|
|
|
|
manager.providers.remove(&id);
|
2025-08-27 11:00:53 +08:00
|
|
|
|
|
2025-08-23 20:15:10 +08:00
|
|
|
|
// 保存配置
|
2025-08-30 21:54:11 +08:00
|
|
|
|
drop(config); // 释放锁
|
2025-08-23 20:15:10 +08:00
|
|
|
|
state.save()?;
|
2025-08-27 11:00:53 +08:00
|
|
|
|
|
2025-08-23 20:15:10 +08:00
|
|
|
|
Ok(true)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 切换供应商
|
|
|
|
|
|
#[tauri::command]
|
2025-08-30 21:54:11 +08:00
|
|
|
|
pub async fn switch_provider(
|
|
|
|
|
|
state: State<'_, AppState>,
|
2025-08-31 16:43:33 +08:00
|
|
|
|
app_type: Option<AppType>,
|
2025-08-30 21:54:11 +08:00
|
|
|
|
app: Option<String>,
|
2025-08-31 16:43:33 +08:00
|
|
|
|
appType: Option<String>,
|
2025-08-30 21:54:11 +08:00
|
|
|
|
id: String,
|
|
|
|
|
|
) -> Result<bool, String> {
|
2025-08-31 16:43:33 +08:00
|
|
|
|
let app_type = app_type
|
|
|
|
|
|
.or_else(|| app.as_deref().map(|s| s.into()))
|
|
|
|
|
|
.or_else(|| appType.as_deref().map(|s| s.into()))
|
|
|
|
|
|
.unwrap_or(AppType::Claude);
|
2025-08-30 21:54:11 +08:00
|
|
|
|
|
|
|
|
|
|
let mut config = state
|
|
|
|
|
|
.config
|
2025-08-27 11:00:53 +08:00
|
|
|
|
.lock()
|
2025-08-23 20:15:10 +08:00
|
|
|
|
.map_err(|e| format!("获取锁失败: {}", e))?;
|
2025-08-27 11:00:53 +08:00
|
|
|
|
|
2025-08-30 21:54:11 +08:00
|
|
|
|
let manager = config
|
|
|
|
|
|
.get_manager_mut(&app_type)
|
|
|
|
|
|
.ok_or_else(|| format!("应用类型不存在: {:?}", app_type))?;
|
|
|
|
|
|
|
|
|
|
|
|
// 检查供应商是否存在
|
|
|
|
|
|
let provider = manager
|
|
|
|
|
|
.providers
|
|
|
|
|
|
.get(&id)
|
|
|
|
|
|
.ok_or_else(|| format!("供应商不存在: {}", id))?
|
|
|
|
|
|
.clone();
|
|
|
|
|
|
|
|
|
|
|
|
// 根据应用类型执行切换
|
|
|
|
|
|
match app_type {
|
|
|
|
|
|
AppType::Codex => {
|
|
|
|
|
|
// 备份当前配置(如果存在)
|
|
|
|
|
|
if !manager.current.is_empty() {
|
|
|
|
|
|
if let Some(current_provider) = manager.providers.get(&manager.current) {
|
|
|
|
|
|
codex_config::backup_codex_config(&manager.current, ¤t_provider.name)?;
|
|
|
|
|
|
log::info!("已备份当前 Codex 供应商配置: {}", current_provider.name);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 恢复目标供应商配置
|
|
|
|
|
|
codex_config::restore_codex_provider_config(&id, &provider.name)?;
|
|
|
|
|
|
}
|
|
|
|
|
|
AppType::Claude => {
|
|
|
|
|
|
// 使用原有的 Claude 切换逻辑
|
|
|
|
|
|
use crate::config::{
|
|
|
|
|
|
backup_config, copy_file, get_claude_settings_path, get_provider_config_path,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
let settings_path = get_claude_settings_path();
|
|
|
|
|
|
let provider_config_path = get_provider_config_path(&id, Some(&provider.name));
|
|
|
|
|
|
|
|
|
|
|
|
// 检查供应商配置文件是否存在
|
|
|
|
|
|
if !provider_config_path.exists() {
|
|
|
|
|
|
return Err(format!(
|
|
|
|
|
|
"供应商配置文件不存在: {}",
|
|
|
|
|
|
provider_config_path.display()
|
|
|
|
|
|
));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 如果当前有配置,先备份到当前供应商
|
|
|
|
|
|
if settings_path.exists() && !manager.current.is_empty() {
|
|
|
|
|
|
if let Some(current_provider) = manager.providers.get(&manager.current) {
|
|
|
|
|
|
let current_provider_path =
|
|
|
|
|
|
get_provider_config_path(&manager.current, Some(¤t_provider.name));
|
|
|
|
|
|
backup_config(&settings_path, ¤t_provider_path)?;
|
|
|
|
|
|
log::info!("已备份当前供应商配置: {}", current_provider.name);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 确保主配置父目录存在
|
|
|
|
|
|
if let Some(parent) = settings_path.parent() {
|
|
|
|
|
|
std::fs::create_dir_all(parent).map_err(|e| format!("创建目录失败: {}", e))?;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 复制新供应商配置到主配置
|
|
|
|
|
|
copy_file(&provider_config_path, &settings_path)?;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新当前供应商
|
|
|
|
|
|
manager.current = id;
|
|
|
|
|
|
|
|
|
|
|
|
log::info!("成功切换到供应商: {}", provider.name);
|
2025-08-27 11:00:53 +08:00
|
|
|
|
|
2025-08-23 20:15:10 +08:00
|
|
|
|
// 保存配置
|
2025-08-30 21:54:11 +08:00
|
|
|
|
drop(config); // 释放锁
|
2025-08-23 20:15:10 +08:00
|
|
|
|
state.save()?;
|
2025-08-27 11:00:53 +08:00
|
|
|
|
|
2025-08-23 20:15:10 +08:00
|
|
|
|
Ok(true)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 导入当前配置为默认供应商
|
|
|
|
|
|
#[tauri::command]
|
2025-08-30 21:54:11 +08:00
|
|
|
|
pub async fn import_default_config(
|
|
|
|
|
|
state: State<'_, AppState>,
|
2025-08-31 16:43:33 +08:00
|
|
|
|
app_type: Option<AppType>,
|
2025-08-30 21:54:11 +08:00
|
|
|
|
app: Option<String>,
|
2025-08-31 16:43:33 +08:00
|
|
|
|
appType: Option<String>,
|
2025-08-30 21:54:11 +08:00
|
|
|
|
) -> Result<bool, String> {
|
2025-08-31 16:43:33 +08:00
|
|
|
|
let app_type = app_type
|
|
|
|
|
|
.or_else(|| app.as_deref().map(|s| s.into()))
|
|
|
|
|
|
.or_else(|| appType.as_deref().map(|s| s.into()))
|
|
|
|
|
|
.unwrap_or(AppType::Claude);
|
2025-08-30 21:54:11 +08:00
|
|
|
|
|
2025-08-24 23:30:35 +08:00
|
|
|
|
// 若已存在 default 供应商,则直接返回,避免重复导入
|
|
|
|
|
|
{
|
2025-08-30 21:54:11 +08:00
|
|
|
|
let config = state
|
|
|
|
|
|
.config
|
2025-08-24 23:30:35 +08:00
|
|
|
|
.lock()
|
|
|
|
|
|
.map_err(|e| format!("获取锁失败: {}", e))?;
|
2025-08-30 21:54:11 +08:00
|
|
|
|
|
|
|
|
|
|
if let Some(manager) = config.get_manager(&app_type) {
|
|
|
|
|
|
if manager.get_all_providers().contains_key("default") {
|
|
|
|
|
|
return Ok(true);
|
|
|
|
|
|
}
|
2025-08-24 23:30:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-30 21:54:11 +08:00
|
|
|
|
// 根据应用类型导入配置
|
|
|
|
|
|
let settings_config = match app_type {
|
|
|
|
|
|
AppType::Codex => codex_config::import_current_codex_config()?,
|
|
|
|
|
|
AppType::Claude => import_current_config_as_default()?,
|
|
|
|
|
|
};
|
2025-08-27 11:00:53 +08:00
|
|
|
|
|
2025-08-23 20:15:10 +08:00
|
|
|
|
// 创建默认供应商
|
|
|
|
|
|
let provider = Provider::with_id(
|
|
|
|
|
|
"default".to_string(),
|
|
|
|
|
|
"default".to_string(),
|
|
|
|
|
|
settings_config,
|
|
|
|
|
|
None,
|
|
|
|
|
|
);
|
2025-08-27 11:00:53 +08:00
|
|
|
|
|
2025-08-23 20:15:10 +08:00
|
|
|
|
// 添加到管理器
|
2025-08-30 21:54:11 +08:00
|
|
|
|
let mut config = state
|
|
|
|
|
|
.config
|
2025-08-27 11:00:53 +08:00
|
|
|
|
.lock()
|
2025-08-23 20:15:10 +08:00
|
|
|
|
.map_err(|e| format!("获取锁失败: {}", e))?;
|
2025-08-27 11:00:53 +08:00
|
|
|
|
|
2025-08-30 21:54:11 +08:00
|
|
|
|
let manager = config
|
|
|
|
|
|
.get_manager_mut(&app_type)
|
|
|
|
|
|
.ok_or_else(|| format!("应用类型不存在: {:?}", app_type))?;
|
|
|
|
|
|
|
|
|
|
|
|
// 根据应用类型保存配置文件
|
|
|
|
|
|
match app_type {
|
|
|
|
|
|
AppType::Codex => {
|
|
|
|
|
|
codex_config::save_codex_provider_config(
|
|
|
|
|
|
&provider.id,
|
|
|
|
|
|
&provider.name,
|
|
|
|
|
|
&provider.settings_config,
|
|
|
|
|
|
)?;
|
|
|
|
|
|
}
|
|
|
|
|
|
AppType::Claude => {
|
|
|
|
|
|
use crate::config::{get_provider_config_path, write_json_file};
|
|
|
|
|
|
let config_path = get_provider_config_path(&provider.id, Some(&provider.name));
|
|
|
|
|
|
write_json_file(&config_path, &provider.settings_config)?;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
manager.providers.insert(provider.id.clone(), provider);
|
2025-08-27 11:00:53 +08:00
|
|
|
|
|
2025-08-23 20:15:10 +08:00
|
|
|
|
// 如果没有当前供应商,设置为 default
|
|
|
|
|
|
if manager.current.is_empty() {
|
|
|
|
|
|
manager.current = "default".to_string();
|
|
|
|
|
|
}
|
2025-08-27 11:00:53 +08:00
|
|
|
|
|
2025-08-23 20:15:10 +08:00
|
|
|
|
// 保存配置
|
2025-08-30 21:54:11 +08:00
|
|
|
|
drop(config); // 释放锁
|
2025-08-23 20:15:10 +08:00
|
|
|
|
state.save()?;
|
2025-08-27 11:00:53 +08:00
|
|
|
|
|
2025-08-23 20:15:10 +08:00
|
|
|
|
Ok(true)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 获取 Claude Code 配置状态
|
|
|
|
|
|
#[tauri::command]
|
|
|
|
|
|
pub async fn get_claude_config_status() -> Result<ConfigStatus, String> {
|
2025-08-23 21:00:50 +08:00
|
|
|
|
Ok(crate::config::get_claude_config_status())
|
2025-08-23 20:15:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-30 21:54:11 +08:00
|
|
|
|
/// 获取应用配置状态(通用)
|
2025-08-31 16:39:38 +08:00
|
|
|
|
/// 兼容两种参数:`app_type`(推荐)或 `app`(字符串)
|
2025-08-30 21:54:11 +08:00
|
|
|
|
#[tauri::command]
|
2025-08-31 16:39:38 +08:00
|
|
|
|
pub async fn get_config_status(
|
|
|
|
|
|
app_type: Option<AppType>,
|
|
|
|
|
|
app: Option<String>,
|
|
|
|
|
|
appType: Option<String>,
|
|
|
|
|
|
) -> Result<ConfigStatus, String> {
|
|
|
|
|
|
let app = app_type
|
|
|
|
|
|
.or_else(|| app.as_deref().map(|s| s.into()))
|
|
|
|
|
|
.or_else(|| appType.as_deref().map(|s| s.into()))
|
|
|
|
|
|
.unwrap_or(AppType::Claude);
|
|
|
|
|
|
|
2025-08-30 21:54:11 +08:00
|
|
|
|
match app {
|
|
|
|
|
|
AppType::Claude => Ok(crate::config::get_claude_config_status()),
|
|
|
|
|
|
AppType::Codex => {
|
2025-08-31 16:39:38 +08:00
|
|
|
|
use crate::codex_config::{get_codex_auth_path, get_codex_config_dir};
|
2025-08-30 21:54:11 +08:00
|
|
|
|
let auth_path = get_codex_auth_path();
|
2025-08-31 16:39:38 +08:00
|
|
|
|
|
|
|
|
|
|
// 放宽:只要 auth.json 存在即可认为已配置;config.toml 允许为空
|
|
|
|
|
|
let exists = auth_path.exists();
|
|
|
|
|
|
let path = get_codex_config_dir().to_string_lossy().to_string();
|
|
|
|
|
|
|
2025-08-30 21:54:11 +08:00
|
|
|
|
Ok(ConfigStatus { exists, path })
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-23 20:15:10 +08:00
|
|
|
|
/// 获取 Claude Code 配置文件路径
|
|
|
|
|
|
#[tauri::command]
|
|
|
|
|
|
pub async fn get_claude_code_config_path() -> Result<String, String> {
|
|
|
|
|
|
Ok(get_claude_settings_path().to_string_lossy().to_string())
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 打开配置文件夹
|
2025-08-31 16:39:38 +08:00
|
|
|
|
/// 兼容两种参数:`app_type`(推荐)或 `app`(字符串)
|
2025-08-23 20:15:10 +08:00
|
|
|
|
#[tauri::command]
|
2025-08-31 16:39:38 +08:00
|
|
|
|
pub async fn open_config_folder(
|
|
|
|
|
|
app: tauri::AppHandle,
|
|
|
|
|
|
app_type: Option<AppType>,
|
|
|
|
|
|
app_str: Option<String>,
|
|
|
|
|
|
appType: Option<String>,
|
|
|
|
|
|
) -> Result<bool, String> {
|
|
|
|
|
|
let app_type = app_type
|
|
|
|
|
|
.or_else(|| app_str.as_deref().map(|s| s.into()))
|
|
|
|
|
|
.or_else(|| appType.as_deref().map(|s| s.into()))
|
|
|
|
|
|
.unwrap_or(AppType::Claude);
|
2025-08-30 21:54:11 +08:00
|
|
|
|
|
|
|
|
|
|
let config_dir = match app_type {
|
|
|
|
|
|
AppType::Claude => crate::config::get_claude_config_dir(),
|
|
|
|
|
|
AppType::Codex => crate::codex_config::get_codex_config_dir(),
|
|
|
|
|
|
};
|
2025-08-27 11:00:53 +08:00
|
|
|
|
|
2025-08-23 20:15:10 +08:00
|
|
|
|
// 确保目录存在
|
|
|
|
|
|
if !config_dir.exists() {
|
2025-08-27 11:00:53 +08:00
|
|
|
|
std::fs::create_dir_all(&config_dir).map_err(|e| format!("创建目录失败: {}", e))?;
|
2025-08-23 20:15:10 +08:00
|
|
|
|
}
|
2025-08-27 11:00:53 +08:00
|
|
|
|
|
2025-08-25 20:16:29 +08:00
|
|
|
|
// 使用 opener 插件打开文件夹
|
|
|
|
|
|
app.opener()
|
|
|
|
|
|
.open_path(config_dir.to_string_lossy().to_string(), None::<String>)
|
|
|
|
|
|
.map_err(|e| format!("打开文件夹失败: {}", e))?;
|
2025-08-27 11:00:53 +08:00
|
|
|
|
|
2025-08-23 20:15:10 +08:00
|
|
|
|
Ok(true)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 打开外部链接
|
|
|
|
|
|
#[tauri::command]
|
2025-08-25 20:16:29 +08:00
|
|
|
|
pub async fn open_external(app: tauri::AppHandle, url: String) -> Result<bool, String> {
|
2025-08-24 23:35:07 +08:00
|
|
|
|
// 规范化 URL,缺少协议时默认加 https://
|
|
|
|
|
|
let url = if url.starts_with("http://") || url.starts_with("https://") {
|
|
|
|
|
|
url
|
|
|
|
|
|
} else {
|
|
|
|
|
|
format!("https://{}", url)
|
|
|
|
|
|
};
|
2025-08-27 11:00:53 +08:00
|
|
|
|
|
2025-08-25 20:16:29 +08:00
|
|
|
|
// 使用 opener 插件打开链接
|
|
|
|
|
|
app.opener()
|
|
|
|
|
|
.open_url(&url, None::<String>)
|
|
|
|
|
|
.map_err(|e| format!("打开链接失败: {}", e))?;
|
2025-08-27 11:00:53 +08:00
|
|
|
|
|
2025-08-23 20:15:10 +08:00
|
|
|
|
Ok(true)
|
2025-08-24 23:30:35 +08:00
|
|
|
|
}
|