185 lines
6.1 KiB
Rust
185 lines
6.1 KiB
Rust
|
|
use serde_json::{json, Value};
|
||
|
|
|
||
|
|
use crate::app_config::{AppType, MultiAppConfig};
|
||
|
|
use crate::config::{get_claude_settings_path, read_json_file, write_json_file};
|
||
|
|
use crate::codex_config::{get_codex_auth_path, get_codex_config_path, write_codex_live_atomic};
|
||
|
|
use crate::error::AppError;
|
||
|
|
use crate::mcp;
|
||
|
|
|
||
|
|
/// 供应商相关业务逻辑
|
||
|
|
pub struct ProviderService;
|
||
|
|
|
||
|
|
impl ProviderService {
|
||
|
|
/// 切换指定应用的供应商
|
||
|
|
pub fn switch(
|
||
|
|
config: &mut MultiAppConfig,
|
||
|
|
app_type: AppType,
|
||
|
|
provider_id: &str,
|
||
|
|
) -> Result<(), AppError> {
|
||
|
|
match app_type {
|
||
|
|
AppType::Codex => Self::switch_codex(config, provider_id),
|
||
|
|
AppType::Claude => Self::switch_claude(config, provider_id),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
fn switch_codex(config: &mut MultiAppConfig, provider_id: &str) -> Result<(), AppError> {
|
||
|
|
let provider = config
|
||
|
|
.get_manager(&AppType::Codex)
|
||
|
|
.ok_or_else(|| AppError::Message("应用类型不存在: Codex".into()))?
|
||
|
|
.providers
|
||
|
|
.get(provider_id)
|
||
|
|
.cloned()
|
||
|
|
.ok_or_else(|| AppError::ProviderNotFound(provider_id.to_string()))?;
|
||
|
|
|
||
|
|
Self::backfill_codex_current(config, provider_id)?;
|
||
|
|
Self::write_codex_live(&provider)?;
|
||
|
|
|
||
|
|
if let Some(manager) = config.get_manager_mut(&AppType::Codex) {
|
||
|
|
manager.current = provider_id.to_string();
|
||
|
|
}
|
||
|
|
|
||
|
|
// 同步启用的 MCP 服务器
|
||
|
|
mcp::sync_enabled_to_codex(config)?;
|
||
|
|
|
||
|
|
// 更新持久化快照
|
||
|
|
let cfg_text_after = crate::codex_config::read_and_validate_codex_config_text()?;
|
||
|
|
if let Some(manager) = config.get_manager_mut(&AppType::Codex) {
|
||
|
|
if let Some(target) = manager.providers.get_mut(provider_id) {
|
||
|
|
if let Some(obj) = target.settings_config.as_object_mut() {
|
||
|
|
obj.insert(
|
||
|
|
"config".to_string(),
|
||
|
|
Value::String(cfg_text_after),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Ok(())
|
||
|
|
}
|
||
|
|
|
||
|
|
fn backfill_codex_current(
|
||
|
|
config: &mut MultiAppConfig,
|
||
|
|
next_provider: &str,
|
||
|
|
) -> Result<(), AppError> {
|
||
|
|
let current_id = config
|
||
|
|
.get_manager(&AppType::Codex)
|
||
|
|
.map(|m| m.current.clone())
|
||
|
|
.unwrap_or_default();
|
||
|
|
|
||
|
|
if current_id.is_empty() || current_id == next_provider {
|
||
|
|
return Ok(());
|
||
|
|
}
|
||
|
|
|
||
|
|
let auth_path = get_codex_auth_path();
|
||
|
|
if !auth_path.exists() {
|
||
|
|
return Ok(());
|
||
|
|
}
|
||
|
|
|
||
|
|
let auth: Value = read_json_file(&auth_path)?;
|
||
|
|
let config_path = get_codex_config_path();
|
||
|
|
let config_text = if config_path.exists() {
|
||
|
|
std::fs::read_to_string(&config_path).map_err(|e| AppError::io(&config_path, e))?
|
||
|
|
} else {
|
||
|
|
String::new()
|
||
|
|
};
|
||
|
|
|
||
|
|
let live = json!({
|
||
|
|
"auth": auth,
|
||
|
|
"config": config_text,
|
||
|
|
});
|
||
|
|
|
||
|
|
if let Some(manager) = config.get_manager_mut(&AppType::Codex) {
|
||
|
|
if let Some(current) = manager.providers.get_mut(¤t_id) {
|
||
|
|
current.settings_config = live;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Ok(())
|
||
|
|
}
|
||
|
|
|
||
|
|
fn write_codex_live(provider: &crate::provider::Provider) -> Result<(), AppError> {
|
||
|
|
let settings = provider
|
||
|
|
.settings_config
|
||
|
|
.as_object()
|
||
|
|
.ok_or_else(|| AppError::Config("Codex 配置必须是 JSON 对象".into()))?;
|
||
|
|
let auth = settings.get("auth").ok_or_else(|| {
|
||
|
|
AppError::Config(format!("供应商 {} 缺少 auth 配置", provider.id))
|
||
|
|
})?;
|
||
|
|
if !auth.is_object() {
|
||
|
|
return Err(AppError::Config(format!(
|
||
|
|
"供应商 {} 的 auth 必须是对象",
|
||
|
|
provider.id
|
||
|
|
)));
|
||
|
|
}
|
||
|
|
let cfg_text = settings.get("config").and_then(Value::as_str);
|
||
|
|
|
||
|
|
write_codex_live_atomic(auth, cfg_text)?;
|
||
|
|
Ok(())
|
||
|
|
}
|
||
|
|
|
||
|
|
fn switch_claude(config: &mut MultiAppConfig, provider_id: &str) -> Result<(), AppError> {
|
||
|
|
let provider = {
|
||
|
|
let manager = config
|
||
|
|
.get_manager(&AppType::Claude)
|
||
|
|
.ok_or_else(|| AppError::Message("应用类型不存在: Claude".into()))?;
|
||
|
|
manager
|
||
|
|
.providers
|
||
|
|
.get(provider_id)
|
||
|
|
.cloned()
|
||
|
|
.ok_or_else(|| AppError::ProviderNotFound(provider_id.to_string()))?
|
||
|
|
};
|
||
|
|
|
||
|
|
Self::backfill_claude_current(config, provider_id)?;
|
||
|
|
Self::write_claude_live(&provider)?;
|
||
|
|
|
||
|
|
if let Some(manager) = config.get_manager_mut(&AppType::Claude) {
|
||
|
|
manager.current = provider_id.to_string();
|
||
|
|
|
||
|
|
if let Some(target) = manager.providers.get_mut(provider_id) {
|
||
|
|
let settings_path = get_claude_settings_path();
|
||
|
|
let live_after = read_json_file::<Value>(&settings_path)?;
|
||
|
|
target.settings_config = live_after;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Ok(())
|
||
|
|
}
|
||
|
|
|
||
|
|
fn backfill_claude_current(
|
||
|
|
config: &mut MultiAppConfig,
|
||
|
|
next_provider: &str,
|
||
|
|
) -> Result<(), AppError> {
|
||
|
|
let settings_path = get_claude_settings_path();
|
||
|
|
if !settings_path.exists() {
|
||
|
|
return Ok(());
|
||
|
|
}
|
||
|
|
|
||
|
|
let current_id = config
|
||
|
|
.get_manager(&AppType::Claude)
|
||
|
|
.map(|m| m.current.clone())
|
||
|
|
.unwrap_or_default();
|
||
|
|
if current_id.is_empty() || current_id == next_provider {
|
||
|
|
return Ok(());
|
||
|
|
}
|
||
|
|
|
||
|
|
let live = read_json_file::<Value>(&settings_path)?;
|
||
|
|
if let Some(manager) = config.get_manager_mut(&AppType::Claude) {
|
||
|
|
if let Some(current) = manager.providers.get_mut(¤t_id) {
|
||
|
|
current.settings_config = live;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Ok(())
|
||
|
|
}
|
||
|
|
|
||
|
|
fn write_claude_live(provider: &crate::provider::Provider) -> Result<(), AppError> {
|
||
|
|
let settings_path = get_claude_settings_path();
|
||
|
|
if let Some(parent) = settings_path.parent() {
|
||
|
|
std::fs::create_dir_all(parent).map_err(|e| AppError::io(parent, e))?;
|
||
|
|
}
|
||
|
|
|
||
|
|
write_json_file(&settings_path, &provider.settings_config)?;
|
||
|
|
Ok(())
|
||
|
|
}
|
||
|
|
}
|