diff --git a/bun.lock b/bun.lock index 28ffad0..d581ed1 100644 --- a/bun.lock +++ b/bun.lock @@ -18,7 +18,7 @@ "@eslint/eslintrc": "3.3.1", "@tailwindcss/postcss": "4.1.16", "@tauri-apps/cli": "2.9.2", - "@types/node": "24.9.2", + "@types/node": "24.10.0", "@types/react": "19.2.2", "@types/react-dom": "19.2.2", "eslint": "9.39.0", @@ -257,7 +257,7 @@ "@types/json5": ["@types/json5@0.0.29", "", {}, "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="], - "@types/node": ["@types/node@24.9.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-uWN8YqxXxqFMX2RqGOrumsKeti4LlmIMIyV0lgut4jx7KQBcBiW6vkDtIBvHnHIquwNfJhk8v2OtmO8zXWHfPA=="], + "@types/node": ["@types/node@24.10.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A=="], "@types/react": ["@types/react@19.2.2", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA=="], @@ -381,7 +381,7 @@ "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], - "caniuse-lite": ["caniuse-lite@1.0.30001752", "", {}, "sha512-vKUk7beoukxE47P5gcVNKkDRzXdVofotshHwfR9vmpeFKxmI5PBpgOMC18LUJUA/DvJ70Y7RveasIBraqsyO/g=="], + "caniuse-lite": ["caniuse-lite@1.0.30001753", "", {}, "sha512-Bj5H35MD/ebaOV4iDLqPEtiliTN29qkGtEHCwawWn4cYm+bPJM2NsaP30vtZcnERClMzp52J4+aw2UNbK4o+zw=="], "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], diff --git a/package.json b/package.json index 38212c4..13dd155 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "devDependencies": { "@tauri-apps/cli": "2.9.2", "typescript": "5.9.3", - "@types/node": "24.9.2", + "@types/node": "24.10.0", "@types/react": "19.2.2", "@types/react-dom": "19.2.2", "@tailwindcss/postcss": "4.1.16", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 98b04cd..e34dce5 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -22,6 +22,7 @@ tauri-plugin-os = "2.3.2" reqwest = { version = "0.12.24", default-features = false, features = ["stream", "rustls-tls"] } tauri-plugin-opener = "2.5.2" tauri-plugin-dialog = "2.4.2" +sha2 = "0.10.9" [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] tauri-plugin-single-instance = "2.3.6" diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 4b91da7..fa23a21 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,3 +1,4 @@ +use sha2::{Digest, Sha256}; use std::{ fs::{File, create_dir_all}, io::{BufReader, copy}, @@ -41,6 +42,13 @@ async fn unzip_to_dir(zip_path: PathBuf, out_dir: PathBuf) -> String { } } +fn get_sha256_hash(data: &[u8]) -> String { + let mut hasher = Sha256::new(); + hasher.update(data); + let hash = hasher.finalize(); + format!("{:x}", hash) +} + #[tauri::command] async fn check_latest_ver(app: AppHandle, version: String) -> String { let updates_path = app.path().app_local_data_dir().unwrap().join("updates"); @@ -55,7 +63,7 @@ async fn check_latest_ver(app: AppHandle, version: String) -> String { } #[tauri::command] -async fn download(app: AppHandle, url: String, name: String) -> String { +async fn download(app: AppHandle, url: String, name: String, hash: String) -> String { let client = reqwest::Client::new(); let resp = match client.get(&url).send().await { Ok(r) => r, @@ -66,6 +74,11 @@ async fn download(app: AppHandle, url: String, name: String) -> String { Err(_) => return "-1".to_string(), }; + let download_hash = get_sha256_hash(&bytes); + if hash != download_hash { + return "-2".to_string(); + } + let downloads_path = app.path().app_local_data_dir().unwrap().join("downloads"); let updates_path = app.path().app_local_data_dir().unwrap().join("updates"); diff --git a/src/app/page.tsx b/src/app/page.tsx index da2bc69..81884b1 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -66,11 +66,15 @@ export default function Home () { if (!launcherUpdateData) return const downloadResult = await invoke('download', { url: getDownloadLink(launcherUpdateData), - name: launcherLatestRequest.data + name: launcherLatestRequest.data, + hash: getDownloadHash(launcherUpdateData) }) - if (downloadResult !== '1') { + if (downloadResult == '-1') { setState('Failed. Check internet connection.') return + } else if (downloadResult == '-2') { + setState('File integrity check failed.') + return } setState('Starting...') } @@ -101,6 +105,26 @@ export default function Home () { return undefined } + function getDownloadHash (version: LauncherUpdate): string | undefined { + const p = platform() + const a = arch() + + const findUrl = (plat: string) => { + const i = version.platforms.indexOf(plat) + return i >= 0 ? version.sha256sums[i] : undefined + } + + if (p === 'windows') { + if (a === 'x86_64') return findUrl('windows-x64') + if (a === 'aarch64') return findUrl('windows-arm64') + } else if (p === 'macos') { + if (a === 'x86_64') return findUrl('macos-intel') + if (a === 'aarch64') return findUrl('macos-silicon') + } else if (p === 'linux') return findUrl(p) + + return undefined + } + return ( <>