CI 修复 (electron-builder.yml): - 移除 mac target 的 arch: [arm64, x64],改由 CI matrix 的 --arch flag 控制 - 根因:两个 macOS job 各自构建两个架构的 DMG,产生两个 draft release, 导致 release job 只发布了没有 DMG 的那个,macOS 产物丢失 下载按钮无响应修复: - electron/updater.ts: downloadUpdate IPC handler 加 try-catch, 失败时通过 sendStatus 发送 error 事件给前端,避免静默失败 - AppShell.tsx: downloadUpdate 回调点击后立即设置 downloadProgress=0 提供即时 UI 反馈;加 await + catch 处理下载失败并重置状态 - AppShell.tsx: error 事件处理中重置 downloadProgress 为 null, 使下载按钮在失败后重新出现 - UpdateDialog.tsx / GeneralSection.tsx: isDownloading 判断移除 > 0 条件, downloadProgress === 0 时也显示下载中状态,消除点击后无反馈的间隙 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
133 lines
3.7 KiB
TypeScript
133 lines
3.7 KiB
TypeScript
import { autoUpdater } from 'electron-updater';
|
|
import type { BrowserWindow } from 'electron';
|
|
import { ipcMain, session } from 'electron';
|
|
|
|
let mainWindow: BrowserWindow | null = null;
|
|
|
|
function sendStatus(data: Record<string, unknown>) {
|
|
mainWindow?.webContents.send('updater:status', data);
|
|
}
|
|
|
|
/**
|
|
* Resolve system proxy for GitHub and inject into electron-updater
|
|
* so that VPN / proxy tools are respected during update downloads.
|
|
*/
|
|
async function configureProxy() {
|
|
try {
|
|
const proxy = await session.defaultSession.resolveProxy('https://github.com');
|
|
// proxy returns "DIRECT" or "PROXY host:port" / "SOCKS5 host:port" etc.
|
|
if (proxy && proxy !== 'DIRECT') {
|
|
const match = proxy.match(/^(?:PROXY|HTTPS)\s+(.+)/i);
|
|
if (match) {
|
|
process.env.HTTPS_PROXY = `http://${match[1]}`;
|
|
console.log('[updater] Using system proxy:', process.env.HTTPS_PROXY);
|
|
}
|
|
const socksMatch = proxy.match(/^SOCKS5?\s+(.+)/i);
|
|
if (socksMatch) {
|
|
process.env.HTTPS_PROXY = `socks5://${socksMatch[1]}`;
|
|
console.log('[updater] Using system SOCKS proxy:', process.env.HTTPS_PROXY);
|
|
}
|
|
}
|
|
} catch (err) {
|
|
console.warn('[updater] Failed to resolve proxy:', err);
|
|
}
|
|
}
|
|
|
|
export function initAutoUpdater(win: BrowserWindow) {
|
|
mainWindow = win;
|
|
|
|
// Configuration — don't auto-download, let user trigger manually
|
|
autoUpdater.autoDownload = false;
|
|
autoUpdater.autoInstallOnAppQuit = true;
|
|
|
|
// Resolve and apply system proxy for update downloads
|
|
configureProxy();
|
|
|
|
// --- Events ---
|
|
autoUpdater.on('checking-for-update', () => {
|
|
sendStatus({ status: 'checking' });
|
|
});
|
|
|
|
autoUpdater.on('update-available', (info) => {
|
|
sendStatus({
|
|
status: 'available',
|
|
info: {
|
|
version: info.version,
|
|
releaseNotes: info.releaseNotes,
|
|
releaseName: info.releaseName,
|
|
releaseDate: info.releaseDate,
|
|
},
|
|
});
|
|
});
|
|
|
|
autoUpdater.on('update-not-available', () => {
|
|
sendStatus({ status: 'not-available' });
|
|
});
|
|
|
|
autoUpdater.on('download-progress', (progress) => {
|
|
sendStatus({
|
|
status: 'downloading',
|
|
progress: {
|
|
percent: progress.percent,
|
|
bytesPerSecond: progress.bytesPerSecond,
|
|
transferred: progress.transferred,
|
|
total: progress.total,
|
|
},
|
|
});
|
|
});
|
|
|
|
autoUpdater.on('update-downloaded', (info) => {
|
|
sendStatus({
|
|
status: 'downloaded',
|
|
info: {
|
|
version: info.version,
|
|
releaseNotes: info.releaseNotes,
|
|
releaseName: info.releaseName,
|
|
releaseDate: info.releaseDate,
|
|
},
|
|
});
|
|
});
|
|
|
|
autoUpdater.on('error', (err) => {
|
|
sendStatus({ status: 'error', error: err.message });
|
|
});
|
|
|
|
// --- IPC handlers ---
|
|
ipcMain.handle('updater:check', async () => {
|
|
return autoUpdater.checkForUpdates();
|
|
});
|
|
|
|
ipcMain.handle('updater:download', async () => {
|
|
try {
|
|
return await autoUpdater.downloadUpdate();
|
|
} catch (err) {
|
|
const message = err instanceof Error ? err.message : String(err);
|
|
console.error('[updater] Download failed:', message);
|
|
sendStatus({ status: 'error', error: message });
|
|
throw err;
|
|
}
|
|
});
|
|
|
|
ipcMain.handle('updater:quit-and-install', () => {
|
|
autoUpdater.quitAndInstall();
|
|
});
|
|
|
|
// Initial check after 10 seconds
|
|
setTimeout(() => {
|
|
autoUpdater.checkForUpdates().catch((err) => {
|
|
console.warn('[updater] Initial check failed:', err.message);
|
|
});
|
|
}, 10_000);
|
|
|
|
// Periodic check every 4 hours
|
|
setInterval(() => {
|
|
autoUpdater.checkForUpdates().catch((err) => {
|
|
console.warn('[updater] Periodic check failed:', err.message);
|
|
});
|
|
}, 4 * 60 * 60 * 1000);
|
|
}
|
|
|
|
export function setUpdaterWindow(win: BrowserWindow) {
|
|
mainWindow = win;
|
|
}
|