fix: use system proxy for app updates
This commit is contained in:
parent
32273b4d6e
commit
2038501b53
|
|
@ -8,6 +8,16 @@ export function shouldOpenUpdateDialog(options: { silent?: boolean }) {
|
|||
return options.silent !== true;
|
||||
}
|
||||
|
||||
export async function resolveUpdaterProxy(): Promise<string | undefined> {
|
||||
if (!isTauriRuntime()) return undefined;
|
||||
try {
|
||||
const proxy = await api.getSystemProxyUrl();
|
||||
return proxy || undefined;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export function useAppUpdater() {
|
||||
const { t } = useI18n();
|
||||
const { toast } = useToast();
|
||||
|
|
@ -74,7 +84,8 @@ export function useAppUpdater() {
|
|||
downloadProgress.value = 0;
|
||||
try {
|
||||
const { check } = await import("@tauri-apps/plugin-updater");
|
||||
const update = await check();
|
||||
const proxy = await resolveUpdaterProxy();
|
||||
const update = await check(proxy ? { proxy } : undefined);
|
||||
if (!update) return;
|
||||
let totalBytes = 0;
|
||||
let downloadedBytes = 0;
|
||||
|
|
|
|||
|
|
@ -213,6 +213,7 @@ export const deleteHistoryEntry = forward("deleteHistoryEntry");
|
|||
|
||||
// Updates
|
||||
export const checkForUpdates = forward("checkForUpdates");
|
||||
export const getSystemProxyUrl = forward("getSystemProxyUrl");
|
||||
export const getAppVersion = forward("getAppVersion");
|
||||
|
||||
// Layout
|
||||
|
|
|
|||
|
|
@ -1258,6 +1258,10 @@ export async function checkForUpdates(): Promise<UpdateInfo> {
|
|||
return get("/api/update/check");
|
||||
}
|
||||
|
||||
export async function getSystemProxyUrl(): Promise<string | null> {
|
||||
return null;
|
||||
}
|
||||
|
||||
export async function getAppVersion(): Promise<string> {
|
||||
const res: { version: string } = await get("/api/version");
|
||||
return res.version;
|
||||
|
|
|
|||
|
|
@ -830,6 +830,10 @@ export async function checkForUpdates(): Promise<UpdateInfo> {
|
|||
return invoke("check_for_updates");
|
||||
}
|
||||
|
||||
export async function getSystemProxyUrl(): Promise<string | null> {
|
||||
return invoke("get_system_proxy_url");
|
||||
}
|
||||
|
||||
export async function getAppVersion(): Promise<string> {
|
||||
const { getVersion } = await import("@tauri-apps/api/app");
|
||||
return getVersion();
|
||||
|
|
|
|||
|
|
@ -41,10 +41,7 @@ pub struct UpdateInfo {
|
|||
}
|
||||
|
||||
pub async fn fetch_latest_release() -> Result<TauriRelease, String> {
|
||||
let client = reqwest::Client::builder()
|
||||
.timeout(std::time::Duration::from_secs(10))
|
||||
.build()
|
||||
.map_err(|e| format!("Failed to create HTTP client: {e}"))?;
|
||||
let client = build_update_http_client()?;
|
||||
|
||||
let resp = crate::race_download(&client, LATEST_JSON_PATH, LATEST_JSON_R2_PATH, "dbx-update-checker")
|
||||
.await
|
||||
|
|
@ -57,6 +54,132 @@ pub async fn fetch_latest_release() -> Result<TauriRelease, String> {
|
|||
Ok(release)
|
||||
}
|
||||
|
||||
fn build_update_http_client() -> Result<reqwest::Client, String> {
|
||||
let mut builder =
|
||||
reqwest::Client::builder().timeout(std::time::Duration::from_secs(10)).user_agent("dbx-update-checker");
|
||||
|
||||
if let Some(proxy_url) = system_proxy_url() {
|
||||
let proxy = reqwest::Proxy::all(&proxy_url).map_err(|e| format!("Invalid system proxy URL: {e}"))?;
|
||||
builder = builder.proxy(proxy);
|
||||
}
|
||||
|
||||
builder.build().map_err(|e| format!("Failed to create HTTP client: {e}"))
|
||||
}
|
||||
|
||||
pub fn system_proxy_url() -> Option<String> {
|
||||
system_proxy_url_from_platform()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn system_proxy_url_from_platform() -> Option<String> {
|
||||
let output = std::process::Command::new("scutil").arg("--proxy").output().ok()?;
|
||||
if !output.status.success() {
|
||||
return None;
|
||||
}
|
||||
let stdout = String::from_utf8(output.stdout).ok()?;
|
||||
system_proxy_url_from_scutil_output(&stdout)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn system_proxy_url_from_platform() -> Option<String> {
|
||||
let key = r"HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings";
|
||||
let proxy_enable = std::process::Command::new("reg").args(["query", key, "/v", "ProxyEnable"]).output().ok()?;
|
||||
let proxy_server = std::process::Command::new("reg").args(["query", key, "/v", "ProxyServer"]).output().ok()?;
|
||||
if !proxy_enable.status.success() || !proxy_server.status.success() {
|
||||
return None;
|
||||
}
|
||||
let proxy_enable = String::from_utf8(proxy_enable.stdout).ok()?;
|
||||
let proxy_server = String::from_utf8(proxy_server.stdout).ok()?;
|
||||
system_proxy_url_from_windows_registry_output(&proxy_enable, &proxy_server)
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows")))]
|
||||
fn system_proxy_url_from_platform() -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg_attr(not(test), allow(dead_code))]
|
||||
fn system_proxy_url_from_scutil_output(output: &str) -> Option<String> {
|
||||
let value = |key: &str| {
|
||||
output.lines().find_map(|line| {
|
||||
let (line_key, line_value) = line.split_once(':')?;
|
||||
(line_key.trim() == key).then(|| line_value.trim())
|
||||
})
|
||||
};
|
||||
|
||||
if value("HTTPSEnable") == Some("1") {
|
||||
if let Some(url) = proxy_url(value("HTTPSProxy")?, value("HTTPSPort")?) {
|
||||
return Some(url);
|
||||
}
|
||||
}
|
||||
|
||||
if value("HTTPEnable") == Some("1") {
|
||||
if let Some(url) = proxy_url(value("HTTPProxy")?, value("HTTPPort")?) {
|
||||
return Some(url);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg_attr(not(test), allow(dead_code))]
|
||||
fn system_proxy_url_from_windows_registry_output(proxy_enable: &str, proxy_server: &str) -> Option<String> {
|
||||
let enabled = proxy_enable
|
||||
.lines()
|
||||
.find(|line| line.contains("ProxyEnable"))?
|
||||
.split_whitespace()
|
||||
.last()
|
||||
.is_some_and(|value| value == "0x1" || value == "1");
|
||||
if !enabled {
|
||||
return None;
|
||||
}
|
||||
|
||||
let server = proxy_server.lines().find(|line| line.contains("ProxyServer"))?.split_whitespace().last()?;
|
||||
|
||||
proxy_url_from_windows_proxy_server(server)
|
||||
}
|
||||
|
||||
fn proxy_url_from_windows_proxy_server(server: &str) -> Option<String> {
|
||||
let entries = server.split(';').map(str::trim).filter(|entry| !entry.is_empty()).collect::<Vec<_>>();
|
||||
|
||||
for key in ["https=", "http="] {
|
||||
if let Some(entry) = entries.iter().find_map(|entry| entry.strip_prefix(key)) {
|
||||
if let Some(url) = proxy_url_from_host_port(entry) {
|
||||
return Some(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
entries.iter().find(|entry| !entry.contains('=')).and_then(|entry| proxy_url_from_host_port(entry))
|
||||
}
|
||||
|
||||
fn proxy_url_from_host_port(value: &str) -> Option<String> {
|
||||
let value = value.trim();
|
||||
if value.starts_with("http://") || value.starts_with("https://") {
|
||||
return Some(value.to_string());
|
||||
}
|
||||
if value.starts_with("socks://") || value.starts_with("socks5://") || value.starts_with("socks5h://") {
|
||||
return None;
|
||||
}
|
||||
|
||||
let (host, port) = if let Some(rest) = value.strip_prefix('[') {
|
||||
let (host, rest) = rest.split_once(']')?;
|
||||
let port = rest.strip_prefix(':')?;
|
||||
(host, port)
|
||||
} else {
|
||||
value.rsplit_once(':')?
|
||||
};
|
||||
proxy_url(host, port)
|
||||
}
|
||||
|
||||
fn proxy_url(host: &str, port: &str) -> Option<String> {
|
||||
if host.is_empty() || port.parse::<u16>().is_err() {
|
||||
return None;
|
||||
}
|
||||
let host = if host.contains(':') && !host.starts_with('[') { format!("[{host}]") } else { host.to_string() };
|
||||
Some(format!("http://{host}:{port}"))
|
||||
}
|
||||
|
||||
async fn fetch_github_release_metadata(
|
||||
client: &reqwest::Client,
|
||||
version: &str,
|
||||
|
|
@ -141,7 +264,10 @@ pub fn is_newer_version(latest: &str, current: &str) -> bool {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{build_update_info, is_newer_version, normalize_version, GithubReleaseMetadata, TauriRelease};
|
||||
use super::{
|
||||
build_update_info, is_newer_version, normalize_version, system_proxy_url_from_scutil_output,
|
||||
system_proxy_url_from_windows_registry_output, GithubReleaseMetadata, TauriRelease,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn normalizes_tag_versions() {
|
||||
|
|
@ -157,6 +283,63 @@ mod tests {
|
|||
assert!(!is_newer_version("0.1.9", "0.2.0"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_macos_https_system_proxy() {
|
||||
let output = r#"<dictionary> {
|
||||
HTTPEnable : 1
|
||||
HTTPPort : 7890
|
||||
HTTPProxy : 127.0.0.1
|
||||
HTTPSEnable : 1
|
||||
HTTPSPort : 7891
|
||||
HTTPSProxy : 127.0.0.1
|
||||
}"#;
|
||||
|
||||
assert_eq!(system_proxy_url_from_scutil_output(output), Some("http://127.0.0.1:7891".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn falls_back_to_macos_http_system_proxy() {
|
||||
let output = r#"<dictionary> {
|
||||
HTTPEnable : 1
|
||||
HTTPPort : 7890
|
||||
HTTPProxy : 127.0.0.1
|
||||
HTTPSEnable : 0
|
||||
}"#;
|
||||
|
||||
assert_eq!(system_proxy_url_from_scutil_output(output), Some("http://127.0.0.1:7890".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ignores_disabled_or_incomplete_macos_system_proxy() {
|
||||
assert_eq!(system_proxy_url_from_scutil_output("HTTPEnable : 0\nHTTPProxy : 127.0.0.1\nHTTPPort : 7890"), None);
|
||||
assert_eq!(system_proxy_url_from_scutil_output("HTTPEnable : 1\nHTTPProxy : 127.0.0.1"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_windows_system_proxy() {
|
||||
let enabled = r#"
|
||||
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings
|
||||
ProxyEnable REG_DWORD 0x1
|
||||
"#;
|
||||
let server = r#"
|
||||
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings
|
||||
ProxyServer REG_SZ http=127.0.0.1:7890;https=127.0.0.1:7891
|
||||
"#;
|
||||
|
||||
assert_eq!(
|
||||
system_proxy_url_from_windows_registry_output(enabled, server),
|
||||
Some("http://127.0.0.1:7891".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ignores_disabled_windows_system_proxy() {
|
||||
let disabled = "ProxyEnable REG_DWORD 0x0";
|
||||
let server = "ProxyServer REG_SZ 127.0.0.1:7890";
|
||||
|
||||
assert_eq!(system_proxy_url_from_windows_registry_output(disabled, server), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_jdbc_plugin_metadata_from_latest_json() {
|
||||
let release: TauriRelease = serde_json::from_str(
|
||||
|
|
|
|||
|
|
@ -34,6 +34,13 @@ test("app passes update availability to the toolbar badge", () => {
|
|||
assert.match(source, /:has-update-available="hasUpdateAvailable"/);
|
||||
});
|
||||
|
||||
test("updater download passes system proxy to tauri updater check", () => {
|
||||
const source = readFileSync("apps/desktop/src/composables/useAppUpdater.ts", "utf8");
|
||||
|
||||
assert.match(source, /getSystemProxyUrl/);
|
||||
assert.match(source, /check\(proxy \? \{ proxy } : undefined\)/);
|
||||
});
|
||||
|
||||
test("driver manager entry can show an update count badge", () => {
|
||||
const toolbarSource = readFileSync("apps/desktop/src/components/layout/AppToolbar.vue", "utf8");
|
||||
const tabSource = readFileSync("apps/desktop/src/components/layout/AppTabBar.vue", "utf8");
|
||||
|
|
|
|||
|
|
@ -6,3 +6,8 @@ pub async fn check_for_updates() -> Result<UpdateInfo, String> {
|
|||
let current_version = env!("CARGO_PKG_VERSION");
|
||||
Ok(dbx_core::update::build_update_info(release, current_version))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn get_system_proxy_url() -> Option<String> {
|
||||
dbx_core::update::system_proxy_url()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -369,6 +369,7 @@ pub fn run() {
|
|||
commands::history::clear_history,
|
||||
commands::history::delete_history_entry,
|
||||
commands::update::check_for_updates,
|
||||
commands::update::get_system_proxy_url,
|
||||
commands::transfer::start_transfer,
|
||||
commands::transfer::cancel_transfer,
|
||||
commands::database_export::export_database_sql,
|
||||
|
|
|
|||
Loading…
Reference in New Issue