Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion backend/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
urlencoding = "2.1.3"
content_disposition = "0.4.0"
83 changes: 42 additions & 41 deletions backend/src/external_web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -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")
),
)
Expand All @@ -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::<Vec<&str>>()[1]
.split(";")
.collect::<Vec<&str>>()[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::<Vec<&str>>().last().unwrap();
slash_split
.split("?")
.collect::<Vec<&str>>()
.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);
Expand All @@ -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.");
Expand Down
1 change: 1 addition & 0 deletions src/pages/Subscriptions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ export const Subscriptions: FC<SubProp> = ({ Subscriptions }) => {
<div id="subscription-qrcode">
<QRCodeCanvas value={QRPageUrl} size={128} />
</div>
<p style={{ textAlign: "center" }}>{QRPageUrl}</p>
<div id="subscription-download-textfiled" style={cleanPadding}>
<TextField
label={localizationManager.getString(L.SUBSCRIPTIONS_LINK)}
Expand Down
19 changes: 13 additions & 6 deletions tomoon-web/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import axios from 'axios'

function App() {
const [url, setUrl] = useState("");
const [isSubscribed, setIsSubscribed] = useState(true);
const [isSubscribed, setIsSubscribed] = useState(false);

const handleUrlChange = (event) => {
setUrl(event.target.value);
Expand Down Expand Up @@ -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}<br>错误信息: ${response.data?.error?.message}`,
confirmButtonColor: '#5A6242',
background: '#DEE7BF'
});
}
}).catch(error => {
Swal.fire({
icon: 'error',
iconColor: '#5E5F55',
title: '请求失败',
html: `错误类型: ${error.name}<br>错误信息: ${error?.message}`,
confirmButtonColor: '#5A6242',
background: '#DEE7BF'
});
});

}
Expand Down