Rewrite Gitea download/page host to external base in update check

Gitea ROOT_URL points to an internal IP, so release/asset URLs returned
by the API are not reachable externally. Rewrite their host to the same
external base the app queries. Also prefer the .exe (NSIS) asset.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
caoqianming 2026-06-11 16:53:09 +08:00
parent 8320432789
commit 3befdd4874
1 changed files with 45 additions and 11 deletions

View File

@ -78,10 +78,27 @@ fn export_excel(app: tauri::AppHandle, db: State<Db>, id: i64) -> Result<Option<
excel::write_to_path(&path, &detail).map(Some) excel::write_to_path(&path, &detail).map(Some)
} }
/// Gitea「最新 release」API。如迁移到 https 站点,需为 ureq 启用 tls 特性。 /// Gitea 站点根地址(应用访问用的外网主机)。如迁移到 https需为 ureq 启用 tls 特性。
const GITEA_BASE: &str = "http://gitea.xxhhcty.xyz:8080";
/// Gitea「最新 release」API。
const GITEA_LATEST_RELEASE_API: &str = const GITEA_LATEST_RELEASE_API: &str =
"http://gitea.xxhhcty.xyz:8080/api/v1/repos/zcdsj/tcjs/releases/latest"; "http://gitea.xxhhcty.xyz:8080/api/v1/repos/zcdsj/tcjs/releases/latest";
/// 把 Gitea 返回的链接主机改写为 `GITEA_BASE`(保留路径/查询)。
/// 规避 Gitea ROOT_URL 指向内网、导致外网用户打不开下载/页面链接的问题。
fn rewrite_host(url: &str) -> String {
if url.is_empty() {
return String::new();
}
match url.find("://") {
Some(scheme) => match url[scheme + 3..].find('/') {
Some(slash) => format!("{GITEA_BASE}{}", &url[scheme + 3 + slash..]),
None => GITEA_BASE.to_string(),
},
None => url.to_string(),
}
}
#[derive(serde::Serialize)] #[derive(serde::Serialize)]
struct UpdateInfo { struct UpdateInfo {
current_version: String, current_version: String,
@ -118,16 +135,20 @@ fn check_update(app: tauri::AppHandle) -> Result<UpdateInfo, String> {
.trim_start_matches(['v', 'V']) .trim_start_matches(['v', 'V'])
.to_string(); .to_string();
let notes = json["body"].as_str().unwrap_or_default().to_string(); let notes = json["body"].as_str().unwrap_or_default().to_string();
let release_url = json["html_url"].as_str().unwrap_or_default().to_string(); let release_url = rewrite_host(json["html_url"].as_str().unwrap_or_default());
let download_url = json["assets"].as_array().and_then(|assets| { let download_url = json["assets"].as_array().and_then(|assets| {
assets.iter().find_map(|asset| { // 优先 .exeNSIS其次 .msi。
let name = asset["name"].as_str().unwrap_or_default().to_ascii_lowercase(); let pick = |ext: &str| {
if name.ends_with(".exe") || name.ends_with(".msi") { assets.iter().find_map(|asset| {
asset["browser_download_url"].as_str().map(str::to_string) let name = asset["name"].as_str().unwrap_or_default().to_ascii_lowercase();
} else { if name.ends_with(ext) {
None asset["browser_download_url"].as_str()
} } else {
}) None
}
})
};
pick(".exe").or_else(|| pick(".msi")).map(rewrite_host)
}); });
Ok(UpdateInfo { Ok(UpdateInfo {
@ -209,7 +230,20 @@ fn main() {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::is_newer; use super::{is_newer, rewrite_host, GITEA_BASE};
#[test]
fn rewrites_internal_host_to_external() {
assert_eq!(
rewrite_host("http://10.0.11.51:3000/zcdsj/tcjs/releases/download/v0.2.0/app.exe"),
format!("{GITEA_BASE}/zcdsj/tcjs/releases/download/v0.2.0/app.exe")
);
assert_eq!(
rewrite_host("http://10.0.11.51:3000/zcdsj/tcjs/releases/tag/v0.2.0"),
format!("{GITEA_BASE}/zcdsj/tcjs/releases/tag/v0.2.0")
);
assert_eq!(rewrite_host(""), "");
}
#[test] #[test]
fn version_comparison() { fn version_comparison() {