diff --git a/scripts/check.mjs b/scripts/check.mjs index b662cdd..1b44b95 100644 --- a/scripts/check.mjs +++ b/scripts/check.mjs @@ -10,6 +10,7 @@ const cwd = process.cwd(); const TEMP_DIR = path.join(cwd, "node_modules/.verge"); const FORCE = process.argv.includes("--force"); +const META = process.argv.includes("--meta"); // use Clash.Meta /** * get the correct clash release infomation @@ -44,55 +45,139 @@ function resolveClash() { return { url, zip, exefile, zipfile }; } +/** + * get the correct Clash.Meta release infomation + */ +async function resolveClashMeta() { + const { platform, arch } = process; + + const urlPrefix = `https://github.com/MetaCubeX/Clash.Meta/releases/download/`; + const latestVersion = "v1.11.0"; + + const map = { + "win32-x64": "Clash.Meta-windows-amd64v3", + "darwin-x64": "Clash.Meta-darwin-amd64v3", + "darwin-arm64": "Clash.Meta-darwin-arm64", + "linux-x64": "Clash.Meta-linux-amd64v3", + }; + + const name = map[`${platform}-${arch}`]; + + if (!name) { + throw new Error(`unsupport platform "${platform}-${arch}"`); + } + + const isWin = platform === "win32"; + const ext = isWin ? "zip" : "gz"; + const url = `${urlPrefix}${latestVersion}/${name}-${latestVersion}.${ext}`; + const exefile = `${name}${isWin ? ".exe" : ""}`; + const zipfile = `${name}-${latestVersion}.${ext}`; + + return { url, zip: ext, exefile, zipfile }; +} + /** * get the sidecar bin + * clash and Clash Meta */ async function resolveSidecar() { const sidecarDir = path.join(cwd, "src-tauri", "sidecar"); const host = execSync("rustc -vV | grep host").toString().slice(6).trim(); const ext = process.platform === "win32" ? ".exe" : ""; - const sidecarFile = `clash-${host}${ext}`; - const sidecarPath = path.join(sidecarDir, sidecarFile); - await fs.mkdirp(sidecarDir); - if (!FORCE && (await fs.pathExists(sidecarPath))) return; + await clash(); + if (META) await clashMeta(); - // download sidecar - const binInfo = resolveClash(); - const tempDir = path.join(TEMP_DIR, "clash"); - const tempZip = path.join(tempDir, binInfo.zipfile); - const tempExe = path.join(tempDir, binInfo.exefile); + async function clash() { + const sidecarFile = `clash-${host}${ext}`; + const sidecarPath = path.join(sidecarDir, sidecarFile); - await fs.mkdirp(tempDir); - if (!(await fs.pathExists(tempZip))) await downloadFile(binInfo.url, tempZip); + await fs.mkdirp(sidecarDir); + if (!FORCE && (await fs.pathExists(sidecarPath))) return; - if (binInfo.zip === "zip") { - const zip = new AdmZip(tempZip); - zip.getEntries().forEach((entry) => { - console.log("[INFO]: entry name", entry.entryName); - }); - zip.extractAllTo(tempDir, true); - // save as sidecar - await fs.rename(tempExe, sidecarPath); - console.log(`[INFO]: unzip finished`); - } else { - // gz - const readStream = fs.createReadStream(tempZip); - const writeStream = fs.createWriteStream(sidecarPath); - readStream - .pipe(zlib.createGunzip()) - .pipe(writeStream) - .on("finish", () => { - console.log(`[INFO]: gunzip finished`); - execSync(`chmod 755 ${sidecarPath}`); - console.log(`[INFO]: chmod binary finished`); - }) - .on("error", (error) => console.error(error)); + // download sidecar + const binInfo = resolveClash(); + const tempDir = path.join(TEMP_DIR, "clash"); + const tempZip = path.join(tempDir, binInfo.zipfile); + const tempExe = path.join(tempDir, binInfo.exefile); + + await fs.mkdirp(tempDir); + if (!(await fs.pathExists(tempZip))) + await downloadFile(binInfo.url, tempZip); + + if (binInfo.zip === "zip") { + const zip = new AdmZip(tempZip); + zip.getEntries().forEach((entry) => { + console.log("[INFO]: entry name", entry.entryName); + }); + zip.extractAllTo(tempDir, true); + // save as sidecar + await fs.rename(tempExe, sidecarPath); + console.log(`[INFO]: unzip finished`); + } else { + // gz + const readStream = fs.createReadStream(tempZip); + const writeStream = fs.createWriteStream(sidecarPath); + readStream + .pipe(zlib.createGunzip()) + .pipe(writeStream) + .on("finish", () => { + console.log(`[INFO]: gunzip finished`); + execSync(`chmod 755 ${sidecarPath}`); + console.log(`[INFO]: chmod binary finished`); + }) + .on("error", (error) => console.error(error)); + } + + // delete temp dir + await fs.remove(tempDir); } - // delete temp dir - await fs.remove(tempDir); + async function clashMeta() { + const sidecarFile = `clash-meta-${host}${ext}`; + const sidecarPath = path.join(sidecarDir, sidecarFile); + + await fs.mkdirp(sidecarDir); + if (!FORCE && (await fs.pathExists(sidecarPath))) return; + + // download sidecar + const binInfo = await resolveClashMeta(); + const tempDir = path.join(TEMP_DIR, "clash-meta"); + const tempZip = path.join(tempDir, binInfo.zipfile); + const tempExe = path.join(tempDir, binInfo.exefile); + + await fs.mkdirp(tempDir); + if (!(await fs.pathExists(tempZip))) + await downloadFile(binInfo.url, tempZip); + + if (binInfo.zip === "zip") { + const zip = new AdmZip(tempZip); + zip.getEntries().forEach((entry) => { + console.log("[INFO]: entry name", entry.entryName); + }); + zip.extractAllTo(tempDir, true); + // save as sidecar + await fs.rename(tempExe, sidecarPath); + console.log(`[INFO]: unzip finished`); + } else { + // gz + const readStream = fs.createReadStream(tempZip); + const writeStream = fs.createWriteStream(sidecarPath); + readStream + .pipe(zlib.createGunzip()) + .pipe(writeStream) + .on("finish", () => { + console.log(`[INFO]: gunzip finished`); + execSync(`chmod 755 ${sidecarPath}`); + console.log(`[INFO]: chmod binary finished`); + }) + .on("error", (error) => console.error(error)); + } + + // delete temp dir + await fs.remove(tempDir); + } } /** diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index c734c04..14e02f1 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -46,7 +46,9 @@ winreg = { version = "0.10", features = ["transactions"] } [features] default = ["custom-protocol", "tauri/ayatana-tray"] custom-protocol = ["tauri/custom-protocol"] +meta-dev = ["clash-meta", "verge-dev"] verge-dev = [] +clash-meta = [] debug-yml = [] [profile.release] diff --git a/src-tauri/src/core/service.rs b/src-tauri/src/core/service.rs index 2a4f055..acde126 100644 --- a/src-tauri/src/core/service.rs +++ b/src-tauri/src/core/service.rs @@ -9,6 +9,8 @@ use std::{collections::HashMap, time::Duration}; use tauri::api::process::{Command, CommandChild, CommandEvent}; use tokio::time::sleep; +static mut CLASH_CORE: &str = "clash"; + #[derive(Debug)] pub struct Service { sidecar: Option, @@ -25,6 +27,12 @@ impl Service { } } + pub fn set_core(&mut self, clash_core: Option) { + unsafe { + CLASH_CORE = Box::leak(clash_core.unwrap_or("clash".into()).into_boxed_str()); + } + } + #[allow(unused)] pub fn set_mode(&mut self, enable: bool) { self.service_mode = enable; @@ -92,7 +100,8 @@ impl Service { let app_dir = dirs::app_home_dir(); let app_dir = app_dir.as_os_str().to_str().unwrap(); - let cmd = Command::new_sidecar("clash")?; + let clash_core = unsafe { CLASH_CORE }; + let cmd = Command::new_sidecar(clash_core)?; let (mut rx, cmd_child) = cmd.args(["-d", app_dir]).spawn()?; self.sidecar = Some(cmd_child); @@ -342,7 +351,9 @@ pub mod win_service { sleep(Duration::from_secs(1)).await; } - let bin_path = current_exe().unwrap().with_file_name("clash.exe"); + let clash_core = unsafe { CLASH_CORE }; + let clash_bin = format!("{clash_core}.exe"); + let bin_path = current_exe().unwrap().with_file_name(clash_bin); let bin_path = bin_path.as_os_str().to_str().unwrap(); let config_dir = dirs::app_home_dir(); diff --git a/src-tauri/src/core/verge.rs b/src-tauri/src/core/verge.rs index a5bf19d..a03bde4 100644 --- a/src-tauri/src/core/verge.rs +++ b/src-tauri/src/core/verge.rs @@ -45,6 +45,10 @@ pub struct Verge { /// theme setting pub theme_setting: Option, + + /// clash core path + #[serde(skip_serializing_if = "Option::is_none")] + pub clash_core: Option, } #[derive(Default, Debug, Clone, Deserialize, Serialize)] @@ -96,6 +100,9 @@ impl Verge { if patch.traffic_graph.is_some() { self.traffic_graph = patch.traffic_graph; } + if patch.clash_core.is_some() { + self.clash_core = patch.clash_core; + } // system setting if patch.enable_silent_start.is_some() { diff --git a/src-tauri/tauri.meta.conf.json b/src-tauri/tauri.meta.conf.json new file mode 100644 index 0000000..9f4bb77 --- /dev/null +++ b/src-tauri/tauri.meta.conf.json @@ -0,0 +1,7 @@ +{ + "tauri": { + "bundle": { + "externalBin": ["sidecar/clash", "sidecar/clash-meta"] + } + } +}