diff --git a/src-tauri/src/commands/mod.rs b/src-tauri/src/commands/mod.rs index 5301d1b0d..9b7393707 100644 --- a/src-tauri/src/commands/mod.rs +++ b/src-tauri/src/commands/mod.rs @@ -632,7 +632,7 @@ fn is_sensitive_path(path: &Path) -> bool { .any(|prefix| lower == *prefix || lower.starts_with(prefix)) } -fn validate_user_json_path(path: &str, must_exist: bool) -> Result { +pub(crate) fn validate_user_json_path(path: &str, must_exist: bool) -> Result { let requested = PathBuf::from(path); if requested.as_os_str().is_empty() { return Err("invalid_path: empty path".to_string()); diff --git a/src-tauri/src/commands/proxy.rs b/src-tauri/src/commands/proxy.rs index 5e365cdb5..d6d9fc554 100644 --- a/src-tauri/src/commands/proxy.rs +++ b/src-tauri/src/commands/proxy.rs @@ -412,13 +412,15 @@ pub async fn get_proxy_logs_count() -> Result { /// 导出所有日志到指定文件 #[tauri::command] pub async fn export_proxy_logs(file_path: String) -> Result { + let validated_path = super::validate_user_json_path(&file_path, false)?; + let logs = crate::modules::proxy_db::get_all_logs_for_export()?; let count = logs.len(); let json = serde_json::to_string_pretty(&logs) .map_err(|e| format!("Failed to serialize logs: {}", e))?; - std::fs::write(&file_path, json).map_err(|e| format!("Failed to write file: {}", e))?; + std::fs::write(&validated_path, json).map_err(|e| format!("Failed to write file: {}", e))?; Ok(count) } @@ -426,6 +428,8 @@ pub async fn export_proxy_logs(file_path: String) -> Result { /// 导出指定的日志JSON到文件 #[tauri::command] pub async fn export_proxy_logs_json(file_path: String, json_data: String) -> Result { + let validated_path = super::validate_user_json_path(&file_path, false)?; + // Parse to count items let logs: Vec = serde_json::from_str(&json_data).map_err(|e| format!("Failed to parse JSON: {}", e))?; @@ -435,7 +439,8 @@ pub async fn export_proxy_logs_json(file_path: String, json_data: String) -> Res let pretty_json = serde_json::to_string_pretty(&logs).map_err(|e| format!("Failed to serialize: {}", e))?; - std::fs::write(&file_path, pretty_json).map_err(|e| format!("Failed to write file: {}", e))?; + std::fs::write(&validated_path, pretty_json) + .map_err(|e| format!("Failed to write file: {}", e))?; Ok(count) } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 9ccd15d3e..3601d5003 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -261,7 +261,7 @@ pub fn run() { info!("🔐 Web UI Password: (Same as API Key)"); } info!("💡 Tips: You can use these keys to login to Web UI and access AI APIs."); - info!("💡 Search docker logs or grep gui_config.json to find them."); + info!("💡 View gui_config.json or check environment variables to find them."); info!("--------------------------------------------------"); // [FIX #1460] Persist environment overrides to ensure they are visible in Web UI/load_config diff --git a/src-tauri/src/modules/account.rs b/src-tauri/src/modules/account.rs index 368704d75..82bd89364 100644 --- a/src-tauri/src/modules/account.rs +++ b/src-tauri/src/modules/account.rs @@ -1749,127 +1749,119 @@ pub async fn fetch_quota_with_retry(account: &mut Account) -> crate::error::AppR } // 3. Handle 401 error - if let Err(AppError::Network(_, status)) = result { - if let Some(code) = status { - if code == 401 { - modules::logger::log_warn(&format!( - "401 Unauthorized for {}, forcing refresh...", - account.email - )); + if let Err(AppError::Network(_, Some(code))) = result { + if code == 401 { + modules::logger::log_warn(&format!( + "401 Unauthorized for {}, forcing refresh...", + account.email + )); - // Force refresh - let token_res = match oauth::refresh_access_token_with_client( - &account.token.refresh_token, - Some(&account.id), - account.token.oauth_client_key.as_deref(), - ) - .await - { - Ok(t) => t, - Err(e) => { - if e.contains("invalid_grant") { - modules::logger::log_error(&format!( + // Force refresh + let token_res = match oauth::refresh_access_token_with_client( + &account.token.refresh_token, + Some(&account.id), + account.token.oauth_client_key.as_deref(), + ) + .await + { + Ok(t) => t, + Err(e) => { + if e.contains("invalid_grant") { + modules::logger::log_error(&format!( "Disabling account {} due to invalid_grant during forced refresh (quota check)", account.email )); - account.disabled = true; - account.disabled_at = Some(chrono::Utc::now().timestamp()); - account.disabled_reason = Some(format!("invalid_grant: {}", e)); - let _ = save_account(account); - crate::proxy::server::trigger_account_reload(&account.id); - } - return Err(AppError::OAuth(e)); + account.disabled = true; + account.disabled_at = Some(chrono::Utc::now().timestamp()); + account.disabled_reason = Some(format!("invalid_grant: {}", e)); + let _ = save_account(account); + crate::proxy::server::trigger_account_reload(&account.id); } - }; - - let new_token = TokenData::new( - token_res.access_token.clone(), - account.token.refresh_token.clone(), - token_res.expires_in, - account.token.email.clone(), - account.token.project_id.clone(), // Keep original project_id - None, // Add None as session_id - account.token.is_gcp_tos, - token_res.id_token.clone(), - ) - .with_oauth_client_key( - token_res - .oauth_client_key - .clone() - .or_else(|| account.token.oauth_client_key.clone()), - ); + return Err(AppError::OAuth(e)); + } + }; + + let new_token = TokenData::new( + token_res.access_token.clone(), + account.token.refresh_token.clone(), + token_res.expires_in, + account.token.email.clone(), + account.token.project_id.clone(), // Keep original project_id + None, // Add None as session_id + account.token.is_gcp_tos, + token_res.id_token.clone(), + ) + .with_oauth_client_key( + token_res + .oauth_client_key + .clone() + .or_else(|| account.token.oauth_client_key.clone()), + ); - // Re-fetch display name - let name = if account.name.is_none() - || account.name.as_ref().map_or(false, |n| n.trim().is_empty()) - { - match oauth::get_user_info(&token_res.access_token, Some(&account.id)).await { - Ok(user_info) => user_info.get_display_name(), - Err(_) => None, - } - } else { - account.name.clone() - }; + // Re-fetch display name + let name = if account.name.is_none() + || account.name.as_ref().map_or(false, |n| n.trim().is_empty()) + { + match oauth::get_user_info(&token_res.access_token, Some(&account.id)).await { + Ok(user_info) => user_info.get_display_name(), + Err(_) => None, + } + } else { + account.name.clone() + }; - account.token = new_token.clone(); - account.name = name.clone(); - upsert_account(account.email.clone(), name, new_token.clone()) - .map_err(AppError::Account)?; - - // Retry query - let retry_result: crate::error::AppResult<(QuotaData, Option)> = - modules::fetch_quota( - &new_token.access_token, - &account.email, - Some(&account.id), - ) + account.token = new_token.clone(); + account.name = name.clone(); + upsert_account(account.email.clone(), name, new_token.clone()) + .map_err(AppError::Account)?; + + // Retry query + let retry_result: crate::error::AppResult<(QuotaData, Option)> = + modules::fetch_quota(&new_token.access_token, &account.email, Some(&account.id)) .await; - // Also handle project_id saving during retry - if let Ok((ref _q, ref project_id)) = retry_result { - if project_id.is_some() && *project_id != account.token.project_id { - modules::logger::log_info(&format!( - "Detected update of project_id after retry ({}), saving...", - account.email - )); - account.token.project_id = project_id.clone(); - let _ = upsert_account( - account.email.clone(), - account.name.clone(), - account.token.clone(), - ); - } + // Also handle project_id saving during retry + if let Ok((ref _q, ref project_id)) = retry_result { + if project_id.is_some() && *project_id != account.token.project_id { + modules::logger::log_info(&format!( + "Detected update of project_id after retry ({}), saving...", + account.email + )); + account.token.project_id = project_id.clone(); + let _ = upsert_account( + account.email.clone(), + account.name.clone(), + account.token.clone(), + ); } + } - if let Err(AppError::Network(_, status)) = retry_result { - if let Some(code) = status { - if code == 403 { - let mut q = QuotaData::new(); - q.is_forbidden = true; - return Ok(q); - } - } + if let Err(AppError::Network(_, Some(code))) = retry_result { + if code == 403 { + let mut q = QuotaData::new(); + q.is_forbidden = true; + return Ok(q); } + } - match retry_result { - Ok((q, _)) => { - clear_validation_blocked(account); - return Ok(q); + match retry_result { + Ok((q, _)) => { + clear_validation_blocked(account); + return Ok(q); + } + Err(e) => { + if is_validation_required_error(&e) { + mark_validation_blocked(account, &e.to_string()); } - Err(e) => { - if is_validation_required_error(&e) { - mark_validation_blocked(account, &e.to_string()); - } - if let Some(cached) = recover_cached_quota_on_rate_limit(account, &e) { - mark_validation_blocked(account, &format_rate_limit_block_reason(&e)); - modules::logger::log_warn(&format!( - "Quota API rate-limited for {}, using cached model list as fallback", - account.email - )); - return Ok(cached); - } - return Err(e); + if let Some(cached) = recover_cached_quota_on_rate_limit(account, &e) { + mark_validation_blocked(account, &format_rate_limit_block_reason(&e)); + modules::logger::log_warn(&format!( + "Quota API rate-limited for {}, using cached model list as fallback", + account.email + )); + return Ok(cached); } + return Err(e); } } } @@ -2010,6 +2002,7 @@ pub async fn refresh_all_quotas_logic() -> Result { /// Check and trigger warmup for models that have recovered to 100% /// Called automatically after quota refresh to enable immediate warmup +#[allow(dead_code)] pub async fn check_and_trigger_warmup_for_recovered_models() { let accounts = match list_accounts() { Ok(acc) => acc, diff --git a/src-tauri/src/modules/cloudflared.rs b/src-tauri/src/modules/cloudflared.rs index f3545198f..8c3db5852 100644 --- a/src-tauri/src/modules/cloudflared.rs +++ b/src-tauri/src/modules/cloudflared.rs @@ -7,9 +7,6 @@ use tokio::process::{Child, Command}; use tokio::sync::RwLock; use tracing::{debug, info}; -#[cfg(target_os = "windows")] -use std::os::windows::process::CommandExt; - #[cfg(target_os = "windows")] const CREATE_NO_WINDOW: u32 = 0x08000000; #[cfg(target_os = "windows")] diff --git a/src-tauri/src/modules/config.rs b/src-tauri/src/modules/config.rs index bff151834..7bc48d46c 100644 --- a/src-tauri/src/modules/config.rs +++ b/src-tauri/src/modules/config.rs @@ -3,8 +3,6 @@ use std::fs; use super::account::get_data_dir; use crate::models::AppConfig; -use tracing::warn; - const CONFIG_FILE: &str = "gui_config.json"; /// Load application configuration diff --git a/src-tauri/src/modules/integration.rs b/src-tauri/src/modules/integration.rs index 87e05c240..af794365d 100644 --- a/src-tauri/src/modules/integration.rs +++ b/src-tauri/src/modules/integration.rs @@ -1,8 +1,6 @@ use crate::models::Account; use crate::modules::{db, device, process, version}; use std::fs; -use std::process::Command; - pub trait SystemIntegration: Send + Sync { /// 当切换账号时执行的系统层操作(如杀进程、写入文件、注入数据库) async fn on_account_switch( @@ -181,6 +179,7 @@ fn write_to_system_keyring(account: &crate::models::Account) -> Result<(), Strin #[cfg(target_os = "macos")] { use base64::{engine::general_purpose::STANDARD, Engine as _}; + use std::process::Command; let encoded_payload = STANDARD.encode(&payload_json); let full_keyring_value = format!("go-keyring-base64:{}", encoded_payload); @@ -299,6 +298,7 @@ fn write_to_system_keyring(account: &crate::models::Account) -> Result<(), Strin { // 2.3 Linux Secret Service API use std::io::Write; + use std::process::Command; let mut child = Command::new("secret-tool") .args([ "store", diff --git a/src-tauri/src/modules/process.rs b/src-tauri/src/modules/process.rs index 572f60c2c..caa2b0860 100644 --- a/src-tauri/src/modules/process.rs +++ b/src-tauri/src/modules/process.rs @@ -457,7 +457,7 @@ fn get_antigravity_pids(target_ide: Option<&str>) -> Vec { } /// Close Antigravity processes -pub fn close_antigravity(timeout_secs: u64, target_ide: Option<&str>) -> Result<(), String> { +pub fn close_antigravity(_timeout_secs: u64, target_ide: Option<&str>) -> Result<(), String> { crate::modules::logger::log_info(&format!("Closing Antigravity ({:?})...", target_ide)); #[cfg(target_os = "windows")] @@ -597,7 +597,7 @@ pub fn close_antigravity(timeout_secs: u64, target_ide: Option<&str>) -> Result< } // Wait for graceful exit (max 70% of timeout_secs) - let graceful_timeout = (timeout_secs * 7) / 10; + let graceful_timeout = (_timeout_secs * 7) / 10; let start = std::time::Instant::now(); while start.elapsed() < Duration::from_secs(graceful_timeout) { if !is_antigravity_running(target_ide) { @@ -705,7 +705,7 @@ pub fn close_antigravity(timeout_secs: u64, target_ide: Option<&str>) -> Result< } // Wait for graceful exit - let graceful_timeout = (timeout_secs * 7) / 10; + let graceful_timeout = (_timeout_secs * 7) / 10; let start = std::time::Instant::now(); while start.elapsed() < Duration::from_secs(graceful_timeout) { if !is_antigravity_running(target_ide) { diff --git a/src-tauri/src/modules/scheduler.rs b/src-tauri/src/modules/scheduler.rs index 3935498e2..f19be4fdb 100644 --- a/src-tauri/src/modules/scheduler.rs +++ b/src-tauri/src/modules/scheduler.rs @@ -50,6 +50,7 @@ pub fn check_cooldown(key: &str, cooldown_seconds: i64) -> bool { } } +#[allow(dead_code)] pub fn start_scheduler( app_handle: Option, proxy_state: crate::commands::proxy::ProxyServiceState, @@ -306,6 +307,7 @@ pub fn start_scheduler( } /// Trigger immediate smart warmup check for a single account +#[allow(dead_code)] pub async fn trigger_warmup_for_account(account: &Account) { // Get valid token let Ok((token, pid)) = quota::get_valid_token_for_warmup(account).await else { diff --git a/src-tauri/src/modules/security_db.rs b/src-tauri/src/modules/security_db.rs index 6f2fa4c85..96a6804ca 100644 --- a/src-tauri/src/modules/security_db.rs +++ b/src-tauri/src/modules/security_db.rs @@ -359,6 +359,7 @@ pub fn get_top_ips(limit: usize, hours: i64) -> Result, String> { } /// 清理旧的 IP 访问日志 +#[allow(dead_code)] pub fn cleanup_old_ip_logs(days: i64) -> Result { let conn = connect_db()?; @@ -655,6 +656,7 @@ pub fn clear_ip_access_logs() -> Result<(), String> { } /// 获取 IP 访问日志总数 +#[allow(dead_code)] pub fn get_ip_access_logs_count( ip_filter: Option<&str>, blocked_only: bool, diff --git a/src-tauri/src/modules/user_token_db.rs b/src-tauri/src/modules/user_token_db.rs index 90b59531c..de583e2ec 100644 --- a/src-tauri/src/modules/user_token_db.rs +++ b/src-tauri/src/modules/user_token_db.rs @@ -4,7 +4,7 @@ #![allow(dead_code)] // 用户令牌存储,部分接口留作后续扩展 -use chrono::{FixedOffset, Local, Timelike, Utc}; +use chrono::{FixedOffset, Timelike, Utc}; use rusqlite::{params, Connection, OptionalExtension}; use serde::{Deserialize, Serialize}; use std::path::PathBuf; diff --git a/src-tauri/src/modules/version.rs b/src-tauri/src/modules/version.rs index 9c1cc21fe..15ae931f3 100644 --- a/src-tauri/src/modules/version.rs +++ b/src-tauri/src/modules/version.rs @@ -12,6 +12,7 @@ pub struct AntigravityVersion { } /// 从任意字符串中提取第一个语义化版本号 (X.Y.Z) +#[allow(dead_code)] fn extract_semver(raw: &str) -> Option { for token in raw.split(|c: char| c.is_whitespace() || c == ',' || c == ';') { let t = token.trim_matches(|c: char| c == '"' || c == '\'' || c == '(' || c == ')'); diff --git a/src-tauri/src/proxy/cli_sync.rs b/src-tauri/src/proxy/cli_sync.rs index e3f0e267b..edfb7e887 100644 --- a/src-tauri/src/proxy/cli_sync.rs +++ b/src-tauri/src/proxy/cli_sync.rs @@ -162,6 +162,7 @@ fn run_version_command(executable_path: &PathBuf) -> Option { } /// 提取版本号(使用更精确的 semver 匹配) +#[cfg(target_os = "windows")] fn extract_version(s: &str) -> Option { // 匹配 semver 格式: x.y.z 或 x.y let re = regex::Regex::new(r"(\d+\.\d+(?:\.\d+)?)").ok()?; diff --git a/src-tauri/src/proxy/handlers/claude.rs b/src-tauri/src/proxy/handlers/claude.rs index 4e91f9674..dba459ea6 100644 --- a/src-tauri/src/proxy/handlers/claude.rs +++ b/src-tauri/src/proxy/handlers/claude.rs @@ -1562,7 +1562,6 @@ pub async fn handle_messages( determine_retry_strategy(status_code, &error_text, retried_without_thinking); // 执行退避 - let mut force_rotate = false; if apply_retry_strategy( retry_strategy.clone(), attempt, @@ -1578,9 +1577,6 @@ pub async fn handle_messages( "[{}] Keeping same account for status {} (Grace Retry or Server Issue)", trace_id, status_code ); - force_rotate = false; - } else { - force_rotate = true; } continue; } else { diff --git a/src-tauri/src/proxy/handlers/gemini.rs b/src-tauri/src/proxy/handlers/gemini.rs index b1241096f..00ca2f646 100644 --- a/src-tauri/src/proxy/handlers/gemini.rs +++ b/src-tauri/src/proxy/handlers/gemini.rs @@ -629,15 +629,11 @@ pub async fn handle_generate( // 判断是否需要轮换账号 // 判断是否需要轮换账号 - let mut force_rotate = false; if !should_rotate_account(status_code, Some(&strategy)) { debug!( "[{}] Keeping same account for status {} (Gemini server-side issue or Grace Retry)", trace_id, status_code ); - force_rotate = false; - } else { - force_rotate = true; } } diff --git a/src-tauri/src/proxy/handlers/openai.rs b/src-tauri/src/proxy/handlers/openai.rs index c3ebd1d22..578f94977 100644 --- a/src-tauri/src/proxy/handlers/openai.rs +++ b/src-tauri/src/proxy/handlers/openai.rs @@ -632,15 +632,11 @@ pub async fn handle_chat_completions( // 判断是否需要轮换账号 // 判断是否需要轮换账号 - let mut force_rotate = false; if !should_rotate_account(status_code, Some(&strategy)) { debug!( "[{}] Keeping same account for status {} (Grace Retry or Server Issue)", trace_id, status_code ); - force_rotate = false; - } else { - force_rotate = true; } // 2. [REMOVED] 不再特殊处理 QUOTA_EXHAUSTED,允许账号轮换 @@ -1674,6 +1670,7 @@ pub async fn handle_list_models(State(state): State) -> impl IntoRespo /// OpenAI Images API: POST /v1/images/generations /// 处理图像生成请求,转换为 Gemini API 格式 +#[allow(dead_code)] pub async fn handle_chat_redirection( State(state): State, headers: HeaderMap, diff --git a/src-tauri/src/proxy/mappers/claude/request.rs b/src-tauri/src/proxy/mappers/claude/request.rs index 57d99f2a6..e29b19f49 100644 --- a/src-tauri/src/proxy/mappers/claude/request.rs +++ b/src-tauri/src/proxy/mappers/claude/request.rs @@ -430,6 +430,7 @@ pub fn transform_claude_request_in( // Map model name (Use standard mapping) // [IMPROVED] 提取 web search 模型为常量,便于维护 + #[allow(dead_code)] const WEB_SEARCH_FALLBACK_MODEL: &str = "gemini-2.5-flash"; let mapped_model = diff --git a/src-tauri/src/proxy/mappers/common_utils.rs b/src-tauri/src/proxy/mappers/common_utils.rs index 904b635f1..4dbc383ea 100644 --- a/src-tauri/src/proxy/mappers/common_utils.rs +++ b/src-tauri/src/proxy/mappers/common_utils.rs @@ -355,7 +355,7 @@ fn calculate_aspect_ratio_from_size(size: &str) -> &'static str { } /// Inject current googleSearch tool and ensure no duplicate legacy search tools -pub fn inject_google_search_tool(body: &mut Value, mapped_model: Option<&str>) { +pub fn inject_google_search_tool(body: &mut Value, _mapped_model: Option<&str>) { if let Some(obj) = body.as_object_mut() { let tools_entry = obj.entry("tools").or_insert_with(|| json!([])); if let Some(tools_arr) = tools_entry.as_array_mut() { diff --git a/src-tauri/src/proxy/mappers/gemini/wrapper.rs b/src-tauri/src/proxy/mappers/gemini/wrapper.rs index 57422283b..21e397874 100644 --- a/src-tauri/src/proxy/mappers/gemini/wrapper.rs +++ b/src-tauri/src/proxy/mappers/gemini/wrapper.rs @@ -24,7 +24,7 @@ pub fn wrap_request( }; // [ADDED v4.1.24] 计算 message_count 供 requestId 使用 - let message_count = body + let _message_count = body .get("contents") .and_then(|c| c.as_array()) .map(|a| a.len()) @@ -571,7 +571,7 @@ pub fn wrap_request( )); } - let sid = session_id.unwrap_or("default"); + let _sid = session_id.unwrap_or("default"); // [NEW] 1. 深度对齐 requestId 格式 (官方格式: agent/{timestamp_ms}/{random_hex_8bytes}) // 每次请求生成完全唯一的 ID,避免重试时的幂等性冲突导致 Google 返回旧缓存 diff --git a/src-tauri/src/proxy/token_manager.rs b/src-tauri/src/proxy/token_manager.rs index 39d56232b..6137441d4 100644 --- a/src-tauri/src/proxy/token_manager.rs +++ b/src-tauri/src/proxy/token_manager.rs @@ -1835,11 +1835,12 @@ impl TokenManager { e ); if e.contains("\"invalid_grant\"") || e.contains("invalid_grant") { - self.disable_account( - &token.account_id, - &format!("invalid_grant: {}", e), - ) - .await; + let _ = self + .disable_account( + &token.account_id, + &format!("invalid_grant: {}", e), + ) + .await; } last_error = Some(format!("Token refresh failed: {}", e)); attempted.insert(token.account_id.clone()); @@ -1871,13 +1872,13 @@ impl TokenManager { } else { // [NEW] 针对 fetch_project_id 实现基于 SingleFlight 的异步合并 // 1. 检查是否已有 inflight 请求 - let (mut rx, is_new) = { + let (_rx, is_new) = { if let Some(existing_rx) = self.load_code_assist_inflight.get(&token.account_id) { (existing_rx.value().clone(), false) } else { // 创建新的 inflight 频道 - let (tx, rx) = tokio::sync::watch::channel(None); + let (_tx, rx) = tokio::sync::watch::channel(None); self.load_code_assist_inflight .insert(token.account_id.clone(), rx.clone()); (rx, true) @@ -1888,7 +1889,7 @@ impl TokenManager { // 仅由“第一个发现者”执行真实请求 tracing::debug!("账号 {} 启动 [SingleFlight] ProjectID 探测...", token.email); - let result = + let _result = match crate::proxy::project_resolver::fetch_project_id(&token.access_token) .await { @@ -1903,8 +1904,7 @@ impl TokenManager { }; // 广播结果并清理 inflight - if let Some(mut entry) = - self.load_code_assist_inflight.get_mut(&token.account_id) + if let Some(_entry) = self.load_code_assist_inflight.get_mut(&token.account_id) { // 这里虽然是 rx,但在 Rust 中 watch 不需要 tx 也可以通过私有方式操作? // 修正:我们需要持有 tx。重新设计此处:使用 Mutex 或在 scope 外持有 tx。 diff --git a/src-tauri/src/proxy/upstream/client.rs b/src-tauri/src/proxy/upstream/client.rs index 9301dec7f..b096142c2 100644 --- a/src-tauri/src/proxy/upstream/client.rs +++ b/src-tauri/src/proxy/upstream/client.rs @@ -44,6 +44,7 @@ pub fn mask_email(email: &str) -> String { } /// [NEW] 错误日志脱敏:抹除报错信息中的 access_token, proxy_url 等敏感凭证 +#[allow(dead_code)] pub fn sanitize_error_for_log(error_text: &str) -> String { // 抹除常见敏感 key 的值 let re = regex::Regex::new(r#"(?i)(access_token|refresh_token|id_token|authorization|api_key|secret|password|proxy_url|http_proxy|https_proxy)\s*[:=]\s*[^"'\\\s,}\]]+"#).unwrap(); diff --git a/src-tauri/src/proxy/upstream/retry.rs b/src-tauri/src/proxy/upstream/retry.rs index 03589250d..03438fd5f 100644 --- a/src-tauri/src/proxy/upstream/retry.rs +++ b/src-tauri/src/proxy/upstream/retry.rs @@ -58,7 +58,7 @@ pub fn parse_duration_ms(duration_str: &str) -> Option { /// 从 429 错误中提取 retry delay (深度递归解析) pub fn parse_retry_delay(error_text: &str) -> Option { - use serde_json::Value; + // Removed unused import: use serde_json::Value; // 1. 尝试正则提取 (针对非 JSON 文本或嵌套不深的文本) for re in RE_QUOTA_PATTERNS.iter() { diff --git a/src-tauri/src/utils/command.rs b/src-tauri/src/utils/command.rs index 4f6b43e09..bd9bf2d88 100644 --- a/src-tauri/src/utils/command.rs +++ b/src-tauri/src/utils/command.rs @@ -1,3 +1,5 @@ +#![cfg(target_os = "windows")] + use std::process::Command as StdCommand; use tokio::process::Command as TokioCommand; diff --git a/src-tauri/src/utils/crypto.rs b/src-tauri/src/utils/crypto.rs index 4c6823ac5..d976a552f 100644 --- a/src-tauri/src/utils/crypto.rs +++ b/src-tauri/src/utils/crypto.rs @@ -119,6 +119,7 @@ fn decrypt_string_v2(encrypted: &str) -> Result { String::from_utf8(plaintext).map_err(|e| format!("UTF-8 conversion failed: {}", e)) } +#[allow(dead_code)] pub fn decrypt_string(encrypted: &str) -> Result { if encrypted.starts_with(ENCRYPTED_V2_PREFIX) { decrypt_string_v2(&encrypted[ENCRYPTED_V2_PREFIX.len()..]) diff --git a/src-tauri/src/utils/http.rs b/src-tauri/src/utils/http.rs index 8af711e47..14f62b642 100644 --- a/src-tauri/src/utils/http.rs +++ b/src-tauri/src/utils/http.rs @@ -8,6 +8,7 @@ use rquest_util::Emulation; pub static SHARED_CLIENT: Lazy = Lazy::new(|| create_base_client(15)); /// Global shared HTTP client (Long timeout: 60s, for warmup etc.) +#[allow(dead_code)] pub static SHARED_CLIENT_LONG: Lazy = Lazy::new(|| create_base_client(60)); /// Global shared standard HTTP client (15s timeout, NO JA3 Emulation) @@ -50,6 +51,7 @@ pub fn get_client() -> Client { } /// Get long timeout HTTP client (60s timeout) +#[allow(dead_code)] pub fn get_long_client() -> Client { SHARED_CLIENT_LONG.clone() } diff --git a/src/locales/ar.json b/src/locales/ar.json index 3aa95d802..3e033276a 100644 --- a/src/locales/ar.json +++ b/src/locales/ar.json @@ -1015,7 +1015,7 @@ "placeholder": "أدخل كلمة مرور الإدارة أو مفتاح API", "btn_login": "تحقق ودخول", "note": "ملاحظة: إذا تم تعيين كلمة مرور إدارة منفصلة، يرجى إدخالها؛ وإلا، أدخل مفتاح API.", - "lookup_hint": "إذا نسيت، قم بتشغيل docker logs antigravity-manager للعثور على مفتاح API الحالي أو كلمة مرور واجهة الويب", + "lookup_hint": "View gui_config.json or check environment variables to find them.", "config_hint": "أو قم بتشغيل grep -E '\"api_key\"|\"admin_password\"' ~/.antigravity_tools/gui_config.json للعرض." }, "token_stats": { diff --git a/src/locales/en.json b/src/locales/en.json index 61821994d..0186c4d3d 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -1223,7 +1223,7 @@ "error_invalid_key": "Invalid password or API Key, please try again", "error_network": "Network connection failed, please check if the service is running", "note": "Note: If a separate management password is set, please enter it; otherwise, enter API_KEY.", - "lookup_hint": "If forgotten, run docker logs antigravity-manager to find Current API Key or Web UI Password", + "lookup_hint": "View gui_config.json or check environment variables to find them.", "config_hint": "Or run grep -E '\"api_key\"|\"admin_password\"' ~/.antigravity_tools/gui_config.json to view." }, "token_stats": { diff --git a/src/locales/es.json b/src/locales/es.json index d7432c409..7ba644896 100644 --- a/src/locales/es.json +++ b/src/locales/es.json @@ -1011,7 +1011,7 @@ "placeholder": "Ingrese contraseña de administración o Clave API", "btn_login": "Verificar y Entrar", "note": "Nota: Si se estableció una contraseña de administración separada, ingrésela; de lo contrario, ingrese la API_KEY.", - "lookup_hint": "Si olvidó, ejecute docker logs antigravity-manager para encontrar la Current API Key o Web UI Password", + "lookup_hint": "View gui_config.json or check environment variables to find them.", "config_hint": "O ejecute grep -E '\"api_key\"|\"admin_password\"' ~/.antigravity_tools/gui_config.json para ver." }, "token_stats": { diff --git a/src/locales/ja.json b/src/locales/ja.json index 9b2d3e312..536b93b94 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -1166,7 +1166,7 @@ "error_invalid_key": "パスワードまたは API キーが無効です。再試行してください", "error_network": "ネットワーク接続に失敗しました。サービスが実行中か確認してください", "note": "注意:独立した管理パスワードが設定されている場合は管理パスワードを入力してください。そうでない場合は API_KEY を入力してください。", - "lookup_hint": "お忘れの場合は、docker logs antigravity-manager を実行して Current API Key または Web UI Password を探してください", + "lookup_hint": "View gui_config.json or check environment variables to find them.", "config_hint": "または grep -E '\"api_key\"|\"admin_password\"' ~/.antigravity_tools/gui_config.json を実行して確認してください。" }, "token_stats": { diff --git a/src/locales/ko.json b/src/locales/ko.json index 0fa17b0f0..8d5ef5acf 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -1048,7 +1048,7 @@ "error_invalid_key": "비밀번호 또는 API 키가 잘못되었습니다. 다시 시도하십시오", "error_network": "네트워크 연결에 실패했습니다. 서비스가 실행 중인지 확인하십시오", "note": "참고: 별도의 관리 비밀번호가 설정된 경우 관리 비밀번호를 입력하십시오. 그렇지 않으면 API_KEY를 입력하십시오.", - "lookup_hint": "잊으신 경우 docker logs antigravity-manager를 실행하여 Current API Key 또는 Web UI Password를 찾으십시오.", + "lookup_hint": "View gui_config.json or check environment variables to find them.", "config_hint": "또는 grep -E '\"api_key\"|\"admin_password\"' ~/.antigravity_tools/gui_config.json을 실행하여 확인하십시오." }, "token_stats": { diff --git a/src/locales/my.json b/src/locales/my.json index e8101f699..a73013748 100644 --- a/src/locales/my.json +++ b/src/locales/my.json @@ -1011,7 +1011,7 @@ "placeholder": "Masukkan kata laluan pengurusan atau Kunci API", "btn_login": "Sahkan dan Masuk", "note": "Nota: Jika kata laluan pengurusan berasingan ditetapkan, sila masukkan; jika tidak, masukkan API_KEY.", - "lookup_hint": "Jika terlupa, jalankan docker logs antigravity-manager untuk mencari Current API Key atau Web UI Password", + "lookup_hint": "View gui_config.json or check environment variables to find them.", "config_hint": "Atau jalankan grep -E '\"api_key\"|\"admin_password\"' ~/.antigravity_tools/gui_config.json untuk melihat." }, "token_stats": { diff --git a/src/locales/pt.json b/src/locales/pt.json index cd92f22de..d4b33fb6e 100644 --- a/src/locales/pt.json +++ b/src/locales/pt.json @@ -1011,7 +1011,7 @@ "placeholder": "Insira a senha de administrador ou a Chave de API", "btn_login": "Verificar e Entrar", "note": "Nota: Se uma senha de administração separada estiver definida, insira-a; caso contrário, insira a API_KEY.", - "lookup_hint": "Se esqueceu, execute docker logs antigravity-manager para encontrar a Chave de API atual ou a senha da Web UI.", + "lookup_hint": "View gui_config.json or check environment variables to find them.", "config_hint": "Ou execute grep -E '\"api_key\"|\"admin_password\"' ~/.antigravity_tools/gui_config.json para visualizar." }, "errors": { diff --git a/src/locales/ru.json b/src/locales/ru.json index a26934d57..f8fbf02f0 100644 --- a/src/locales/ru.json +++ b/src/locales/ru.json @@ -1015,7 +1015,7 @@ "placeholder": "Введите пароль администратора или ключ API", "btn_login": "Проверить и войти", "note": "Примечание: если установлен отдельный пароль управления, введите его; в противном случае введите API_KEY.", - "lookup_hint": "Если вы забыли, запустите docker logs antigravity-manager, чтобы найти текущий ключ API или пароль Web UI.", + "lookup_hint": "View gui_config.json or check environment variables to find them.", "config_hint": "Или выполните grep -E '\"api_key\"|\"admin_password\"' ~/.antigravity_tools/gui_config.json для просмотра." }, "token_stats": { diff --git a/src/locales/tr.json b/src/locales/tr.json index de4b8a4e9..c1d2398a3 100644 --- a/src/locales/tr.json +++ b/src/locales/tr.json @@ -989,7 +989,7 @@ "placeholder": "Yönetici parolasını veya API Anahtarını girin", "btn_login": "Doğrula ve Gir", "note": "Not: Ayarlanmış ayrı bir yönetici parolası varsa, lütfen onu girin; aksi takdirde API_KEY girin.", - "lookup_hint": "Unuttuysanız, Mevcut API Anahtarını veya Web Kullanıcı Arayüzü Parolasını bulmak için docker logs antigravity-manager komutunu çalıştırın.", + "lookup_hint": "View gui_config.json or check environment variables to find them.", "config_hint": "Veya görüntülemek için grep -E '\"api_key\"|\"admin_password\"' ~/.antigravity_tools/gui_config.json komutunu çalıştırın." }, "token_stats": { diff --git a/src/locales/vi.json b/src/locales/vi.json index 7e0e5594c..d5aff3ecd 100644 --- a/src/locales/vi.json +++ b/src/locales/vi.json @@ -1038,7 +1038,7 @@ "placeholder": "Nhập mật khẩu quản trị hoặc API Key", "btn_login": "Xác minh và Vào", "note": "Lưu ý: Nếu đã thiết lập mật khẩu quản trị riêng, vui lòng nhập mật khẩu đó; nếu không, hãy nhập API_KEY.", - "lookup_hint": "Nếu quên, hãy chạy lệnh docker logs antigravity-manager để tìm API Key hiện tại hoặc Mật khẩu Web UI.", + "lookup_hint": "View gui_config.json or check environment variables to find them.", "config_hint": "Hoặc chạy lệnh grep -E '\"api_key\"|\"admin_password\"' ~/.antigravity_tools/gui_config.json để xem." }, "token_stats": { diff --git a/src/locales/zh-TW.json b/src/locales/zh-TW.json index c81cfcf52..260a9b14a 100644 --- a/src/locales/zh-TW.json +++ b/src/locales/zh-TW.json @@ -1,4 +1,4 @@ -{ +{ "common": { "empty": "空", "loading": "載入中...", @@ -1048,7 +1048,7 @@ "error_invalid_key": "密碼或 API Key 錯誤,請重試", "error_network": "網路連線失敗,請檢查服務是否正常運行", "note": "注意:如果設置了獨立的管理密碼,請輸入管理密碼;否則請輸入 API_KEY。", - "lookup_hint": "如果您忘記了,請運行 docker logs antigravity-manager 尋找 Current API Key 或 Web UI Password", + "lookup_hint": "請查看 gui_config.json 或檢查環境變數以取得它們。", "config_hint": "或執行 grep -E '\"api_key\"|\"admin_password\"' ~/.antigravity_tools/gui_config.json 查看。" }, "token_stats": { diff --git a/src/locales/zh.json b/src/locales/zh.json index 748c27901..f781eb752 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -1185,7 +1185,7 @@ "error_invalid_key": "密码或 API Key 错误,请重试", "error_network": "网络连接失败,请检查服务是否正常运行", "note": "注意:如果设置了独立的管理密码,请输入管理密码;否则请输入 API_KEY。", - "lookup_hint": "如果您忘记了,请运行 docker logs antigravity-manager 寻找 Current API Key 或 Web UI Password", + "lookup_hint": "请查看 gui_config.json 或检查环境变量以获取它们。", "config_hint": "或执行 grep -E '\"api_key\"|\"admin_password\"' ~/.antigravity_tools/gui_config.json 查看。" }, "token_stats": { diff --git a/src/stores/useAccountStore.ts b/src/stores/useAccountStore.ts index 26580e019..81f6acb12 100644 --- a/src/stores/useAccountStore.ts +++ b/src/stores/useAccountStore.ts @@ -107,7 +107,20 @@ export const useAccountStore = create((set, get) => ({ set({ loading: true, error: null }); try { await accountService.switchAccount(accountId, targetIde); - await get().fetchCurrentAccount(); + await Promise.all([ + get().fetchCurrentAccount(), + get().fetchAccounts() + ]); + + // Asynchronously refresh quota to update the dashboard, do not block the UI + // NOTE: We call the service directly instead of get().refreshQuota() to avoid + // re-setting loading=true after we just set loading=false. + accountService.fetchAccountQuota(accountId) + .then(() => Promise.all([get().fetchAccounts(), get().fetchCurrentAccount()])) + .catch(e => { + console.error('[Store] Background quota refresh failed after switch:', e); + }); + set({ loading: false }); } catch (error) { set({ error: String(error), loading: false }); @@ -120,6 +133,12 @@ export const useAccountStore = create((set, get) => ({ try { await accountService.fetchAccountQuota(accountId); await get().fetchAccounts(); + + const { currentAccount } = get(); + if (currentAccount && currentAccount.id === accountId) { + await get().fetchCurrentAccount(); + } + set({ loading: false }); } catch (error) { set({ error: String(error), loading: false }); diff --git a/vite.config.ts b/vite.config.ts index c8793496b..992f561ba 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -7,6 +7,9 @@ const host = process.env.TAURI_DEV_HOST; // https://vite.dev/config/ export default defineConfig(async () => ({ plugins: [react()], + build: { + chunkSizeWarningLimit: 1500, + }, // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build` //