mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-28 08:44:41 +08:00
61bfc29d82
Co-authored-by: liyang <liyang25@pku.edu.cn>
172 lines
5.1 KiB
Rust
172 lines
5.1 KiB
Rust
//! 故障转移队列命令
|
|
//!
|
|
//! 管理代理模式下的故障转移队列(基于 providers 表的 in_failover_queue 字段)
|
|
|
|
use crate::database::FailoverQueueItem;
|
|
use crate::provider::Provider;
|
|
use crate::store::AppState;
|
|
use std::str::FromStr;
|
|
use tauri::Emitter;
|
|
|
|
/// 获取故障转移队列
|
|
#[tauri::command]
|
|
pub async fn get_failover_queue(
|
|
state: tauri::State<'_, AppState>,
|
|
app_type: String,
|
|
) -> Result<Vec<FailoverQueueItem>, String> {
|
|
state
|
|
.db
|
|
.get_failover_queue(&app_type)
|
|
.map_err(|e| e.to_string())
|
|
}
|
|
|
|
/// 获取可添加到故障转移队列的供应商(不在队列中的)
|
|
#[tauri::command]
|
|
pub async fn get_available_providers_for_failover(
|
|
state: tauri::State<'_, AppState>,
|
|
app_type: String,
|
|
) -> Result<Vec<Provider>, String> {
|
|
state
|
|
.db
|
|
.get_available_providers_for_failover(&app_type)
|
|
.map_err(|e| e.to_string())
|
|
}
|
|
|
|
/// 添加供应商到故障转移队列
|
|
#[tauri::command]
|
|
pub async fn add_to_failover_queue(
|
|
state: tauri::State<'_, AppState>,
|
|
app_type: String,
|
|
provider_id: String,
|
|
) -> Result<(), String> {
|
|
state
|
|
.db
|
|
.add_to_failover_queue(&app_type, &provider_id)
|
|
.map_err(|e| e.to_string())
|
|
}
|
|
|
|
/// 从故障转移队列移除供应商
|
|
#[tauri::command]
|
|
pub async fn remove_from_failover_queue(
|
|
state: tauri::State<'_, AppState>,
|
|
app_type: String,
|
|
provider_id: String,
|
|
) -> Result<(), String> {
|
|
state
|
|
.db
|
|
.remove_from_failover_queue(&app_type, &provider_id)
|
|
.map_err(|e| e.to_string())
|
|
}
|
|
|
|
/// 获取指定应用的自动故障转移开关状态(从 proxy_config 表读取)
|
|
#[tauri::command]
|
|
pub async fn get_auto_failover_enabled(
|
|
state: tauri::State<'_, AppState>,
|
|
app_type: String,
|
|
) -> Result<bool, String> {
|
|
state
|
|
.db
|
|
.get_proxy_config_for_app(&app_type)
|
|
.await
|
|
.map(|config| config.auto_failover_enabled)
|
|
.map_err(|e| e.to_string())
|
|
}
|
|
|
|
/// 设置指定应用的自动故障转移开关状态(写入 proxy_config 表)
|
|
///
|
|
/// 注意:关闭故障转移时不会清除队列,队列内容会保留供下次开启时使用
|
|
#[tauri::command]
|
|
pub async fn set_auto_failover_enabled(
|
|
app: tauri::AppHandle,
|
|
state: tauri::State<'_, AppState>,
|
|
app_type: String,
|
|
enabled: bool,
|
|
) -> Result<(), String> {
|
|
log::info!(
|
|
"[Failover] Setting auto_failover_enabled: app_type='{app_type}', enabled={enabled}"
|
|
);
|
|
|
|
// 强一致语义:开启故障转移后立即切到队列 P1(并确保队列非空)
|
|
//
|
|
// 说明:
|
|
// - 仅在 enabled=true 时执行“切到 P1”
|
|
// - 若队列为空,则尝试把“当前供应商”自动加入队列作为 P1,避免用户在 UI 上陷入死锁(无法先加队列再开启)
|
|
let p1_provider_id = if enabled {
|
|
let mut queue = state
|
|
.db
|
|
.get_failover_queue(&app_type)
|
|
.map_err(|e| e.to_string())?;
|
|
|
|
if queue.is_empty() {
|
|
let app_enum = crate::app_config::AppType::from_str(&app_type)
|
|
.map_err(|_| format!("无效的应用类型: {app_type}"))?;
|
|
|
|
let current_id = crate::settings::get_effective_current_provider(&state.db, &app_enum)
|
|
.map_err(|e| e.to_string())?;
|
|
|
|
let Some(current_id) = current_id else {
|
|
return Err("故障转移队列为空,且未设置当前供应商,无法开启故障转移".to_string());
|
|
};
|
|
|
|
state
|
|
.db
|
|
.add_to_failover_queue(&app_type, ¤t_id)
|
|
.map_err(|e| e.to_string())?;
|
|
|
|
queue = state
|
|
.db
|
|
.get_failover_queue(&app_type)
|
|
.map_err(|e| e.to_string())?;
|
|
}
|
|
|
|
queue
|
|
.first()
|
|
.map(|item| item.provider_id.clone())
|
|
.ok_or_else(|| "故障转移队列为空,无法开启故障转移".to_string())?
|
|
} else {
|
|
String::new()
|
|
};
|
|
|
|
// 读取当前配置
|
|
let mut config = state
|
|
.db
|
|
.get_proxy_config_for_app(&app_type)
|
|
.await
|
|
.map_err(|e| e.to_string())?;
|
|
|
|
// 更新 auto_failover_enabled 字段
|
|
config.auto_failover_enabled = enabled;
|
|
|
|
// 写回数据库
|
|
state
|
|
.db
|
|
.update_proxy_config_for_app(config)
|
|
.await
|
|
.map_err(|e| e.to_string())?;
|
|
|
|
// 开启后立即切到 P1:更新 is_current + 本地 settings + Live 备份(接管模式下)
|
|
if enabled {
|
|
state
|
|
.proxy_service
|
|
.switch_proxy_target(&app_type, &p1_provider_id)
|
|
.await?;
|
|
|
|
// 发射 provider-switched 事件(让前端刷新当前供应商)
|
|
let event_data = serde_json::json!({
|
|
"appType": app_type,
|
|
"providerId": p1_provider_id,
|
|
"source": "failoverEnabled"
|
|
});
|
|
let _ = app.emit("provider-switched", event_data);
|
|
}
|
|
|
|
// 刷新托盘菜单,确保状态同步
|
|
if let Ok(new_menu) = crate::tray::create_tray_menu(&app, &state) {
|
|
if let Some(tray) = app.tray_by_id(crate::tray::TRAY_ID) {
|
|
let _ = tray.set_menu(Some(new_menu));
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|