diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 340aed5..acfa5e5 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -24,4 +24,5 @@ env_logger = "0.10.0" local-ip-address = "0.5.1" actix-cors = "0.6.4" tokio = { version = "1.24.1", features = ["macros", "process"]} -urlencoding = "2.1.3" \ No newline at end of file +urlencoding = "2.1.3" +content_disposition = "0.4.0" diff --git a/backend/src/external_web.rs b/backend/src/external_web.rs index ce737cd..69069b0 100644 --- a/backend/src/external_web.rs +++ b/backend/src/external_web.rs @@ -2,12 +2,8 @@ use actix_web::{body::BoxBody, web, HttpResponse, Result}; use local_ip_address::local_ip; use rand::{distributions::Alphanumeric, Rng}; use serde::{Deserialize, Serialize}; -use std::time::Duration; use std::{collections::HashMap, fs, path::PathBuf, sync::Mutex}; -use tokio::net::TcpStream; -use tokio::process::Command; -use tokio::sync::mpsc; -use tokio::time::sleep; +use content_disposition; use crate::{ control::{ClashError, ClashErrorKind, EnhancedMode}, @@ -693,7 +689,7 @@ pub async fn download_sub( .with_header( "User-Agent", format!( - "ToMoon/{} mihomo/1.18.3 Clash/v1.18.0", + "ToMoon/{} mihomo/1.19.4 clash-verge/2.2.3 Clash/v1.18.0", env!("CARGO_PKG_VERSION") ), ) @@ -710,44 +706,49 @@ pub async fn download_sub( ErrorKind: ClashErrorKind::ConfigFormatError, })); } - let filename = x.headers.get("content-disposition"); - let filename = match filename { - Some(x) => { - let filename = x.split("filename=").collect::>()[1] - .split(";") - .collect::>()[0] - .replace("\"", ""); - filename.to_string() + let filename = x.headers.get("content-disposition") + .and_then(|header| { + // 尝试从 content-disposition 头部获取文件名 + // header.split("filename=").nth(1) + // .and_then(|s| s.split(';').next()) + // .map(|s| s.trim_matches('"')) + content_disposition::parse_content_disposition(header).filename_full() + }) + .filter(|s| !s.is_empty()) + .or_else(|| { + // 如果 content-disposition 头部中没有文件名,则尝试从 URL 中获取 + log::info!("Failed to get content-disposition, using url instead."); + url.rsplit('/').next() + .and_then(|last_part| last_part.split('?').next()).map(|s| s.to_string()) + }) + .unwrap_or_else(|| { + // 如果 URL 中没有文件名,则生成一个随机文件名 + log::warn!("The downloaded subscription does not have a file name."); + gen_random_name() + }); + let filename = match filename.to_ascii_lowercase() { + ref lower if lower.ends_with(".yaml") || lower.ends_with(".yml") => filename, + _ => filename + ".yaml", + }; + let mut filepath = path.join(filename.clone()); + if filepath.exists() { + for i in 1..=128 { + let new_filename = format!("{}_{}.yaml", filename.trim_end_matches(".yaml"), i); + filepath = path.join(new_filename); + if !filepath.exists() { + break; + } } - None => { - let slash_split = *url.split("/").collect::>().last().unwrap(); - slash_split - .split("?") - .collect::>() - .first() - .unwrap() - .to_string() + if filepath.exists() { + log::error!("Failed while saving sub, cannot find a new name."); + return Err(actix_web::Error::from(ClashError { + Message: "The file already exists.".to_string(), + ErrorKind: ClashErrorKind::InnerError, + })); } - }; - let filename = if filename.is_empty() { - log::warn!("The downloaded subscription does not have a file name."); - gen_random_name() - } else { - filename - }; - let filename = if filename.to_lowercase().ends_with(".yaml") - || filename.to_lowercase().ends_with(".yml") - { - filename - } else { - filename + ".yaml" - }; - let mut path = path.join(filename); - if fs::metadata(&path).is_ok() { - path = path.parent().unwrap().join(gen_random_name() + ".yaml"); } //保存订阅 - if let Some(parent) = path.parent() { + if let Some(parent) = filepath.parent() { if let Err(e) = std::fs::create_dir_all(parent) { log::error!("Failed while creating sub dir."); log::error!("Error Message:{}", e); @@ -757,7 +758,7 @@ pub async fn download_sub( })); } } - let path = path.to_str().unwrap(); + let path = filepath.to_str().unwrap(); log::info!("Writing to path: {}", path); if let Err(e) = fs::write(path, response) { log::error!("Failed while saving sub."); diff --git a/src/pages/Subscriptions.tsx b/src/pages/Subscriptions.tsx index debf656..aeb6655 100644 --- a/src/pages/Subscriptions.tsx +++ b/src/pages/Subscriptions.tsx @@ -127,6 +127,7 @@ export const Subscriptions: FC = ({ Subscriptions }) => {
+

{QRPageUrl}

{ setUrl(event.target.value); @@ -75,18 +75,25 @@ const on_download_btn_click = (url, isSubscribed) => { confirmButtonColor: '#5A6242', background: '#DEE7BF' }); - } - }).catch(error => { - if (error.response) { + } else { Swal.fire({ icon: 'error', iconColor: '#5E5F55', - title: '失败', - text: error.response.data?.error?.message, + title: '后端失败', + html: `错误状态: ${response.status}
错误信息: ${response.data?.error?.message}`, confirmButtonColor: '#5A6242', background: '#DEE7BF' }); } + }).catch(error => { + Swal.fire({ + icon: 'error', + iconColor: '#5E5F55', + title: '请求失败', + html: `错误类型: ${error.name}
错误信息: ${error?.message}`, + confirmButtonColor: '#5A6242', + background: '#DEE7BF' + }); }); }