feat(updater): add app update checks and refine the kernel dashboard
This commit is contained in:
1 parent
4e8a02fdfc
commit
d1833e63ad
7 files changed
+247
-44
No files matched your search
@@ -26,6 +26,7 @@ const RELEASE_PAGE_URL: &str = "https://github.com/router-for-me/CLIProxyAPI/rel
|
||||
const RELEASE_ATOM_URL: &str = "https://github.com/router-for-me/CLIProxyAPI/releases.atom";
|
||||
const RELEASE_DOWNLOAD_PREFIX: &str =
|
||||
"https://github.com/router-for-me/CLIProxyAPI/releases/download/";
|
||||
const APP_RELEASE_PAGE_URL: &str = "https://github.com/lzt404/Easy_CLIProxyAPI/releases/latest";
|
||||
const CORE_INSTALL_PROGRESS_EVENT: &str = "core-install-progress";
|
||||
const CORE_STATUS_EVENT: &str = "core-status-changed";
|
||||
const CORE_METADATA_FILE: &str = "cpa-gui-meta.json";
|
||||
@@ -54,6 +55,11 @@ const USER_AGENT: &str = concat!(
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
" (+https://github.com/router-for-me/CLIProxyAPI)"
|
||||
);
|
||||
const APP_USER_AGENT: &str = concat!(
|
||||
"Easy_CLIProxyAPI/",
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
" (+https://github.com/lzt404/Easy_CLIProxyAPI)"
|
||||
);
|
||||
static CORE_CONFIG_FILE_LOCK: Mutex<()> = Mutex::new(());
|
||||
static AGENT_CONFIG_FILE_LOCK: Mutex<()> = Mutex::new(());
|
||||
|
||||
@@ -110,6 +116,15 @@ struct CoreLatest {
|
||||
asset_name: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct AppUpdateInfo {
|
||||
current_version: String,
|
||||
latest_version: String,
|
||||
update_available: bool,
|
||||
release_url: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct BundledCoreInfo {
|
||||
@@ -4629,6 +4644,45 @@ fn open_external_url(url: String) -> Result<(), String> {
|
||||
open_external_url_inner(&url)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn check_app_update() -> Result<AppUpdateInfo, String> {
|
||||
let client = reqwest::Client::builder()
|
||||
.connect_timeout(Duration::from_secs(8))
|
||||
.read_timeout(Duration::from_secs(15))
|
||||
.timeout(Duration::from_secs(20))
|
||||
.build()
|
||||
.map_err(|error| format!("创建版本检查客户端失败: {error}"))?;
|
||||
let response = client
|
||||
.get(APP_RELEASE_PAGE_URL)
|
||||
.header(reqwest::header::ACCEPT, "text/html,application/xhtml+xml")
|
||||
.header(reqwest::header::USER_AGENT, APP_USER_AGENT)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|error| format!("检查软件更新失败: {error}"))?;
|
||||
let status = response.status();
|
||||
let final_url = response.url().clone();
|
||||
if !status.is_success() {
|
||||
let body = response
|
||||
.text()
|
||||
.await
|
||||
.map_err(|error| format!("读取软件更新响应失败: {error}"))?;
|
||||
return Err(format_github_error(status.as_u16(), &body));
|
||||
}
|
||||
|
||||
let latest_version = release_tag_from_url(&final_url)
|
||||
.map(|version| normalize_version(&version))
|
||||
.ok_or_else(|| "GitHub latest 页面没有返回正式版本标签".to_string())?;
|
||||
let current_version = normalize_version(env!("CARGO_PKG_VERSION"));
|
||||
let update_available = is_app_update_available(¤t_version, &latest_version)?;
|
||||
|
||||
Ok(AppUpdateInfo {
|
||||
current_version,
|
||||
latest_version,
|
||||
update_available,
|
||||
release_url: APP_RELEASE_PAGE_URL.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn check_latest_core() -> Result<CoreLatest, String> {
|
||||
let platform = current_core_platform()?;
|
||||
@@ -5010,6 +5064,14 @@ fn release_tag_from_url(url: &reqwest::Url) -> Option<String> {
|
||||
}
|
||||
}
|
||||
|
||||
fn is_app_update_available(current: &str, latest: &str) -> Result<bool, String> {
|
||||
let parse = |value: &str| {
|
||||
semver::Version::parse(value.trim().trim_start_matches('v'))
|
||||
.map_err(|error| format!("无法解析版本号 {value}: {error}"))
|
||||
};
|
||||
Ok(parse(latest)? > parse(current)?)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn parse_release_assets(html: &str) -> Vec<GithubAsset> {
|
||||
let mut assets = Vec::new();
|
||||
@@ -8622,6 +8684,7 @@ fn main() {
|
||||
get_oauth_status,
|
||||
submit_oauth_callback,
|
||||
open_external_url,
|
||||
check_app_update,
|
||||
check_latest_core,
|
||||
detect_bundled_core,
|
||||
install_core_version,
|
||||
@@ -10214,6 +10277,14 @@ mod tests {
|
||||
assert_eq!(release_tag_from_atom(xml).as_deref(), Some("v7.2.80"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn app_update_comparison_uses_semantic_versions() {
|
||||
assert!(is_app_update_available("v0.1.9", "v0.2.0").unwrap());
|
||||
assert!(is_app_update_available("v0.2.0-beta.1", "v0.2.0").unwrap());
|
||||
assert!(!is_app_update_available("v0.2.0", "v0.2.0").unwrap());
|
||||
assert!(!is_app_update_available("v0.2.0", "v0.1.9").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn synthetic_release_uses_official_asset_names_and_urls() {
|
||||
let release = release_from_tag("7.2.80");
|
||||
|
||||
Reference in new issue
Block a user