| use crate::models::{Account, AppConfig, QuotaData}; |
| use crate::modules; |
| use tauri::{Emitter, Manager}; |
| use tauri_plugin_opener::OpenerExt; |
|
|
| |
| pub mod proxy; |
| |
| pub mod autostart; |
| |
| pub mod cloudflared; |
| |
| pub mod security; |
| |
| pub mod proxy_pool; |
| |
| pub mod user_token; |
|
|
| |
| #[tauri::command] |
| pub async fn list_accounts() -> Result<Vec<Account>, String> { |
| modules::list_accounts() |
| } |
|
|
| |
| #[tauri::command] |
| pub async fn add_account( |
| app: tauri::AppHandle, |
| _email: String, |
| refresh_token: String, |
| ) -> Result<Account, String> { |
| let service = modules::account_service::AccountService::new( |
| crate::modules::integration::SystemManager::Desktop(app.clone()), |
| ); |
|
|
| let mut account = service.add_account(&refresh_token).await?; |
|
|
| |
| let _ = internal_refresh_account_quota(&app, &mut account).await; |
|
|
| |
| let _ = crate::commands::proxy::reload_proxy_accounts( |
| app.state::<crate::commands::proxy::ProxyServiceState>(), |
| ) |
| .await; |
|
|
| Ok(account) |
| } |
|
|
| |
| |
| #[tauri::command] |
| pub async fn delete_account( |
| app: tauri::AppHandle, |
| proxy_state: tauri::State<'_, crate::commands::proxy::ProxyServiceState>, |
| account_id: String, |
| ) -> Result<(), String> { |
| let service = modules::account_service::AccountService::new( |
| crate::modules::integration::SystemManager::Desktop(app.clone()), |
| ); |
| service.delete_account(&account_id)?; |
|
|
| |
| let _ = crate::commands::proxy::reload_proxy_accounts(proxy_state).await; |
|
|
| Ok(()) |
| } |
|
|
| |
| #[tauri::command] |
| pub async fn delete_accounts( |
| app: tauri::AppHandle, |
| proxy_state: tauri::State<'_, crate::commands::proxy::ProxyServiceState>, |
| account_ids: Vec<String>, |
| ) -> Result<(), String> { |
| modules::logger::log_info(&format!( |
| "收到批量删除请求,共 {} 个账号", |
| account_ids.len() |
| )); |
| modules::account::delete_accounts(&account_ids).map_err(|e| { |
| modules::logger::log_error(&format!("批量删除失败: {}", e)); |
| e |
| })?; |
|
|
| |
| crate::modules::tray::update_tray_menus(&app); |
|
|
| |
| let _ = crate::commands::proxy::reload_proxy_accounts(proxy_state).await; |
|
|
| Ok(()) |
| } |
|
|
| |
| |
| #[tauri::command] |
| pub async fn reorder_accounts( |
| proxy_state: tauri::State<'_, crate::commands::proxy::ProxyServiceState>, |
| account_ids: Vec<String>, |
| ) -> Result<(), String> { |
| modules::logger::log_info(&format!( |
| "收到账号重排序请求,共 {} 个账号", |
| account_ids.len() |
| )); |
| modules::account::reorder_accounts(&account_ids).map_err(|e| { |
| modules::logger::log_error(&format!("账号重排序失败: {}", e)); |
| e |
| })?; |
|
|
| |
| let _ = crate::commands::proxy::reload_proxy_accounts(proxy_state).await; |
| Ok(()) |
| } |
|
|
| |
| #[tauri::command] |
| pub async fn switch_account( |
| app: tauri::AppHandle, |
| proxy_state: tauri::State<'_, crate::commands::proxy::ProxyServiceState>, |
| account_id: String, |
| ) -> Result<(), String> { |
| let service = modules::account_service::AccountService::new( |
| crate::modules::integration::SystemManager::Desktop(app.clone()), |
| ); |
|
|
| service.switch_account(&account_id).await?; |
|
|
| |
| crate::modules::tray::update_tray_menus(&app); |
|
|
| |
| let _ = crate::commands::proxy::reload_proxy_accounts(proxy_state).await; |
|
|
| Ok(()) |
| } |
|
|
| |
| #[tauri::command] |
| pub async fn get_current_account() -> Result<Option<Account>, String> { |
| |
| |
| modules::logger::log_info("Backend Command: get_current_account called"); |
|
|
| let account_id = modules::get_current_account_id()?; |
|
|
| if let Some(id) = account_id { |
| |
| modules::load_account(&id).map(Some) |
| } else { |
| modules::logger::log_info(" No current account set"); |
| Ok(None) |
| } |
| } |
|
|
| |
| use crate::models::AccountExportResponse; |
|
|
| #[tauri::command] |
| pub async fn export_accounts(account_ids: Vec<String>) -> Result<AccountExportResponse, String> { |
| modules::account::export_accounts_by_ids(&account_ids) |
| } |
|
|
| |
| async fn internal_refresh_account_quota( |
| app: &tauri::AppHandle, |
| account: &mut Account, |
| ) -> Result<QuotaData, String> { |
| modules::logger::log_info(&format!("自动触发刷新配额: {}", account.email)); |
|
|
| |
| match modules::account::fetch_quota_with_retry(account).await { |
| Ok(quota) => { |
| |
| let _ = modules::update_account_quota(&account.id, quota.clone()); |
| |
| crate::modules::tray::update_tray_menus(app); |
| Ok(quota) |
| } |
| Err(e) => { |
| modules::logger::log_warn(&format!("自动刷新配额失败 ({}): {}", account.email, e)); |
| Err(e.to_string()) |
| } |
| } |
| } |
|
|
| |
| #[tauri::command] |
| pub async fn fetch_account_quota( |
| app: tauri::AppHandle, |
| proxy_state: tauri::State<'_, crate::commands::proxy::ProxyServiceState>, |
| account_id: String, |
| ) -> crate::error::AppResult<QuotaData> { |
| modules::logger::log_info(&format!("手动刷新配额请求: {}", account_id)); |
| let mut account = |
| modules::load_account(&account_id).map_err(crate::error::AppError::Account)?; |
|
|
| |
| let quota = modules::account::fetch_quota_with_retry(&mut account).await?; |
|
|
| |
| modules::update_account_quota(&account_id, quota.clone()) |
| .map_err(crate::error::AppError::Account)?; |
|
|
| crate::modules::tray::update_tray_menus(&app); |
|
|
| |
| let instance_lock = proxy_state.instance.read().await; |
| if let Some(instance) = instance_lock.as_ref() { |
| let _ = instance.token_manager.reload_account(&account_id).await; |
| } |
|
|
| Ok(quota) |
| } |
|
|
| pub use modules::account::RefreshStats; |
|
|
| |
| pub async fn refresh_all_quotas_internal( |
| proxy_state: &crate::commands::proxy::ProxyServiceState, |
| app_handle: Option<tauri::AppHandle>, |
| ) -> Result<RefreshStats, String> { |
| let stats = modules::account::refresh_all_quotas_logic().await?; |
|
|
| |
| let instance_lock = proxy_state.instance.read().await; |
| if let Some(instance) = instance_lock.as_ref() { |
| let _ = instance.token_manager.reload_all_accounts().await; |
| } |
|
|
| |
| if let Some(handle) = app_handle { |
| use tauri::Emitter; |
| let _ = handle.emit("accounts://refreshed", ()); |
| } |
|
|
| Ok(stats) |
| } |
|
|
| |
| #[tauri::command] |
| pub async fn refresh_all_quotas( |
| proxy_state: tauri::State<'_, crate::commands::proxy::ProxyServiceState>, |
| app_handle: tauri::AppHandle, |
| ) -> Result<RefreshStats, String> { |
| refresh_all_quotas_internal(&proxy_state, Some(app_handle)).await |
| } |
| |
| #[tauri::command] |
| pub async fn get_device_profiles( |
| account_id: String, |
| ) -> Result<modules::account::DeviceProfiles, String> { |
| modules::get_device_profiles(&account_id) |
| } |
|
|
| |
| #[tauri::command] |
| pub async fn bind_device_profile( |
| account_id: String, |
| mode: String, |
| ) -> Result<crate::models::DeviceProfile, String> { |
| modules::bind_device_profile(&account_id, &mode) |
| } |
|
|
| |
| #[tauri::command] |
| pub async fn preview_generate_profile() -> Result<crate::models::DeviceProfile, String> { |
| Ok(crate::modules::device::generate_profile()) |
| } |
|
|
| |
| #[tauri::command] |
| pub async fn bind_device_profile_with_profile( |
| account_id: String, |
| profile: crate::models::DeviceProfile, |
| ) -> Result<crate::models::DeviceProfile, String> { |
| modules::bind_device_profile_with_profile(&account_id, profile, Some("generated".to_string())) |
| } |
|
|
| |
| #[tauri::command] |
| pub async fn apply_device_profile( |
| account_id: String, |
| ) -> Result<crate::models::DeviceProfile, String> { |
| modules::apply_device_profile(&account_id) |
| } |
|
|
| |
| #[tauri::command] |
| pub async fn restore_original_device() -> Result<String, String> { |
| modules::restore_original_device() |
| } |
|
|
| |
| #[tauri::command] |
| pub async fn list_device_versions( |
| account_id: String, |
| ) -> Result<modules::account::DeviceProfiles, String> { |
| modules::list_device_versions(&account_id) |
| } |
|
|
| |
| #[tauri::command] |
| pub async fn restore_device_version( |
| account_id: String, |
| version_id: String, |
| ) -> Result<crate::models::DeviceProfile, String> { |
| modules::restore_device_version(&account_id, &version_id) |
| } |
|
|
| |
| #[tauri::command] |
| pub async fn delete_device_version(account_id: String, version_id: String) -> Result<(), String> { |
| modules::delete_device_version(&account_id, &version_id) |
| } |
|
|
| |
| #[tauri::command] |
| pub async fn open_device_folder(app: tauri::AppHandle) -> Result<(), String> { |
| let dir = modules::device::get_storage_dir()?; |
| let dir_str = dir |
| .to_str() |
| .ok_or("无法解析存储目录路径为字符串")? |
| .to_string(); |
| app.opener() |
| .open_path(dir_str, None::<&str>) |
| .map_err(|e| format!("打开目录失败: {}", e)) |
| } |
|
|
| |
| #[tauri::command] |
| pub async fn load_config() -> Result<AppConfig, String> { |
| modules::load_app_config() |
| } |
|
|
| |
| #[tauri::command] |
| pub async fn save_config( |
| app: tauri::AppHandle, |
| proxy_state: tauri::State<'_, crate::commands::proxy::ProxyServiceState>, |
| config: AppConfig, |
| ) -> Result<(), String> { |
| modules::save_app_config(&config)?; |
|
|
| |
| let _ = app.emit("config://updated", ()); |
|
|
| |
| let instance_lock = proxy_state.instance.read().await; |
| if let Some(instance) = instance_lock.as_ref() { |
| |
| instance.axum_server.update_mapping(&config.proxy).await; |
| |
| instance |
| .axum_server |
| .update_proxy(config.proxy.upstream_proxy.clone()) |
| .await; |
| |
| instance.axum_server.update_security(&config.proxy).await; |
| |
| instance.axum_server.update_zai(&config.proxy).await; |
| |
| instance |
| .axum_server |
| .update_experimental(&config.proxy) |
| .await; |
| |
| instance |
| .axum_server |
| .update_debug_logging(&config.proxy) |
| .await; |
| |
| instance.axum_server.update_user_agent(&config.proxy).await; |
| |
| crate::proxy::update_thinking_budget_config(config.proxy.thinking_budget.clone()); |
| |
| crate::proxy::update_global_system_prompt_config(config.proxy.global_system_prompt.clone()); |
| |
| crate::proxy::update_image_thinking_mode(config.proxy.image_thinking_mode.clone()); |
| |
| instance |
| .axum_server |
| .update_proxy_pool(config.proxy.proxy_pool.clone()) |
| .await; |
| |
| instance |
| .token_manager |
| .update_circuit_breaker_config(config.circuit_breaker.clone()) |
| .await; |
| tracing::debug!("已同步热更新反代服务配置"); |
| } |
|
|
| Ok(()) |
| } |
|
|
| |
|
|
| #[tauri::command] |
| pub async fn start_oauth_login(app_handle: tauri::AppHandle, oauth_client_key: Option<String>) -> Result<Account, String> { |
| modules::logger::log_info("开始 OAuth 授权流程..."); |
| let service = modules::account_service::AccountService::new( |
| crate::modules::integration::SystemManager::Desktop(app_handle.clone()), |
| ); |
|
|
| let mut account = service.start_oauth_login(oauth_client_key).await?; |
|
|
| |
| let _ = internal_refresh_account_quota(&app_handle, &mut account).await; |
|
|
| |
| let _ = crate::commands::proxy::reload_proxy_accounts( |
| app_handle.state::<crate::commands::proxy::ProxyServiceState>(), |
| ) |
| .await; |
|
|
| Ok(account) |
| } |
|
|
| |
| #[tauri::command] |
| pub async fn complete_oauth_login(app_handle: tauri::AppHandle) -> Result<Account, String> { |
| modules::logger::log_info("完成 OAuth 授权流程 (manual)..."); |
| let service = modules::account_service::AccountService::new( |
| crate::modules::integration::SystemManager::Desktop(app_handle.clone()), |
| ); |
|
|
| let mut account = service.complete_oauth_login().await?; |
|
|
| |
| let _ = internal_refresh_account_quota(&app_handle, &mut account).await; |
|
|
| |
| let _ = crate::commands::proxy::reload_proxy_accounts( |
| app_handle.state::<crate::commands::proxy::ProxyServiceState>(), |
| ) |
| .await; |
|
|
| Ok(account) |
| } |
|
|
| |
| #[tauri::command] |
| pub async fn prepare_oauth_url(app_handle: tauri::AppHandle, oauth_client_key: Option<String>) -> Result<String, String> { |
| let service = modules::account_service::AccountService::new( |
| crate::modules::integration::SystemManager::Desktop(app_handle.clone()), |
| ); |
| service.prepare_oauth_url(oauth_client_key).await |
| } |
|
|
| #[tauri::command] |
| pub async fn cancel_oauth_login() -> Result<(), String> { |
| modules::oauth_server::cancel_oauth_flow(); |
| Ok(()) |
| } |
|
|
| |
| #[tauri::command] |
| pub async fn submit_oauth_code(code: String, state: Option<String>) -> Result<(), String> { |
| modules::logger::log_info("收到手动提交 OAuth Code 请求"); |
| modules::oauth_server::submit_oauth_code(code, state).await |
| } |
|
|
| #[tauri::command] |
| pub async fn list_oauth_clients() -> Result<Vec<crate::modules::oauth::OAuthClientDescriptor>, String> { |
| crate::modules::oauth::list_oauth_clients() |
| } |
|
|
| #[tauri::command] |
| pub async fn get_active_oauth_client() -> Result<String, String> { |
| crate::modules::oauth::get_active_oauth_client_key() |
| } |
|
|
| #[tauri::command] |
| pub async fn set_active_oauth_client(client_key: String) -> Result<(), String> { |
| crate::modules::oauth::set_active_oauth_client_key(&client_key) |
| } |
|
|
| |
|
|
| #[tauri::command] |
| pub async fn import_v1_accounts( |
| app: tauri::AppHandle, |
| proxy_state: tauri::State<'_, crate::commands::proxy::ProxyServiceState>, |
| ) -> Result<Vec<Account>, String> { |
| let accounts = modules::migration::import_from_v1().await?; |
|
|
| |
| for mut account in accounts.clone() { |
| let _ = internal_refresh_account_quota(&app, &mut account).await; |
| } |
|
|
| |
| let _ = crate::commands::proxy::reload_proxy_accounts(proxy_state).await; |
|
|
| Ok(accounts) |
| } |
|
|
| #[tauri::command] |
| pub async fn import_from_db( |
| app: tauri::AppHandle, |
| proxy_state: tauri::State<'_, crate::commands::proxy::ProxyServiceState>, |
| ) -> Result<Account, String> { |
| |
| let mut account = modules::migration::import_from_db().await?; |
|
|
| |
| let account_id = account.id.clone(); |
| modules::account::set_current_account_id(&account_id)?; |
|
|
| |
| let _ = internal_refresh_account_quota(&app, &mut account).await; |
|
|
| |
| crate::modules::tray::update_tray_menus(&app); |
|
|
| |
| let _ = crate::commands::proxy::reload_proxy_accounts(proxy_state).await; |
|
|
| Ok(account) |
| } |
|
|
| #[tauri::command] |
| #[allow(dead_code)] |
| pub async fn import_custom_db( |
| app: tauri::AppHandle, |
| proxy_state: tauri::State<'_, crate::commands::proxy::ProxyServiceState>, |
| path: String, |
| ) -> Result<Account, String> { |
| |
| let mut account = modules::migration::import_from_custom_db_path(path).await?; |
|
|
| |
| let account_id = account.id.clone(); |
| modules::account::set_current_account_id(&account_id)?; |
|
|
| |
| let _ = internal_refresh_account_quota(&app, &mut account).await; |
|
|
| |
| crate::modules::tray::update_tray_menus(&app); |
|
|
| |
| let _ = crate::commands::proxy::reload_proxy_accounts(proxy_state).await; |
|
|
| Ok(account) |
| } |
|
|
| #[tauri::command] |
| pub async fn sync_account_from_db( |
| app: tauri::AppHandle, |
| proxy_state: tauri::State<'_, crate::commands::proxy::ProxyServiceState>, |
| ) -> Result<Option<Account>, String> { |
| |
| let db_refresh_token = match modules::migration::get_refresh_token_from_db() { |
| Ok(token) => token, |
| Err(e) => { |
| modules::logger::log_info(&format!("自动同步跳过: {}", e)); |
| return Ok(None); |
| } |
| }; |
|
|
| |
| let curr_account = modules::account::get_current_account()?; |
|
|
| |
| if let Some(acc) = curr_account { |
| if acc.token.refresh_token == db_refresh_token { |
| |
| |
| return Ok(None); |
| } |
| modules::logger::log_info(&format!( |
| "检测到账号切换 ({} -> DB新账号),正在同步...", |
| acc.email |
| )); |
| } else { |
| modules::logger::log_info("检测到新登录账号,正在自动同步..."); |
| } |
|
|
| |
| let account = import_from_db(app, proxy_state).await?; |
| Ok(Some(account)) |
| } |
|
|
| fn validate_path(path: &str) -> Result<(), String> { |
| if path.contains("..") { |
| return Err("非法路径: 不允许目录遍历".to_string()); |
| } |
|
|
| |
| let lower_path = path.to_lowercase(); |
| let sensitive_prefixes = [ |
| "/etc/", |
| "/var/spool/cron", |
| "/root/", |
| "/proc/", |
| "/sys/", |
| "/dev/", |
| "c:\\windows", |
| "c:\\users\\administrator", |
| "c:\\pagefile.sys", |
| ]; |
|
|
| for prefix in sensitive_prefixes { |
| if lower_path.starts_with(prefix) { |
| return Err(format!("安全拒绝: 禁止访问系统敏感路径 ({})", prefix)); |
| } |
| } |
|
|
| Ok(()) |
| } |
|
|
| |
| #[tauri::command] |
| pub async fn save_text_file(path: String, content: String) -> Result<(), String> { |
| validate_path(&path)?; |
| std::fs::write(&path, content).map_err(|e| format!("写入文件失败: {}", e)) |
| } |
|
|
| |
| #[tauri::command] |
| pub async fn read_text_file(path: String) -> Result<String, String> { |
| validate_path(&path)?; |
| std::fs::read_to_string(&path).map_err(|e| format!("读取文件失败: {}", e)) |
| } |
|
|
| |
| #[tauri::command] |
| pub async fn clear_log_cache() -> Result<(), String> { |
| modules::logger::clear_logs() |
| } |
|
|
| |
| |
| #[tauri::command] |
| pub async fn clear_antigravity_cache() -> Result<modules::cache::ClearResult, String> { |
| modules::cache::clear_antigravity_cache(None) |
| } |
|
|
| |
| #[tauri::command] |
| pub async fn get_antigravity_cache_paths() -> Result<Vec<String>, String> { |
| Ok(modules::cache::get_existing_cache_paths() |
| .into_iter() |
| .map(|p| p.to_string_lossy().to_string()) |
| .collect()) |
| } |
|
|
| |
| #[tauri::command] |
| pub async fn open_data_folder() -> Result<(), String> { |
| let path = modules::account::get_data_dir()?; |
|
|
| #[cfg(target_os = "macos")] |
| { |
| std::process::Command::new("open") |
| .arg(path) |
| .spawn() |
| .map_err(|e| format!("打开文件夹失败: {}", e))?; |
| } |
|
|
| #[cfg(target_os = "windows")] |
| { |
| use crate::utils::command::CommandExtWrapper; |
| std::process::Command::new("explorer") |
| .creation_flags_windows() |
| .arg(path) |
| .spawn() |
| .map_err(|e| format!("打开文件夹失败: {}", e))?; |
| } |
|
|
| #[cfg(target_os = "linux")] |
| { |
| std::process::Command::new("xdg-open") |
| .arg(path) |
| .spawn() |
| .map_err(|e| format!("打开文件夹失败: {}", e))?; |
| } |
|
|
| Ok(()) |
| } |
|
|
| |
| #[tauri::command] |
| pub async fn get_data_dir_path() -> Result<String, String> { |
| let path = modules::account::get_data_dir()?; |
| Ok(path.to_string_lossy().to_string()) |
| } |
|
|
| |
| #[tauri::command] |
| pub async fn show_main_window(window: tauri::Window) -> Result<(), String> { |
| window.show().map_err(|e| e.to_string()) |
| } |
|
|
| |
| #[tauri::command] |
| pub async fn set_window_theme(window: tauri::Window, theme: String) -> Result<(), String> { |
| use tauri::Theme; |
|
|
| let tauri_theme = match theme.as_str() { |
| "dark" => Some(Theme::Dark), |
| "light" => Some(Theme::Light), |
| _ => None, |
| }; |
|
|
| window.set_theme(tauri_theme).map_err(|e| e.to_string()) |
| } |
|
|
| |
| #[tauri::command] |
| pub async fn get_antigravity_path(bypass_config: Option<bool>) -> Result<String, String> { |
| |
| if bypass_config != Some(true) { |
| if let Ok(config) = crate::modules::config::load_app_config() { |
| if let Some(path) = config.antigravity_executable { |
| if std::path::Path::new(&path).exists() { |
| return Ok(path); |
| } |
| } |
| } |
| } |
|
|
| |
| match crate::modules::process::get_antigravity_executable_path() { |
| Some(path) => Ok(path.to_string_lossy().to_string()), |
| None => Err("未找到 Antigravity 安装路径".to_string()), |
| } |
| } |
|
|
| |
| #[tauri::command] |
| pub async fn get_antigravity_args() -> Result<Vec<String>, String> { |
| match crate::modules::process::get_args_from_running_process() { |
| Some(args) => Ok(args), |
| None => Err("未找到正在运行的 Antigravity 进程".to_string()), |
| } |
| } |
|
|
| |
| pub use crate::modules::update_checker::UpdateInfo; |
|
|
| |
| #[tauri::command] |
| pub async fn check_for_updates() -> Result<UpdateInfo, String> { |
| modules::logger::log_info("收到前端触发的更新检查请求"); |
| crate::modules::update_checker::check_for_updates().await |
| } |
|
|
| #[tauri::command] |
| pub async fn should_check_updates() -> Result<bool, String> { |
| let settings = crate::modules::update_checker::load_update_settings()?; |
| Ok(crate::modules::update_checker::should_check_for_updates( |
| &settings, |
| )) |
| } |
|
|
| #[tauri::command] |
| pub async fn update_last_check_time() -> Result<(), String> { |
| crate::modules::update_checker::update_last_check_time() |
| } |
|
|
|
|
| |
| #[tauri::command] |
| pub async fn check_homebrew_installation() -> Result<bool, String> { |
| Ok(crate::modules::update_checker::is_homebrew_installed()) |
| } |
|
|
| |
| #[tauri::command] |
| pub async fn brew_upgrade_cask() -> Result<String, String> { |
| modules::logger::log_info("收到前端触发的 Homebrew 升级请求"); |
| crate::modules::update_checker::brew_upgrade_cask().await |
| } |
|
|
|
|
| |
| #[tauri::command] |
| pub async fn get_update_settings() -> Result<crate::modules::update_checker::UpdateSettings, String> |
| { |
| crate::modules::update_checker::load_update_settings() |
| } |
|
|
| |
| #[tauri::command] |
| pub async fn save_update_settings( |
| settings: crate::modules::update_checker::UpdateSettings, |
| ) -> Result<(), String> { |
| crate::modules::update_checker::save_update_settings(&settings) |
| } |
|
|
| |
| #[tauri::command] |
| pub async fn toggle_proxy_status( |
| app: tauri::AppHandle, |
| proxy_state: tauri::State<'_, crate::commands::proxy::ProxyServiceState>, |
| account_id: String, |
| enable: bool, |
| reason: Option<String>, |
| ) -> Result<(), String> { |
| modules::logger::log_info(&format!( |
| "切换账号反代状态: {} -> {}", |
| account_id, |
| if enable { "启用" } else { "禁用" } |
| )); |
|
|
| |
| let data_dir = modules::account::get_data_dir()?; |
| let account_path = data_dir |
| .join("accounts") |
| .join(format!("{}.json", account_id)); |
|
|
| if !account_path.exists() { |
| return Err(format!("账号文件不存在: {}", account_id)); |
| } |
|
|
| let content = |
| std::fs::read_to_string(&account_path).map_err(|e| format!("读取账号文件失败: {}", e))?; |
|
|
| let mut account_json: serde_json::Value = |
| serde_json::from_str(&content).map_err(|e| format!("解析账号文件失败: {}", e))?; |
|
|
| |
| if enable { |
| |
| account_json["proxy_disabled"] = serde_json::Value::Bool(false); |
| account_json["proxy_disabled_reason"] = serde_json::Value::Null; |
| account_json["proxy_disabled_at"] = serde_json::Value::Null; |
| } else { |
| |
| let now = chrono::Utc::now().timestamp(); |
| account_json["proxy_disabled"] = serde_json::Value::Bool(true); |
| account_json["proxy_disabled_at"] = serde_json::Value::Number(now.into()); |
| account_json["proxy_disabled_reason"] = |
| serde_json::Value::String(reason.unwrap_or_else(|| "用户手动禁用".to_string())); |
| } |
|
|
| |
| let json_str = serde_json::to_string_pretty(&account_json) |
| .map_err(|e| format!("序列化账号数据失败: {}", e))?; |
| std::fs::write(&account_path, json_str).map_err(|e| format!("写入账号文件失败: {}", e))?; |
|
|
| modules::logger::log_info(&format!( |
| "账号反代状态已更新: {} ({})", |
| account_id, |
| if enable { "已启用" } else { "已禁用" } |
| )); |
|
|
| |
| { |
| let instance_lock = proxy_state.instance.read().await; |
| if let Some(instance) = instance_lock.as_ref() { |
| |
| if !enable { |
| let pref_id = instance.token_manager.get_preferred_account().await; |
| if pref_id.as_deref() == Some(&account_id) { |
| instance.token_manager.set_preferred_account(None).await; |
|
|
| if let Ok(mut cfg) = crate::modules::config::load_app_config() { |
| if cfg.proxy.preferred_account_id.as_deref() == Some(&account_id) { |
| cfg.proxy.preferred_account_id = None; |
| let _ = crate::modules::config::save_app_config(&cfg); |
| } |
| } |
| } |
| } |
|
|
| instance |
| .token_manager |
| .reload_account(&account_id) |
| .await |
| .map_err(|e| format!("同步账号失败: {}", e))?; |
| } |
| } |
|
|
| |
| crate::modules::tray::update_tray_menus(&app); |
|
|
| Ok(()) |
| } |
|
|
| |
| #[tauri::command] |
| pub async fn warm_up_all_accounts() -> Result<String, String> { |
| modules::quota::warm_up_all_accounts().await |
| } |
|
|
| |
| #[tauri::command] |
| pub async fn warm_up_account(account_id: String) -> Result<String, String> { |
| modules::quota::warm_up_account(&account_id).await |
| } |
|
|
| |
| #[tauri::command] |
| pub async fn update_account_label(account_id: String, label: String) -> Result<(), String> { |
| |
| if label.chars().count() > 15 { |
| return Err("标签长度不能超过15个字符".to_string()); |
| } |
|
|
| modules::logger::log_info(&format!( |
| "更新账号标签: {} -> {:?}", |
| account_id, |
| if label.is_empty() { "无" } else { &label } |
| )); |
|
|
| |
| let data_dir = modules::account::get_data_dir()?; |
| let account_path = data_dir |
| .join("accounts") |
| .join(format!("{}.json", account_id)); |
|
|
| if !account_path.exists() { |
| return Err(format!("账号文件不存在: {}", account_id)); |
| } |
|
|
| let content = |
| std::fs::read_to_string(&account_path).map_err(|e| format!("读取账号文件失败: {}", e))?; |
|
|
| let mut account_json: serde_json::Value = |
| serde_json::from_str(&content).map_err(|e| format!("解析账号文件失败: {}", e))?; |
|
|
| |
| if label.is_empty() { |
| account_json["custom_label"] = serde_json::Value::Null; |
| } else { |
| account_json["custom_label"] = serde_json::Value::String(label.clone()); |
| } |
|
|
| |
| let json_str = serde_json::to_string_pretty(&account_json) |
| .map_err(|e| format!("序列化账号数据失败: {}", e))?; |
| std::fs::write(&account_path, json_str).map_err(|e| format!("写入账号文件失败: {}", e))?; |
|
|
| modules::logger::log_info(&format!( |
| "账号标签已更新: {} ({})", |
| account_id, |
| if label.is_empty() { |
| "已清除".to_string() |
| } else { |
| label |
| } |
| )); |
|
|
| Ok(()) |
| } |
|
|
| |
| |
| |
|
|
| |
| #[tauri::command] |
| pub async fn get_http_api_settings() -> Result<crate::modules::http_api::HttpApiSettings, String> { |
| crate::modules::http_api::load_settings() |
| } |
|
|
| |
| #[tauri::command] |
| pub async fn save_http_api_settings( |
| settings: crate::modules::http_api::HttpApiSettings, |
| ) -> Result<(), String> { |
| crate::modules::http_api::save_settings(&settings) |
| } |
|
|
| |
| |
| |
|
|
| pub use crate::modules::token_stats::{AccountTokenStats, TokenStatsAggregated, TokenStatsSummary}; |
|
|
| #[tauri::command] |
| pub async fn get_token_stats_hourly(hours: i64) -> Result<Vec<TokenStatsAggregated>, String> { |
| crate::modules::token_stats::get_hourly_stats(hours) |
| } |
|
|
| #[tauri::command] |
| pub async fn get_token_stats_daily(days: i64) -> Result<Vec<TokenStatsAggregated>, String> { |
| crate::modules::token_stats::get_daily_stats(days) |
| } |
|
|
| #[tauri::command] |
| pub async fn get_token_stats_weekly(weeks: i64) -> Result<Vec<TokenStatsAggregated>, String> { |
| crate::modules::token_stats::get_weekly_stats(weeks) |
| } |
|
|
| #[tauri::command] |
| pub async fn get_token_stats_by_account(hours: i64) -> Result<Vec<AccountTokenStats>, String> { |
| crate::modules::token_stats::get_account_stats(hours) |
| } |
|
|
| #[tauri::command] |
| pub async fn get_token_stats_summary(hours: i64) -> Result<TokenStatsSummary, String> { |
| crate::modules::token_stats::get_summary_stats(hours) |
| } |
|
|
| #[tauri::command] |
| pub async fn get_token_stats_by_model( |
| hours: i64, |
| ) -> Result<Vec<crate::modules::token_stats::ModelTokenStats>, String> { |
| crate::modules::token_stats::get_model_stats(hours) |
| } |
|
|
| #[tauri::command] |
| pub async fn get_token_stats_model_trend_hourly( |
| hours: i64, |
| ) -> Result<Vec<crate::modules::token_stats::ModelTrendPoint>, String> { |
| crate::modules::token_stats::get_model_trend_hourly(hours) |
| } |
|
|
| #[tauri::command] |
| pub async fn get_token_stats_model_trend_daily( |
| days: i64, |
| ) -> Result<Vec<crate::modules::token_stats::ModelTrendPoint>, String> { |
| crate::modules::token_stats::get_model_trend_daily(days) |
| } |
|
|
| #[tauri::command] |
| pub async fn get_token_stats_account_trend_hourly( |
| hours: i64, |
| ) -> Result<Vec<crate::modules::token_stats::AccountTrendPoint>, String> { |
| crate::modules::token_stats::get_account_trend_hourly(hours) |
| } |
|
|
| #[tauri::command] |
| pub async fn get_token_stats_account_trend_daily( |
| days: i64, |
| ) -> Result<Vec<crate::modules::token_stats::AccountTrendPoint>, String> { |
| crate::modules::token_stats::get_account_trend_daily(days) |
| } |
|
|