mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-01-29 00:35:38 +08:00
fix: clippy errors with new config (#4428)
* refactor: improve code quality with clippy fixes and standardized logging
- Replace dangerous unwrap()/expect() calls with proper error handling
- Standardize logging from log:: to logging\! macro with Type:: classifications
- Fix app handle panics with graceful fallback patterns
- Improve error resilience across 35+ modules without breaking functionality
- Reduce clippy warnings from 300+ to 0 in main library code
* chore: update Cargo.toml configuration
* refactor: resolve all clippy warnings
- Fix Arc clone warnings using explicit Arc::clone syntax across 9 files
- Add #[allow(clippy::expect_used)] to test functions for appropriate expect usage
- Remove no-effect statements from debug code cleanup
- Apply clippy auto-fixes for dbg\! macro removals and path statements
- Achieve zero clippy warnings on all targets with -D warnings flag
* chore: update Cargo.toml clippy configuration
* refactor: simplify macOS job configuration and improve caching
* refactor: remove unnecessary async/await from service and proxy functions
* refactor: streamline pnpm installation in CI configuration
* refactor: simplify error handling and remove unnecessary else statements
* refactor: replace async/await with synchronous locks for core management
* refactor: add workflow_dispatch trigger to clippy job
* refactor: convert async functions to synchronous for service management
* refactor: convert async functions to synchronous for UWP tool invocation
* fix: change wrong logging
* refactor: convert proxy restoration functions to async
* Revert "refactor: convert proxy restoration functions to async"
This reverts commit b82f5d250b.
* refactor: update proxy restoration functions to return Result types
* fix: handle errors during proxy restoration and update async function signatures
* fix: handle errors during proxy restoration and update async function signatures
* refactor: update restore_pac_proxy and restore_sys_proxy functions to async
* fix: convert restore_pac_proxy and restore_sys_proxy functions to async
* fix: await restore_sys_proxy calls in proxy restoration logic
* fix: suppress clippy warnings for unused async functions in proxy restoration
* fix: suppress clippy warnings for unused async functions in proxy restoration
This commit is contained in:
@@ -121,9 +121,8 @@ pub async fn download_icon_cache(url: String, name: String) -> CmdResult<String>
|
||||
Err(_) => {
|
||||
if icon_path.exists() {
|
||||
return Ok(icon_path.to_string_lossy().to_string());
|
||||
} else {
|
||||
return Err("Failed to create temporary file".into());
|
||||
}
|
||||
return Err("Failed to create temporary file".into());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ pub async fn patch_clash_mode(payload: String) -> CmdResult {
|
||||
/// 切换Clash核心
|
||||
#[tauri::command]
|
||||
pub async fn change_clash_core(clash_core: String) -> CmdResult<Option<String>> {
|
||||
log::info!(target: "app", "changing core to {clash_core}");
|
||||
logging!(info, Type::Config, "changing core to {clash_core}");
|
||||
|
||||
match CoreManager::global()
|
||||
.change_core(Some(clash_core.clone()))
|
||||
@@ -54,14 +54,18 @@ pub async fn change_clash_core(clash_core: String) -> CmdResult<Option<String>>
|
||||
// 切换内核后重启内核
|
||||
match CoreManager::global().restart_core().await {
|
||||
Ok(_) => {
|
||||
log::info!(target: "app", "core changed and restarted to {clash_core}");
|
||||
logging!(
|
||||
info,
|
||||
Type::Core,
|
||||
"core changed and restarted to {clash_core}"
|
||||
);
|
||||
handle::Handle::notice_message("config_core::change_success", &clash_core);
|
||||
handle::Handle::refresh_clash();
|
||||
Ok(None)
|
||||
}
|
||||
Err(err) => {
|
||||
let error_msg = format!("Core changed but failed to restart: {err}");
|
||||
log::error!(target: "app", "{error_msg}");
|
||||
logging!(error, Type::Core, "{error_msg}");
|
||||
handle::Handle::notice_message("config_core::change_error", &error_msg);
|
||||
Ok(Some(error_msg))
|
||||
}
|
||||
@@ -69,7 +73,7 @@ pub async fn change_clash_core(clash_core: String) -> CmdResult<Option<String>>
|
||||
}
|
||||
Err(err) => {
|
||||
let error_msg = err.to_string();
|
||||
log::error!(target: "app", "failed to change core: {error_msg}");
|
||||
logging!(error, Type::Core, "failed to change core: {error_msg}");
|
||||
handle::Handle::notice_message("config_core::change_error", &error_msg);
|
||||
Ok(Some(error_msg))
|
||||
}
|
||||
@@ -141,7 +145,7 @@ pub async fn save_dns_config(dns_config: Mapping) -> CmdResult {
|
||||
// 保存DNS配置到文件
|
||||
let yaml_str = serde_yaml::to_string(&dns_config).map_err(|e| e.to_string())?;
|
||||
fs::write(&dns_path, yaml_str).map_err(|e| e.to_string())?;
|
||||
log::info!(target: "app", "DNS config saved to {dns_path:?}");
|
||||
logging!(info, Type::Config, "DNS config saved to {dns_path:?}");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -162,20 +166,20 @@ pub fn apply_dns_config(apply: bool) -> CmdResult {
|
||||
let dns_path = match dirs::app_home_dir() {
|
||||
Ok(path) => path.join("dns_config.yaml"),
|
||||
Err(e) => {
|
||||
log::error!(target: "app", "Failed to get home dir: {e}");
|
||||
logging!(error, Type::Config, "Failed to get home dir: {e}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if !dns_path.exists() {
|
||||
log::warn!(target: "app", "DNS config file not found");
|
||||
logging!(warn, Type::Config, "DNS config file not found");
|
||||
return;
|
||||
}
|
||||
|
||||
let dns_yaml = match std::fs::read_to_string(&dns_path) {
|
||||
Ok(content) => content,
|
||||
Err(e) => {
|
||||
log::error!(target: "app", "Failed to read DNS config: {e}");
|
||||
logging!(error, Type::Config, "Failed to read DNS config: {e}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
@@ -188,12 +192,12 @@ pub fn apply_dns_config(apply: bool) -> CmdResult {
|
||||
patch
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!(target: "app", "Failed to parse DNS config: {e}");
|
||||
logging!(error, Type::Config, "Failed to parse DNS config: {e}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
log::info!(target: "app", "Applying DNS config from file");
|
||||
logging!(info, Type::Config, "Applying DNS config from file");
|
||||
|
||||
// 重新生成配置,确保DNS配置被正确应用
|
||||
// 这里不调用patch_clash以避免将DNS配置写入config.yaml
|
||||
@@ -202,37 +206,53 @@ pub fn apply_dns_config(apply: bool) -> CmdResult {
|
||||
.patch_config(patch_config.clone());
|
||||
|
||||
// 首先重新生成配置
|
||||
if let Err(err) = Config::generate().await {
|
||||
log::error!(target: "app", "Failed to regenerate config with DNS: {err}");
|
||||
if let Err(err) = Config::generate() {
|
||||
logging!(
|
||||
error,
|
||||
Type::Config,
|
||||
"Failed to regenerate config with DNS: {err}"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// 然后应用新配置
|
||||
if let Err(err) = CoreManager::global().update_config().await {
|
||||
log::error!(target: "app", "Failed to apply config with DNS: {err}");
|
||||
logging!(
|
||||
error,
|
||||
Type::Config,
|
||||
"Failed to apply config with DNS: {err}"
|
||||
);
|
||||
} else {
|
||||
log::info!(target: "app", "DNS config successfully applied");
|
||||
logging!(info, Type::Config, "DNS config successfully applied");
|
||||
handle::Handle::refresh_clash();
|
||||
}
|
||||
} else {
|
||||
// 当关闭DNS设置时,不需要对配置进行任何修改
|
||||
// 直接重新生成配置,让enhance函数自动跳过DNS配置的加载
|
||||
log::info!(target: "app", "DNS settings disabled, regenerating config");
|
||||
logging!(
|
||||
info,
|
||||
Type::Config,
|
||||
"DNS settings disabled, regenerating config"
|
||||
);
|
||||
|
||||
// 重新生成配置
|
||||
if let Err(err) = Config::generate().await {
|
||||
log::error!(target: "app", "Failed to regenerate config: {err}");
|
||||
if let Err(err) = Config::generate() {
|
||||
logging!(error, Type::Config, "Failed to regenerate config: {err}");
|
||||
return;
|
||||
}
|
||||
|
||||
// 应用新配置
|
||||
match CoreManager::global().update_config().await {
|
||||
Ok(_) => {
|
||||
log::info!(target: "app", "Config regenerated successfully");
|
||||
logging!(info, Type::Config, "Config regenerated successfully");
|
||||
handle::Handle::refresh_clash();
|
||||
}
|
||||
Err(err) => {
|
||||
log::error!(target: "app", "Failed to apply regenerated config: {err}");
|
||||
logging!(
|
||||
error,
|
||||
Type::Config,
|
||||
"Failed to apply regenerated config: {err}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -307,7 +327,10 @@ pub async fn get_clash_config() -> CmdResult<serde_json::Value> {
|
||||
let key = ProxyRequestCache::make_key("clash_config", "default");
|
||||
let value = cache
|
||||
.get_or_fetch(key, CONFIG_REFRESH_INTERVAL, || async {
|
||||
manager.get_config().await.expect("fetch failed")
|
||||
manager.get_config().await.unwrap_or_else(|e| {
|
||||
logging!(error, Type::Cmd, "Failed to fetch clash config: {e}");
|
||||
serde_json::Value::Object(serde_json::Map::new())
|
||||
})
|
||||
})
|
||||
.await;
|
||||
Ok((*value).clone())
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::{logging, utils::logging::Type};
|
||||
use chrono::Local;
|
||||
use regex::Regex;
|
||||
use reqwest::Client;
|
||||
@@ -250,7 +251,23 @@ async fn check_gemini(client: &Client) -> UnlockItem {
|
||||
let status = if is_ok { "Yes" } else { "No" };
|
||||
|
||||
// 尝试提取国家代码
|
||||
let re = Regex::new(r#",2,1,200,"([A-Z]{3})""#).unwrap();
|
||||
let re = match Regex::new(r#",2,1,200,"([A-Z]{3})""#) {
|
||||
Ok(re) => re,
|
||||
Err(e) => {
|
||||
logging!(
|
||||
error,
|
||||
Type::Network,
|
||||
"Failed to compile Gemini regex: {}",
|
||||
e
|
||||
);
|
||||
return UnlockItem {
|
||||
name: "Gemini".to_string(),
|
||||
status: "Failed".to_string(),
|
||||
region: None,
|
||||
check_time: Some(get_local_date_string()),
|
||||
};
|
||||
}
|
||||
};
|
||||
let region = re.captures(&body).and_then(|caps| {
|
||||
caps.get(1).map(|m| {
|
||||
let country_code = m.as_str();
|
||||
@@ -303,7 +320,23 @@ async fn check_youtube_premium(client: &Client) -> UnlockItem {
|
||||
}
|
||||
} else if body_lower.contains("ad-free") {
|
||||
// 尝试解析国家代码
|
||||
let re = Regex::new(r#"id="country-code"[^>]*>([^<]+)<"#).unwrap();
|
||||
let re = match Regex::new(r#"id="country-code"[^>]*>([^<]+)<"#) {
|
||||
Ok(re) => re,
|
||||
Err(e) => {
|
||||
logging!(
|
||||
error,
|
||||
Type::Network,
|
||||
"Failed to compile YouTube Premium regex: {}",
|
||||
e
|
||||
);
|
||||
return UnlockItem {
|
||||
name: "Youtube Premium".to_string(),
|
||||
status: "Failed".to_string(),
|
||||
region: None,
|
||||
check_time: Some(get_local_date_string()),
|
||||
};
|
||||
}
|
||||
};
|
||||
let region = re.captures(&body).and_then(|caps| {
|
||||
caps.get(1).map(|m| {
|
||||
let country_code = m.as_str().trim();
|
||||
@@ -350,11 +383,16 @@ async fn check_bahamut_anime(client: &Client) -> UnlockItem {
|
||||
let cookie_store = Arc::new(reqwest::cookie::Jar::default());
|
||||
|
||||
// 使用带Cookie的客户端
|
||||
let client_with_cookies = reqwest::Client::builder()
|
||||
let client_with_cookies = match reqwest::Client::builder()
|
||||
.user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36")
|
||||
.cookie_provider(Arc::clone(&cookie_store))
|
||||
.build()
|
||||
.unwrap_or_else(|_| client.clone());
|
||||
.build() {
|
||||
Ok(client) => client,
|
||||
Err(e) => {
|
||||
logging!(error, Type::Network, "Failed to create client with cookies for Bahamut Anime: {}", e);
|
||||
client.clone()
|
||||
}
|
||||
};
|
||||
|
||||
// 第一步:获取设备ID (会自动保存Cookie)
|
||||
let device_url = "https://ani.gamer.com.tw/ajax/getdeviceid.php";
|
||||
@@ -363,10 +401,21 @@ async fn check_bahamut_anime(client: &Client) -> UnlockItem {
|
||||
match response.text().await {
|
||||
Ok(text) => {
|
||||
// 使用正则提取deviceid
|
||||
let re = Regex::new(r#""deviceid"\s*:\s*"([^"]+)"#).unwrap();
|
||||
re.captures(&text)
|
||||
.and_then(|caps| caps.get(1).map(|m| m.as_str().to_string()))
|
||||
.unwrap_or_default()
|
||||
match Regex::new(r#""deviceid"\s*:\s*"([^"]+)"#) {
|
||||
Ok(re) => re
|
||||
.captures(&text)
|
||||
.and_then(|caps| caps.get(1).map(|m| m.as_str().to_string()))
|
||||
.unwrap_or_default(),
|
||||
Err(e) => {
|
||||
logging!(
|
||||
error,
|
||||
Type::Network,
|
||||
"Failed to compile deviceid regex for Bahamut Anime: {}",
|
||||
e
|
||||
);
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => String::new(),
|
||||
}
|
||||
@@ -421,17 +470,25 @@ async fn check_bahamut_anime(client: &Client) -> UnlockItem {
|
||||
.await
|
||||
{
|
||||
Ok(response) => match response.text().await {
|
||||
Ok(body) => {
|
||||
let region_re = Regex::new(r#"data-geo="([^"]+)"#).unwrap();
|
||||
region_re
|
||||
Ok(body) => match Regex::new(r#"data-geo="([^"]+)"#) {
|
||||
Ok(region_re) => region_re
|
||||
.captures(&body)
|
||||
.and_then(|caps| caps.get(1))
|
||||
.map(|m| {
|
||||
let country_code = m.as_str();
|
||||
let emoji = country_code_to_emoji(country_code);
|
||||
format!("{emoji}{country_code}")
|
||||
})
|
||||
}
|
||||
}),
|
||||
Err(e) => {
|
||||
logging!(
|
||||
error,
|
||||
Type::Network,
|
||||
"Failed to compile region regex for Bahamut Anime: {}",
|
||||
e
|
||||
);
|
||||
None
|
||||
}
|
||||
},
|
||||
Err(_) => None,
|
||||
},
|
||||
Err(_) => None,
|
||||
@@ -495,8 +552,40 @@ async fn check_netflix(client: &Client) -> UnlockItem {
|
||||
}
|
||||
|
||||
// 获取状态码
|
||||
let status1 = result1.unwrap().status().as_u16();
|
||||
let status2 = result2.unwrap().status().as_u16();
|
||||
let status1 = match result1 {
|
||||
Ok(response) => response.status().as_u16(),
|
||||
Err(e) => {
|
||||
logging!(
|
||||
error,
|
||||
Type::Network,
|
||||
"Failed to get Netflix response 1: {}",
|
||||
e
|
||||
);
|
||||
return UnlockItem {
|
||||
name: "Netflix".to_string(),
|
||||
status: "Failed".to_string(),
|
||||
region: None,
|
||||
check_time: Some(get_local_date_string()),
|
||||
};
|
||||
}
|
||||
};
|
||||
let status2 = match result2 {
|
||||
Ok(response) => response.status().as_u16(),
|
||||
Err(e) => {
|
||||
logging!(
|
||||
error,
|
||||
Type::Network,
|
||||
"Failed to get Netflix response 2: {}",
|
||||
e
|
||||
);
|
||||
return UnlockItem {
|
||||
name: "Netflix".to_string(),
|
||||
status: "Failed".to_string(),
|
||||
region: None,
|
||||
check_time: Some(get_local_date_string()),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// 根据状态码判断解锁状况
|
||||
if status1 == 404 && status2 == 404 {
|
||||
@@ -685,7 +774,23 @@ async fn check_disney_plus(client: &Client) -> UnlockItem {
|
||||
};
|
||||
}
|
||||
|
||||
let device_response = device_result.unwrap();
|
||||
let device_response = match device_result {
|
||||
Ok(response) => response,
|
||||
Err(e) => {
|
||||
logging!(
|
||||
error,
|
||||
Type::Network,
|
||||
"Failed to get Disney+ device response: {}",
|
||||
e
|
||||
);
|
||||
return UnlockItem {
|
||||
name: "Disney+".to_string(),
|
||||
status: "Failed (Network Connection)".to_string(),
|
||||
region: None,
|
||||
check_time: Some(get_local_date_string()),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// 检查是否 403 错误
|
||||
if device_response.status().as_u16() == 403 {
|
||||
@@ -710,7 +815,23 @@ async fn check_disney_plus(client: &Client) -> UnlockItem {
|
||||
};
|
||||
|
||||
// 提取 assertion
|
||||
let re = Regex::new(r#""assertion"\s*:\s*"([^"]+)"#).unwrap();
|
||||
let re = match Regex::new(r#""assertion"\s*:\s*"([^"]+)"#) {
|
||||
Ok(re) => re,
|
||||
Err(e) => {
|
||||
logging!(
|
||||
error,
|
||||
Type::Network,
|
||||
"Failed to compile assertion regex for Disney+: {}",
|
||||
e
|
||||
);
|
||||
return UnlockItem {
|
||||
name: "Disney+".to_string(),
|
||||
status: "Failed (Regex Error)".to_string(),
|
||||
region: None,
|
||||
check_time: Some(get_local_date_string()),
|
||||
};
|
||||
}
|
||||
};
|
||||
let assertion = match re.captures(&device_body) {
|
||||
Some(caps) => caps.get(1).map(|m| m.as_str().to_string()),
|
||||
None => None,
|
||||
@@ -729,7 +850,18 @@ async fn check_disney_plus(client: &Client) -> UnlockItem {
|
||||
let token_url = "https://disney.api.edge.bamgrid.com/token";
|
||||
|
||||
// 构建请求体 - 使用表单数据格式而非 JSON
|
||||
let assertion_str = assertion.unwrap();
|
||||
let assertion_str = match assertion {
|
||||
Some(assertion) => assertion,
|
||||
None => {
|
||||
logging!(error, Type::Network, "No assertion found for Disney+");
|
||||
return UnlockItem {
|
||||
name: "Disney+".to_string(),
|
||||
status: "Failed (No Assertion)".to_string(),
|
||||
region: None,
|
||||
check_time: Some(get_local_date_string()),
|
||||
};
|
||||
}
|
||||
};
|
||||
let token_body = [
|
||||
(
|
||||
"grant_type",
|
||||
@@ -762,7 +894,23 @@ async fn check_disney_plus(client: &Client) -> UnlockItem {
|
||||
};
|
||||
}
|
||||
|
||||
let token_response = token_result.unwrap();
|
||||
let token_response = match token_result {
|
||||
Ok(response) => response,
|
||||
Err(e) => {
|
||||
logging!(
|
||||
error,
|
||||
Type::Network,
|
||||
"Failed to get Disney+ token response: {}",
|
||||
e
|
||||
);
|
||||
return UnlockItem {
|
||||
name: "Disney+".to_string(),
|
||||
status: "Failed (Network Connection)".to_string(),
|
||||
region: None,
|
||||
check_time: Some(get_local_date_string()),
|
||||
};
|
||||
}
|
||||
};
|
||||
let token_status = token_response.status();
|
||||
|
||||
// 保存原始响应用于调试
|
||||
@@ -798,10 +946,20 @@ async fn check_disney_plus(client: &Client) -> UnlockItem {
|
||||
.map(|s| s.to_string()),
|
||||
Err(_) => {
|
||||
// 如果 JSON 解析失败,尝试使用正则表达式
|
||||
let refresh_token_re = Regex::new(r#""refresh_token"\s*:\s*"([^"]+)"#).unwrap();
|
||||
refresh_token_re
|
||||
.captures(&token_body_text)
|
||||
.and_then(|caps| caps.get(1).map(|m| m.as_str().to_string()))
|
||||
match Regex::new(r#""refresh_token"\s*:\s*"([^"]+)"#) {
|
||||
Ok(refresh_token_re) => refresh_token_re
|
||||
.captures(&token_body_text)
|
||||
.and_then(|caps| caps.get(1).map(|m| m.as_str().to_string())),
|
||||
Err(e) => {
|
||||
logging!(
|
||||
error,
|
||||
Type::Network,
|
||||
"Failed to compile refresh_token regex for Disney+: {}",
|
||||
e
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -825,7 +983,7 @@ async fn check_disney_plus(client: &Client) -> UnlockItem {
|
||||
// GraphQL API 通常接受 JSON 格式
|
||||
let graphql_payload = format!(
|
||||
r#"{{"query":"mutation refreshToken($input: RefreshTokenInput!) {{ refreshToken(refreshToken: $input) {{ activeSession {{ sessionId }} }} }}","variables":{{"input":{{"refreshToken":"{}"}}}}}}"#,
|
||||
refresh_token.unwrap()
|
||||
refresh_token.unwrap_or_default()
|
||||
);
|
||||
|
||||
let graphql_result = client
|
||||
@@ -857,21 +1015,56 @@ async fn check_disney_plus(client: &Client) -> UnlockItem {
|
||||
};
|
||||
|
||||
// 解析 GraphQL 响应获取区域信息
|
||||
let graphql_response = graphql_result.unwrap();
|
||||
let graphql_response = match graphql_result {
|
||||
Ok(response) => response,
|
||||
Err(e) => {
|
||||
logging!(
|
||||
error,
|
||||
Type::Network,
|
||||
"Failed to get Disney+ GraphQL response: {}",
|
||||
e
|
||||
);
|
||||
return UnlockItem {
|
||||
name: "Disney+".to_string(),
|
||||
status: "Failed (Network Connection)".to_string(),
|
||||
region: None,
|
||||
check_time: Some(get_local_date_string()),
|
||||
};
|
||||
}
|
||||
};
|
||||
let graphql_status = graphql_response.status();
|
||||
let graphql_body_text = (graphql_response.text().await).unwrap_or_default();
|
||||
let graphql_body_text = match graphql_response.text().await {
|
||||
Ok(text) => text,
|
||||
Err(e) => {
|
||||
logging!(
|
||||
error,
|
||||
Type::Network,
|
||||
"Failed to read Disney+ GraphQL response text: {}",
|
||||
e
|
||||
);
|
||||
String::new()
|
||||
}
|
||||
};
|
||||
|
||||
// 如果 GraphQL 响应为空或明显错误,尝试直接获取区域信息
|
||||
if graphql_body_text.is_empty() || graphql_status.as_u16() >= 400 {
|
||||
// 尝试直接从主页获取区域信息
|
||||
let region_from_main = match client.get("https://www.disneyplus.com/").send().await {
|
||||
Ok(response) => match response.text().await {
|
||||
Ok(body) => {
|
||||
let region_re = Regex::new(r#"region"\s*:\s*"([^"]+)"#).unwrap();
|
||||
region_re
|
||||
Ok(body) => match Regex::new(r#"region"\s*:\s*"([^"]+)"#) {
|
||||
Ok(region_re) => region_re
|
||||
.captures(&body)
|
||||
.and_then(|caps| caps.get(1).map(|m| m.as_str().to_string()))
|
||||
}
|
||||
.and_then(|caps| caps.get(1).map(|m| m.as_str().to_string())),
|
||||
Err(e) => {
|
||||
logging!(
|
||||
error,
|
||||
Type::Network,
|
||||
"Failed to compile Disney+ main page region regex: {}",
|
||||
e
|
||||
);
|
||||
None
|
||||
}
|
||||
},
|
||||
Err(_) => None,
|
||||
},
|
||||
Err(_) => None,
|
||||
@@ -898,28 +1091,59 @@ async fn check_disney_plus(client: &Client) -> UnlockItem {
|
||||
region: None,
|
||||
check_time: Some(get_local_date_string()),
|
||||
};
|
||||
} else {
|
||||
}
|
||||
return UnlockItem {
|
||||
name: "Disney+".to_string(),
|
||||
status: format!(
|
||||
"Failed (GraphQL error: {}, status: {})",
|
||||
graphql_body_text.chars().take(50).collect::<String>() + "...",
|
||||
graphql_status.as_u16()
|
||||
),
|
||||
region: None,
|
||||
check_time: Some(get_local_date_string()),
|
||||
};
|
||||
}
|
||||
|
||||
// 提取国家代码
|
||||
let region_re = match Regex::new(r#""countryCode"\s*:\s*"([^"]+)"#) {
|
||||
Ok(re) => re,
|
||||
Err(e) => {
|
||||
logging!(
|
||||
error,
|
||||
Type::Network,
|
||||
"Failed to compile Disney+ countryCode regex: {}",
|
||||
e
|
||||
);
|
||||
return UnlockItem {
|
||||
name: "Disney+".to_string(),
|
||||
status: format!(
|
||||
"Failed (GraphQL error: {}, status: {})",
|
||||
graphql_body_text.chars().take(50).collect::<String>() + "...",
|
||||
graphql_status.as_u16()
|
||||
),
|
||||
status: "Failed (Regex Error)".to_string(),
|
||||
region: None,
|
||||
check_time: Some(get_local_date_string()),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 提取国家代码
|
||||
let region_re = Regex::new(r#""countryCode"\s*:\s*"([^"]+)"#).unwrap();
|
||||
};
|
||||
let region_code = region_re
|
||||
.captures(&graphql_body_text)
|
||||
.and_then(|caps| caps.get(1).map(|m| m.as_str().to_string()));
|
||||
|
||||
// 提取支持状态
|
||||
let supported_re = Regex::new(r#""inSupportedLocation"\s*:\s*(false|true)"#).unwrap();
|
||||
let supported_re = match Regex::new(r#""inSupportedLocation"\s*:\s*(false|true)"#) {
|
||||
Ok(re) => re,
|
||||
Err(e) => {
|
||||
logging!(
|
||||
error,
|
||||
Type::Network,
|
||||
"Failed to compile Disney+ supported location regex: {}",
|
||||
e
|
||||
);
|
||||
return UnlockItem {
|
||||
name: "Disney+".to_string(),
|
||||
status: "Failed (Regex Error)".to_string(),
|
||||
region: None,
|
||||
check_time: Some(get_local_date_string()),
|
||||
};
|
||||
}
|
||||
};
|
||||
let in_supported_location = supported_re
|
||||
.captures(&graphql_body_text)
|
||||
.and_then(|caps| caps.get(1).map(|m| m.as_str() == "true"));
|
||||
@@ -929,12 +1153,20 @@ async fn check_disney_plus(client: &Client) -> UnlockItem {
|
||||
// 尝试直接从主页获取区域信息
|
||||
let region_from_main = match client.get("https://www.disneyplus.com/").send().await {
|
||||
Ok(response) => match response.text().await {
|
||||
Ok(body) => {
|
||||
let region_re = Regex::new(r#"region"\s*:\s*"([^"]+)"#).unwrap();
|
||||
region_re
|
||||
Ok(body) => match Regex::new(r#"region"\s*:\s*"([^"]+)"#) {
|
||||
Ok(region_re) => region_re
|
||||
.captures(&body)
|
||||
.and_then(|caps| caps.get(1).map(|m| m.as_str().to_string()))
|
||||
}
|
||||
.and_then(|caps| caps.get(1).map(|m| m.as_str().to_string())),
|
||||
Err(e) => {
|
||||
logging!(
|
||||
error,
|
||||
Type::Network,
|
||||
"Failed to compile Disney+ main page region regex: {}",
|
||||
e
|
||||
);
|
||||
None
|
||||
}
|
||||
},
|
||||
Err(_) => None,
|
||||
},
|
||||
Err(_) => None,
|
||||
@@ -958,7 +1190,18 @@ async fn check_disney_plus(client: &Client) -> UnlockItem {
|
||||
};
|
||||
}
|
||||
|
||||
let region = region_code.unwrap();
|
||||
let region = match region_code {
|
||||
Some(code) => code,
|
||||
None => {
|
||||
logging!(error, Type::Network, "No region code found for Disney+");
|
||||
return UnlockItem {
|
||||
name: "Disney+".to_string(),
|
||||
status: "No".to_string(),
|
||||
region: None,
|
||||
check_time: Some(get_local_date_string()),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// 判断日本地区
|
||||
if region == "JP" {
|
||||
@@ -1028,13 +1271,47 @@ async fn check_prime_video(client: &Client) -> UnlockItem {
|
||||
}
|
||||
|
||||
// 解析响应内容
|
||||
match result.unwrap().text().await {
|
||||
let response = match result {
|
||||
Ok(response) => response,
|
||||
Err(e) => {
|
||||
logging!(
|
||||
error,
|
||||
Type::Network,
|
||||
"Failed to get Prime Video response: {}",
|
||||
e
|
||||
);
|
||||
return UnlockItem {
|
||||
name: "Prime Video".to_string(),
|
||||
status: "Failed (Network Connection)".to_string(),
|
||||
region: None,
|
||||
check_time: Some(get_local_date_string()),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
match response.text().await {
|
||||
Ok(body) => {
|
||||
// 检查是否被地区限制
|
||||
let is_blocked = body.contains("isServiceRestricted");
|
||||
|
||||
// 提取地区信息
|
||||
let region_re = Regex::new(r#""currentTerritory":"([^"]+)"#).unwrap();
|
||||
let region_re = match Regex::new(r#""currentTerritory":"([^"]+)"#) {
|
||||
Ok(re) => re,
|
||||
Err(e) => {
|
||||
logging!(
|
||||
error,
|
||||
Type::Network,
|
||||
"Failed to compile Prime Video region regex: {}",
|
||||
e
|
||||
);
|
||||
return UnlockItem {
|
||||
name: "Prime Video".to_string(),
|
||||
status: "Failed (Regex Error)".to_string(),
|
||||
region: None,
|
||||
check_time: Some(get_local_date_string()),
|
||||
};
|
||||
}
|
||||
};
|
||||
let region_code = region_re
|
||||
.captures(&body)
|
||||
.and_then(|caps| caps.get(1).map(|m| m.as_str().to_string()));
|
||||
@@ -1182,8 +1459,8 @@ pub async fn check_media_unlock() -> Result<Vec<UnlockItem>, String> {
|
||||
|
||||
// 添加哔哩哔哩大陆检测任务
|
||||
{
|
||||
let client = client_arc.clone();
|
||||
let results = results.clone();
|
||||
let client = Arc::clone(&client_arc);
|
||||
let results = Arc::clone(&results);
|
||||
tasks.spawn(async move {
|
||||
let result = check_bilibili_china_mainland(&client).await;
|
||||
let mut results = results.lock().await;
|
||||
@@ -1193,8 +1470,8 @@ pub async fn check_media_unlock() -> Result<Vec<UnlockItem>, String> {
|
||||
|
||||
// 添加哔哩哔哩港澳台检测任务
|
||||
{
|
||||
let client = client_arc.clone();
|
||||
let results = results.clone();
|
||||
let client = Arc::clone(&client_arc);
|
||||
let results = Arc::clone(&results);
|
||||
tasks.spawn(async move {
|
||||
let result = check_bilibili_hk_mc_tw(&client).await;
|
||||
let mut results = results.lock().await;
|
||||
@@ -1204,8 +1481,8 @@ pub async fn check_media_unlock() -> Result<Vec<UnlockItem>, String> {
|
||||
|
||||
// 添加合并的ChatGPT检测任务
|
||||
{
|
||||
let client = client_arc.clone();
|
||||
let results = results.clone();
|
||||
let client = Arc::clone(&client_arc);
|
||||
let results = Arc::clone(&results);
|
||||
tasks.spawn(async move {
|
||||
let chatgpt_results = check_chatgpt_combined(&client).await;
|
||||
let mut results = results.lock().await;
|
||||
@@ -1215,8 +1492,8 @@ pub async fn check_media_unlock() -> Result<Vec<UnlockItem>, String> {
|
||||
|
||||
// 添加Gemini检测任务
|
||||
{
|
||||
let client = client_arc.clone();
|
||||
let results = results.clone();
|
||||
let client = Arc::clone(&client_arc);
|
||||
let results = Arc::clone(&results);
|
||||
tasks.spawn(async move {
|
||||
let result = check_gemini(&client).await;
|
||||
let mut results = results.lock().await;
|
||||
@@ -1226,8 +1503,8 @@ pub async fn check_media_unlock() -> Result<Vec<UnlockItem>, String> {
|
||||
|
||||
// 添加YouTube Premium检测任务
|
||||
{
|
||||
let client = client_arc.clone();
|
||||
let results = results.clone();
|
||||
let client = Arc::clone(&client_arc);
|
||||
let results = Arc::clone(&results);
|
||||
tasks.spawn(async move {
|
||||
let result = check_youtube_premium(&client).await;
|
||||
let mut results = results.lock().await;
|
||||
@@ -1237,8 +1514,8 @@ pub async fn check_media_unlock() -> Result<Vec<UnlockItem>, String> {
|
||||
|
||||
// 添加动画疯检测任务
|
||||
{
|
||||
let client = client_arc.clone();
|
||||
let results = results.clone();
|
||||
let client = Arc::clone(&client_arc);
|
||||
let results = Arc::clone(&results);
|
||||
tasks.spawn(async move {
|
||||
let result = check_bahamut_anime(&client).await;
|
||||
let mut results = results.lock().await;
|
||||
@@ -1248,8 +1525,8 @@ pub async fn check_media_unlock() -> Result<Vec<UnlockItem>, String> {
|
||||
|
||||
// 添加 Netflix 检测任务
|
||||
{
|
||||
let client = client_arc.clone();
|
||||
let results = results.clone();
|
||||
let client = Arc::clone(&client_arc);
|
||||
let results = Arc::clone(&results);
|
||||
tasks.spawn(async move {
|
||||
let result = check_netflix(&client).await;
|
||||
let mut results = results.lock().await;
|
||||
@@ -1259,8 +1536,8 @@ pub async fn check_media_unlock() -> Result<Vec<UnlockItem>, String> {
|
||||
|
||||
// 添加 Disney+ 检测任务
|
||||
{
|
||||
let client = client_arc.clone();
|
||||
let results = results.clone();
|
||||
let client = Arc::clone(&client_arc);
|
||||
let results = Arc::clone(&results);
|
||||
tasks.spawn(async move {
|
||||
let result = check_disney_plus(&client).await;
|
||||
let mut results = results.lock().await;
|
||||
@@ -1270,8 +1547,8 @@ pub async fn check_media_unlock() -> Result<Vec<UnlockItem>, String> {
|
||||
|
||||
// 添加 Prime Video 检测任务
|
||||
{
|
||||
let client = client_arc.clone();
|
||||
let results = results.clone();
|
||||
let client = Arc::clone(&client_arc);
|
||||
let results = Arc::clone(&results);
|
||||
tasks.spawn(async move {
|
||||
let result = check_prime_video(&client).await;
|
||||
let mut results = results.lock().await;
|
||||
@@ -1287,9 +1564,17 @@ pub async fn check_media_unlock() -> Result<Vec<UnlockItem>, String> {
|
||||
}
|
||||
|
||||
// 获取所有结果
|
||||
let results = Arc::try_unwrap(results)
|
||||
.expect("无法获取结果,可能仍有引用存在")
|
||||
.into_inner();
|
||||
let results = match Arc::try_unwrap(results) {
|
||||
Ok(mutex) => mutex.into_inner(),
|
||||
Err(_) => {
|
||||
logging!(
|
||||
error,
|
||||
Type::Network,
|
||||
"Failed to unwrap results Arc, references still exist"
|
||||
);
|
||||
return Err("Failed to collect results".to_string());
|
||||
}
|
||||
};
|
||||
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use super::CmdResult;
|
||||
use crate::{ipc::IpcManager, state::proxy::ProxyRequestCache};
|
||||
use crate::{ipc::IpcManager, logging, state::proxy::ProxyRequestCache, utils::logging::Type};
|
||||
use std::time::Duration;
|
||||
|
||||
const PROXIES_REFRESH_INTERVAL: Duration = Duration::from_secs(60);
|
||||
@@ -12,7 +12,10 @@ pub async fn get_proxies() -> CmdResult<serde_json::Value> {
|
||||
let key = ProxyRequestCache::make_key("proxies", "default");
|
||||
let value = cache
|
||||
.get_or_fetch(key, PROXIES_REFRESH_INTERVAL, || async {
|
||||
manager.get_proxies().await.expect("fetch failed")
|
||||
manager.get_proxies().await.unwrap_or_else(|e| {
|
||||
logging!(error, Type::Cmd, "Failed to fetch proxies: {e}");
|
||||
serde_json::Value::Object(serde_json::Map::new())
|
||||
})
|
||||
})
|
||||
.await;
|
||||
Ok((*value).clone())
|
||||
@@ -34,7 +37,10 @@ pub async fn get_providers_proxies() -> CmdResult<serde_json::Value> {
|
||||
let key = ProxyRequestCache::make_key("providers", "default");
|
||||
let value = cache
|
||||
.get_or_fetch(key, PROVIDERS_REFRESH_INTERVAL, || async {
|
||||
manager.get_providers_proxies().await.expect("fetch failed")
|
||||
manager.get_providers_proxies().await.unwrap_or_else(|e| {
|
||||
logging!(error, Type::Cmd, "Failed to fetch provider proxies: {e}");
|
||||
serde_json::Value::Object(serde_json::Map::new())
|
||||
})
|
||||
})
|
||||
.await;
|
||||
Ok((*value).clone())
|
||||
|
||||
@@ -29,7 +29,8 @@ pub async fn save_profile_file(index: String, file_data: Option<String>) -> CmdR
|
||||
};
|
||||
|
||||
// 保存新的配置文件
|
||||
wrap_err!(fs::write(&file_path, file_data.clone().unwrap()))?;
|
||||
let file_data = file_data.ok_or("file_data is None")?;
|
||||
wrap_err!(fs::write(&file_path, &file_data))?;
|
||||
|
||||
let file_path_str = file_path.to_string_lossy().to_string();
|
||||
logging!(
|
||||
@@ -139,17 +140,29 @@ pub async fn save_profile_file(index: String, file_data: Option<String>) -> CmdR
|
||||
|| (!file_path_str.ends_with(".js") && !is_script_error)
|
||||
{
|
||||
// 普通YAML错误使用YAML通知处理
|
||||
log::info!(target: "app", "[cmd配置save] YAML配置文件验证失败,发送通知");
|
||||
logging!(
|
||||
info,
|
||||
Type::Config,
|
||||
"[cmd配置save] YAML配置文件验证失败,发送通知"
|
||||
);
|
||||
let result = (false, error_msg.clone());
|
||||
crate::cmd::validate::handle_yaml_validation_notice(&result, "YAML配置文件");
|
||||
} else if is_script_error {
|
||||
// 脚本错误使用专门的通知处理
|
||||
log::info!(target: "app", "[cmd配置save] 脚本文件验证失败,发送通知");
|
||||
logging!(
|
||||
info,
|
||||
Type::Config,
|
||||
"[cmd配置save] 脚本文件验证失败,发送通知"
|
||||
);
|
||||
let result = (false, error_msg.clone());
|
||||
crate::cmd::validate::handle_script_validation_notice(&result, "脚本文件");
|
||||
} else {
|
||||
// 普通配置错误使用一般通知
|
||||
log::info!(target: "app", "[cmd配置save] 其他类型验证失败,发送一般通知");
|
||||
logging!(
|
||||
info,
|
||||
Type::Config,
|
||||
"[cmd配置save] 其他类型验证失败,发送一般通知"
|
||||
);
|
||||
handle::Handle::notice_message("config_validate::error", &error_msg);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,13 +3,15 @@ use crate::{
|
||||
core::{service, CoreManager},
|
||||
utils::i18n::t,
|
||||
};
|
||||
use anyhow::Result;
|
||||
|
||||
async fn execute_service_operation(
|
||||
service_op: impl std::future::Future<Output = Result<(), impl ToString + std::fmt::Debug>>,
|
||||
op_type: &str,
|
||||
) -> CmdResult {
|
||||
if service_op.await.is_err() {
|
||||
let emsg = format!("{} {} failed", op_type, "Service");
|
||||
async fn execute_service_operation_sync<F, E>(service_op: F, op_type: &str) -> CmdResult
|
||||
where
|
||||
F: FnOnce() -> Result<(), E>,
|
||||
E: ToString + std::fmt::Debug,
|
||||
{
|
||||
if let Err(e) = service_op() {
|
||||
let emsg = format!("{} {} failed: {}", op_type, "Service", e.to_string());
|
||||
return Err(t(emsg.as_str()));
|
||||
}
|
||||
if CoreManager::global().restart_core().await.is_err() {
|
||||
@@ -21,22 +23,22 @@ async fn execute_service_operation(
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn install_service() -> CmdResult {
|
||||
execute_service_operation(service::install_service(), "Install").await
|
||||
execute_service_operation_sync(service::install_service, "Install").await
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn uninstall_service() -> CmdResult {
|
||||
execute_service_operation(service::uninstall_service(), "Uninstall").await
|
||||
execute_service_operation_sync(service::uninstall_service, "Uninstall").await
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn reinstall_service() -> CmdResult {
|
||||
execute_service_operation(service::reinstall_service(), "Reinstall").await
|
||||
execute_service_operation_sync(service::reinstall_service, "Reinstall").await
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn repair_service() -> CmdResult {
|
||||
execute_service_operation(service::force_reinstall_service(), "Repair").await
|
||||
execute_service_operation_sync(service::force_reinstall_service, "Repair").await
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
use super::CmdResult;
|
||||
use crate::{
|
||||
core::{handle, CoreManager},
|
||||
logging,
|
||||
module::sysinfo::PlatformSpecification,
|
||||
utils::logging::Type,
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
use std::{
|
||||
@@ -23,20 +25,22 @@ static APP_START_TIME: Lazy<AtomicI64> = Lazy::new(|| {
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn export_diagnostic_info() -> CmdResult<()> {
|
||||
let sysinfo = PlatformSpecification::new_async().await;
|
||||
let sysinfo = PlatformSpecification::new_sync();
|
||||
let info = format!("{sysinfo:?}");
|
||||
|
||||
let app_handle = handle::Handle::global().app_handle().unwrap();
|
||||
let app_handle = handle::Handle::global()
|
||||
.app_handle()
|
||||
.ok_or("Failed to get app handle")?;
|
||||
let cliboard = app_handle.clipboard();
|
||||
if cliboard.write_text(info).is_err() {
|
||||
log::error!(target: "app", "Failed to write to clipboard");
|
||||
logging!(error, Type::System, "Failed to write to clipboard");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_system_info() -> CmdResult<String> {
|
||||
let sysinfo = PlatformSpecification::new_async().await;
|
||||
let sysinfo = PlatformSpecification::new_sync();
|
||||
let info = format!("{sysinfo:?}");
|
||||
Ok(info)
|
||||
}
|
||||
@@ -44,7 +48,7 @@ pub async fn get_system_info() -> CmdResult<String> {
|
||||
/// 获取当前内核运行模式
|
||||
#[tauri::command]
|
||||
pub async fn get_running_mode() -> Result<String, String> {
|
||||
Ok(CoreManager::global().get_running_mode().await.to_string())
|
||||
Ok(CoreManager::global().get_running_mode().to_string())
|
||||
}
|
||||
|
||||
/// 获取应用的运行时间(毫秒)
|
||||
|
||||
@@ -6,8 +6,8 @@ mod platform {
|
||||
use super::CmdResult;
|
||||
use crate::{core::win_uwp, wrap_err};
|
||||
|
||||
pub async fn invoke_uwp_tool() -> CmdResult {
|
||||
wrap_err!(win_uwp::invoke_uwptools().await)
|
||||
pub fn invoke_uwp_tool() -> CmdResult {
|
||||
wrap_err!(win_uwp::invoke_uwptools())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ mod platform {
|
||||
mod platform {
|
||||
use super::CmdResult;
|
||||
|
||||
pub async fn invoke_uwp_tool() -> CmdResult {
|
||||
pub fn invoke_uwp_tool() -> CmdResult {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -24,5 +24,5 @@ mod platform {
|
||||
/// Command exposed to Tauri
|
||||
#[tauri::command]
|
||||
pub async fn invoke_uwp_tool() -> CmdResult {
|
||||
platform::invoke_uwp_tool().await
|
||||
platform::invoke_uwp_tool()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user