import { createRequire } from 'node:module' import { app, BrowserWindow } from 'electron' import type { AppUpdater } from 'electron-updater' import { IPC } from '../../shared/ipc-channels' const require = createRequire(import.meta.url) let autoUpdaterRef: AppUpdater | null = null function getAutoUpdater(): AppUpdater { if (!autoUpdaterRef) { const electronUpdater = require('electron-updater') as { autoUpdater: AppUpdater } autoUpdaterRef = electronUpdater.autoUpdater } return autoUpdaterRef } export interface UpdateInfoPayload { version: string releaseNotes?: string } export interface UpdateProgressPayload { percent: number transferred: number total: number } export class UpdateService { private disabled: boolean private mockVersion: string | null = null constructor() { if (process.env.BILIN_MOCK_UPDATE === '1') { this.disabled = false this.mockVersion = process.env.BILIN_MOCK_UPDATE_VERSION ?? '9.9.9' return } this.disabled = process.env.BILIN_E2E === '1' || !app.isPackaged || process.env.NODE_ENV === 'development' const autoUpdater = getAutoUpdater() autoUpdater.autoDownload = false autoUpdater.autoInstallOnAppQuit = true autoUpdater.on('update-available', (info) => { this.broadcast(IPC.UPDATE_AVAILABLE, { version: info.version, releaseNotes: typeof info.releaseNotes === 'string' ? info.releaseNotes : undefined }) }) autoUpdater.on('download-progress', (p) => { this.broadcast(IPC.UPDATE_PROGRESS, { percent: p.percent, transferred: p.transferred, total: p.total }) }) autoUpdater.on('update-downloaded', (info) => { this.broadcast(IPC.UPDATE_DOWNLOADED, { version: info.version }) }) autoUpdater.on('error', (err) => { this.broadcast(IPC.UPDATE_ERROR, { message: err.message }) }) } async check(): Promise { if (this.disabled) return null if (this.mockVersion) { const payload = { version: this.mockVersion, releaseNotes: 'Mock update' } this.broadcast(IPC.UPDATE_AVAILABLE, payload) return payload } const result = await getAutoUpdater().checkForUpdates() const info = result?.updateInfo if (!info) return null return { version: info.version, releaseNotes: typeof info.releaseNotes === 'string' ? info.releaseNotes : undefined } } async download(): Promise { if (this.disabled) return if (this.mockVersion) { this.broadcast(IPC.UPDATE_PROGRESS, { percent: 50, transferred: 50, total: 100 }) this.broadcast(IPC.UPDATE_DOWNLOADED, { version: this.mockVersion }) return } await getAutoUpdater().downloadUpdate() } install(): void { if (this.disabled || this.mockVersion) return getAutoUpdater().quitAndInstall() } private broadcast(channel: string, payload: unknown): void { try { for (const win of BrowserWindow.getAllWindows()) { win.webContents.send(channel, payload) } } catch { /* test env */ } } }