|
| 1 | +use std::path::PathBuf; |
| 2 | +use crate::ui; |
| 3 | + |
| 4 | +const GITHUB_API: &str = "https://api.github.com/repos/eddmpython/dartlab-desktop/releases/latest"; |
| 5 | +const CURRENT_VERSION: &str = env!("CARGO_PKG_VERSION"); |
| 6 | + |
| 7 | +pub fn cleanup_old() { |
| 8 | + let current = std::env::current_exe().ok(); |
| 9 | + if let Some(ref exe) = current { |
| 10 | + let old = exe.with_extension("exe.old"); |
| 11 | + if old.exists() { |
| 12 | + std::fs::remove_file(&old).ok(); |
| 13 | + } |
| 14 | + } |
| 15 | +} |
| 16 | + |
| 17 | +pub fn check_and_update() { |
| 18 | + let latest = match get_latest_release() { |
| 19 | + Ok(r) => r, |
| 20 | + Err(_) => return, |
| 21 | + }; |
| 22 | + |
| 23 | + let current = format!("v{CURRENT_VERSION}"); |
| 24 | + if latest.tag == current { |
| 25 | + return; |
| 26 | + } |
| 27 | + |
| 28 | + ui::print_info(&format!("런처 업데이트 발견: {current} → {}", latest.tag)); |
| 29 | + |
| 30 | + let download_url = match latest.asset_url { |
| 31 | + Some(url) => url, |
| 32 | + None => return, |
| 33 | + }; |
| 34 | + |
| 35 | + let current_exe = match std::env::current_exe() { |
| 36 | + Ok(p) => p, |
| 37 | + Err(_) => return, |
| 38 | + }; |
| 39 | + |
| 40 | + let new_exe = current_exe.with_extension("exe.new"); |
| 41 | + let old_exe = current_exe.with_extension("exe.old"); |
| 42 | + |
| 43 | + if let Err(e) = download_file(&download_url, &new_exe) { |
| 44 | + ui::print_warn(&format!("런처 다운로드 실패: {e}")); |
| 45 | + std::fs::remove_file(&new_exe).ok(); |
| 46 | + return; |
| 47 | + } |
| 48 | + |
| 49 | + if std::fs::rename(¤t_exe, &old_exe).is_err() { |
| 50 | + ui::print_warn("런처 교체 실패 (권한 부족)"); |
| 51 | + std::fs::remove_file(&new_exe).ok(); |
| 52 | + return; |
| 53 | + } |
| 54 | + |
| 55 | + if std::fs::rename(&new_exe, ¤t_exe).is_err() { |
| 56 | + std::fs::rename(&old_exe, ¤t_exe).ok(); |
| 57 | + ui::print_warn("런처 교체 실패"); |
| 58 | + return; |
| 59 | + } |
| 60 | + |
| 61 | + ui::print_ok(&format!("런처 업데이트 완료 ({})", latest.tag)); |
| 62 | + ui::print_info("다음 실행 시 새 버전이 적용됩니다"); |
| 63 | +} |
| 64 | + |
| 65 | +struct ReleaseInfo { |
| 66 | + tag: String, |
| 67 | + asset_url: Option<String>, |
| 68 | +} |
| 69 | + |
| 70 | +fn get_latest_release() -> Result<ReleaseInfo, String> { |
| 71 | + let client = reqwest::blocking::Client::builder() |
| 72 | + .user_agent("dartlab-desktop") |
| 73 | + .timeout(std::time::Duration::from_secs(5)) |
| 74 | + .build() |
| 75 | + .map_err(|e| e.to_string())?; |
| 76 | + |
| 77 | + let resp = client.get(GITHUB_API).send().map_err(|e| e.to_string())?; |
| 78 | + |
| 79 | + if !resp.status().is_success() { |
| 80 | + return Err(format!("HTTP {}", resp.status())); |
| 81 | + } |
| 82 | + |
| 83 | + let body = resp.text().map_err(|e| e.to_string())?; |
| 84 | + let json: serde_json::Value = serde_json::from_str(&body).map_err(|e| e.to_string())?; |
| 85 | + |
| 86 | + let tag = json["tag_name"] |
| 87 | + .as_str() |
| 88 | + .ok_or("No tag_name")? |
| 89 | + .to_string(); |
| 90 | + |
| 91 | + let asset_url = json["assets"] |
| 92 | + .as_array() |
| 93 | + .and_then(|assets| { |
| 94 | + assets.iter().find_map(|a| { |
| 95 | + let name = a["name"].as_str()?; |
| 96 | + if name == "DartLab.exe" { |
| 97 | + a["browser_download_url"].as_str().map(|s| s.to_string()) |
| 98 | + } else { |
| 99 | + None |
| 100 | + } |
| 101 | + }) |
| 102 | + }); |
| 103 | + |
| 104 | + Ok(ReleaseInfo { tag, asset_url }) |
| 105 | +} |
| 106 | + |
| 107 | +fn download_file(url: &str, dest: &PathBuf) -> Result<(), String> { |
| 108 | + let client = reqwest::blocking::Client::builder() |
| 109 | + .user_agent("dartlab-desktop") |
| 110 | + .build() |
| 111 | + .map_err(|e| e.to_string())?; |
| 112 | + |
| 113 | + let resp = client.get(url).send().map_err(|e| format!("Download failed: {e}"))?; |
| 114 | + |
| 115 | + if !resp.status().is_success() { |
| 116 | + return Err(format!("HTTP {}", resp.status())); |
| 117 | + } |
| 118 | + |
| 119 | + let bytes = resp.bytes().map_err(|e| e.to_string())?; |
| 120 | + std::fs::write(dest, &bytes).map_err(|e| e.to_string())?; |
| 121 | + Ok(()) |
| 122 | +} |
0 commit comments