feat(desktop): add detailed error messages and SSL bypass checkbox

This commit is contained in:
OpenCode Bot
2026-05-24 21:41:26 +08:00
parent 2255ce9c65
commit d2c60b56c9

View File

@@ -21,6 +21,7 @@ struct App {
share_url: String, share_url: String,
upload_rx: Option<mpsc::Receiver<UploadResult>>, upload_rx: Option<mpsc::Receiver<UploadResult>>,
uploading: bool, uploading: bool,
skip_ssl: bool,
} }
impl App { impl App {
@@ -31,16 +32,19 @@ impl App {
share_url: String::new(), share_url: String::new(),
upload_rx: None, upload_rx: None,
uploading: false, uploading: false,
skip_ssl: false,
} }
} }
fn start_upload(&mut self, filepath: std::path::PathBuf, api_url: String) { fn start_upload(&mut self, filepath: std::path::PathBuf) {
let (tx, rx) = mpsc::channel(); let (tx, rx) = mpsc::channel();
self.upload_rx = Some(rx); self.upload_rx = Some(rx);
self.uploading = true; self.uploading = true;
let api_url = self.api_url.clone();
let skip_ssl = self.skip_ssl;
std::thread::spawn(move || { std::thread::spawn(move || {
let result = do_upload(&api_url, &filepath); let result = do_upload(&api_url, &filepath, skip_ssl);
let _ = tx.send(result); let _ = tx.send(result);
}); });
} }
@@ -65,7 +69,7 @@ impl App {
} }
} }
fn do_upload(api_url: &str, filepath: &std::path::Path) -> UploadResult { fn do_upload(api_url: &str, filepath: &std::path::Path, skip_ssl: bool) -> UploadResult {
let filename = filepath let filename = filepath
.file_name() .file_name()
.map(|n| n.to_string_lossy().to_string()) .map(|n| n.to_string_lossy().to_string())
@@ -81,18 +85,33 @@ fn do_upload(api_url: &str, filepath: &std::path::Path) -> UploadResult {
.part("file", part) .part("file", part)
.text("expiry", "24h"); .text("expiry", "24h");
let client = match reqwest::blocking::Client::builder() let mut builder = reqwest::blocking::Client::builder()
.user_agent("TempFileTransfer-Desktop/0.1") .user_agent("TempFileTransfer-Desktop/0.2")
.timeout(std::time::Duration::from_secs(300)) .timeout(std::time::Duration::from_secs(300));
.build()
{ if skip_ssl {
builder = builder.danger_accept_invalid_certs(true);
}
let client = match builder.build() {
Ok(c) => c, Ok(c) => c,
Err(e) => return UploadResult::Err(format!("创建客户端失败: {}", e)), Err(e) => return UploadResult::Err(format!("创建客户端失败: {}", e)),
}; };
let resp = match client.post(api_url).multipart(form).send() { let resp = match client.post(api_url).multipart(form).send() {
Ok(r) => r, Ok(r) => r,
Err(e) => return UploadResult::Err(format!("网络请求失败: {}", e)), Err(e) => {
let detail = if e.is_connect() {
format!("连接失败(无法连接到服务器): {}", e)
} else if e.is_timeout() {
format!("连接超时: {}", e)
} else if e.is_body() {
format!("SSL/数据传输出错请尝试勾选「跳过SSL验证」: {}", e)
} else {
format!("{}", e)
};
return UploadResult::Err(detail);
}
}; };
let status = resp.status(); let status = resp.status();
@@ -138,6 +157,11 @@ impl eframe::App for App {
); );
}); });
ui.add_space(4.0);
ui.horizontal(|ui| {
ui.checkbox(&mut self.skip_ssl, "跳过 SSL 证书验证(仅当连接失败时尝试)");
});
ui.add_space(8.0); ui.add_space(8.0);
let drop_frame = egui::Frame::dark_canvas(ui.style()) let drop_frame = egui::Frame::dark_canvas(ui.style())
@@ -180,7 +204,7 @@ impl eframe::App for App {
if !self.uploading { if !self.uploading {
for f in files { for f in files {
if let Some(ref path) = f.path { if let Some(ref path) = f.path {
self.start_upload(path.to_owned(), self.api_url.clone()); self.start_upload(path.to_owned());
break; break;
} }
} }
@@ -191,10 +215,14 @@ impl eframe::App for App {
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.label("状态:"); ui.label("状态:");
ui.label( let color = if self.status.starts_with("上传失败") {
egui::RichText::new(&self.status) egui::Color32::from_rgb(220, 80, 80)
.color(egui::Color32::from_rgb(100, 180, 100)), } else if self.status.starts_with("上传成功") {
); egui::Color32::from_rgb(80, 200, 80)
} else {
egui::Color32::from_rgb(100, 180, 100)
};
ui.label(egui::RichText::new(&self.status).color(color));
}); });
ui.add_space(8.0); ui.add_space(8.0);
@@ -253,7 +281,7 @@ fn load_chinese_font(ctx: &egui::Context) {
fn main() { fn main() {
let options = eframe::NativeOptions { let options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default() viewport: egui::ViewportBuilder::default()
.with_inner_size([560.0, 480.0]) .with_inner_size([560.0, 500.0])
.with_resizable(false) .with_resizable(false)
.with_title("临时文件传输"), .with_title("临时文件传输"),
..Default::default() ..Default::default()