From 44aead42e95a1168a32e7e16014ccd99575525b4 Mon Sep 17 00:00:00 2001 From: Logosww <87473526+Logosww@users.noreply.github.com> Date: Tue, 29 Sep 2026 15:55:03 +0800 Subject: [PATCH] fix(macos): restore proxy startup on Apple Silicon --- scripts/run.js | 10 ++++++++ src/main/const.ts | 5 +++- src/main/install.ts | 59 +++++++++++++++++++++++++++------------------ src/main/main.ts | 18 +++++++++++++- 4 files changed, 67 insertions(+), 25 deletions(-) diff --git a/scripts/run.js b/scripts/run.js index 4efce1c9..2b3f325f 100644 --- a/scripts/run.js +++ b/scripts/run.js @@ -167,6 +167,16 @@ function updateNodeModules() { fs.rmSync(path.join(nodeDir, 'modules'), { recursive: true, force: true }); fs.renameSync(path.join(nodeDir, 'node_modules'), path.join(nodeDir, 'modules')); + // Yarn's local package install can omit Whistle's generated web UI bundle. + // Whistle reads this file during startup, so a missing bundle prevents the proxy from listening. + const webUiBundle = path.join('biz', 'webui', 'htdocs', 'js', 'index.js'); + const bundleSource = path.join(root, 'vendor', 'whistle', webUiBundle); + const bundleDest = path.join(nodeDir, 'modules', 'whistle', webUiBundle); + if (!fs.existsSync(bundleSource)) { + throw new Error(`Whistle web UI bundle is missing: ${bundleSource}`); + } + fs.mkdirSync(path.dirname(bundleDest), { recursive: true }); + fs.copyFileSync(bundleSource, bundleDest); updateZip(); } diff --git a/src/main/const.ts b/src/main/const.ts index 3d097449..cc719a9c 100644 --- a/src/main/const.ts +++ b/src/main/const.ts @@ -27,7 +27,10 @@ export const SYSTEM_IS_LINUX = systemTypelinux === 'Linux'; export const IPROXY_UPDATE_DIR = path.join(IPROXY_HOME_PATH, './update'); export const IPROXY_UPDATE_CONFIG = path.join(IPROXY_UPDATE_DIR, './config.json'); export const IPROXY_FILES_DIR = path.join(IPROXY_HOME_PATH, './files'); -export const PROXY_CONF_HELPER_FILE_PATH = path.join(IPROXY_FILES_DIR, './proxy_conf_helper'); +export const PROXY_CONF_HELPER_FILE_PATH = path.join( + IPROXY_FILES_DIR, + os.arch() === 'arm64' && SYSTEM_IS_MACOS ? 'proxy_conf_helper_arm64' : 'proxy_conf_helper', +); export const PROXY_CONF_HELPER_PATH = path.join(IPROXY_HOME_PATH, './proxy_conf_helper'); export const PROXY_REFRESH_WINDOWS_HELPER_PATH = path.join(IPROXY_FILES_DIR, './win_proxy_helper'); diff --git a/src/main/install.ts b/src/main/install.ts index 8c09fc23..9487db53 100644 --- a/src/main/install.ts +++ b/src/main/install.ts @@ -119,19 +119,16 @@ Application will quit treeKill(process.pid); } -export async function installCertAndHelper() { - console.log('Install cert'); - const certs = (await generateCert()) as { - key: string; - cert: string; - }; +export async function installCertAndHelper(installCert = true) { + const dir = installCert ? (await tempdir()).replace('ADMINI~1', 'Administrator') : ''; - const dir = (await tempdir()).replace('ADMINI~1', 'Administrator'); - - // 写入证书 - await fs.mkdirp(dir); - await fs.writeFileAsync(path.join(dir, CERT_KEY_FILE_NAME), certs.key, 'utf-8'); - await fs.writeFileAsync(path.join(dir, CERT_FILE_NAME), certs.cert, 'utf-8'); + if (installCert) { + console.log('Install cert'); + const certs = (await generateCert()) as { key: string; cert: string }; + await fs.mkdirp(dir); + await fs.writeFileAsync(path.join(dir, CERT_KEY_FILE_NAME), certs.key, 'utf-8'); + await fs.writeFileAsync(path.join(dir, CERT_FILE_NAME), certs.cert, 'utf-8'); + } const formatPath = (path: string) => '"' + path + '"'; @@ -141,11 +138,14 @@ export async function installCertAndHelper() { if (SYSTEM_IS_MACOS) { // macOS big sur do not allow trust cert in any auto way // show box to guide user run command + fs.removeSync(INSTALL_DONE_FILE); const showGuide = () => { - const cmd = `echo "Please input local login password 请输入本地登录密码" && sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "${path.join( - dir, - CERT_FILE_NAME, - )}" && sudo cp ${formatPath(PROXY_CONF_HELPER_FILE_PATH)} ${formatPath( + const trustCertCommand = installCert + ? `sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ${formatPath( + path.join(dir, CERT_FILE_NAME), + )} && ` + : ''; + const cmd = `echo "Please input local login password 请输入本地登录密码" && ${trustCertCommand}sudo cp ${formatPath(PROXY_CONF_HELPER_FILE_PATH)} ${formatPath( PROXY_CONF_HELPER_PATH, )} && sudo chown root:admin ${formatPath(PROXY_CONF_HELPER_PATH)} && sudo chmod a+rx+s ${formatPath( PROXY_CONF_HELPER_PATH, @@ -155,8 +155,8 @@ export async function installCertAndHelper() { dialog.showMessageBoxSync({ type: 'info', - message: `Paste command to your Terminal and run to install cert and helper - (命令已复制到剪贴板)粘贴命令到终端并运行以安装并信任证书 + message: `Paste command to your Terminal and run to install ${installCert ? 'the certificate and helper' : 'the proxy helper'} + (命令已复制到剪贴板)粘贴命令到终端并运行以安装${installCert ? '并信任证书及代理辅助程序' : '代理辅助程序'} `, }); }; @@ -227,8 +227,10 @@ export async function installCertAndHelper() { } console.log('after install'); // 信任完成,把证书目录拷贝过去 - await fs.copyAsync(dir, IPROXY_CERT_DIR_PATH); - console.log('copy cert done'); + if (installCert) { + await fs.copyAsync(dir, IPROXY_CERT_DIR_PATH); + console.log('copy cert done'); + } } async function checkCertInstall() { @@ -258,14 +260,25 @@ async function checkHelperInstall() { // 权限不对 return false; } - return true; + // An older install may still contain the Intel helper on Apple Silicon. + // Compare the installed binary with the one packaged for this architecture. + try { + const [installed, packaged] = await Promise.all([ + fs.readFileAsync(PROXY_CONF_HELPER_PATH), + fs.readFileAsync(PROXY_CONF_HELPER_FILE_PATH), + ]); + return installed.equals(packaged); + } catch (e) { + return false; + } } // 检查安装状态,如果没安装就安装一下 export default async function checkInstallStatus() { - if ((await checkCertInstall()) && (await checkHelperInstall())) { + const certInstalled = await checkCertInstall(); + if (certInstalled && (await checkHelperInstall())) { // pass } else { - await installCertAndHelper(); + await installCertAndHelper(!certInstalled); } } diff --git a/src/main/main.ts b/src/main/main.ts index 93565efb..7541fb77 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -331,7 +331,23 @@ async function initCopyFiles() { } const versionFile = path.join(IPROXY_FILES_DIR, 'version'); - if (fs.existsSync(versionFile) && fs.readFileSync(versionFile, 'utf-8') === version && !electronIsDev) { + const whistleBundle = path.join( + IPROXY_FILES_DIR, + 'node', + 'node_modules', + 'whistle', + 'biz', + 'webui', + 'htdocs', + 'js', + 'index.js', + ); + if ( + fs.existsSync(versionFile) && + fs.readFileSync(versionFile, 'utf-8') === version && + fs.existsSync(whistleBundle) && + !electronIsDev + ) { updateSplashProgress(100, 'Files already up to date'); } else { console.log('copy files');