@@ -67,6 +67,18 @@ const DOWNLOAD_RETRY_DELAYS = [30_000, 60_000, 120_000]; // 30s, 60s, 120s
6767/** 标记当前是否正在下载(用于区分 error 事件来源) */
6868let isDownloading = false ;
6969
70+ /**
71+ * CL-L4: 更新状态机——主进程侧状态校验,防止渲染层在任意时机调用
72+ * install(会立即退出应用)或重复触发 download(并发下载)
73+ */
74+ type UpdateState = 'idle' | 'checking' | 'available' | 'downloading' | 'downloaded' | 'error' ;
75+ let updateState : UpdateState = 'idle' ;
76+
77+ function setUpdateState ( state : UpdateState ) : void {
78+ updateState = state ;
79+ logger . info ( `[AutoUpdater] State → ${ state } ` ) ;
80+ }
81+
7082/**
7183 * 设置自动检查开关
7284 * 由 main.ts 的 IPC handler 调用
@@ -127,11 +139,13 @@ export function initAutoUpdater(mainWindow: BrowserWindow | null): void {
127139
128140 autoUpdater . on ( 'checking-for-update' , ( ) => {
129141 logger . info ( '[AutoUpdater] Checking for update...' ) ;
142+ setUpdateState ( 'checking' ) ;
130143 sendToRenderer ( mainWindow , 'update-status' , { status : 'checking' } ) ;
131144 } ) ;
132145
133146 autoUpdater . on ( 'update-available' , ( info : UpdateInfo ) => {
134147 logger . info ( `[AutoUpdater] Update available: v${ info . version } ` ) ;
148+ setUpdateState ( 'available' ) ;
135149 sendToRenderer ( mainWindow , 'update-status' , {
136150 status : 'available' ,
137151 version : info . version ,
@@ -141,10 +155,12 @@ export function initAutoUpdater(mainWindow: BrowserWindow | null): void {
141155
142156 autoUpdater . on ( 'update-not-available' , ( ) => {
143157 logger . info ( '[AutoUpdater] No update available' ) ;
158+ setUpdateState ( 'idle' ) ;
144159 sendToRenderer ( mainWindow , 'update-status' , { status : 'not-available' } ) ;
145160 } ) ;
146161
147162 autoUpdater . on ( 'download-progress' , ( progress : ProgressInfo ) => {
163+ setUpdateState ( 'downloading' ) ;
148164 sendToRenderer ( mainWindow , 'update-status' , {
149165 status : 'downloading' ,
150166 percent : Math . round ( progress . percent ) ,
@@ -157,6 +173,7 @@ export function initAutoUpdater(mainWindow: BrowserWindow | null): void {
157173 // 下载成功,重置重试计数
158174 downloadRetryCount = 0 ;
159175 isDownloading = false ;
176+ setUpdateState ( 'downloaded' ) ;
160177 sendToRenderer ( mainWindow , 'update-status' , {
161178 status : 'downloaded' ,
162179 version : info . version ,
@@ -259,14 +276,25 @@ export function checkForUpdate(): void {
259276}
260277
261278export function downloadUpdate ( ) : void {
279+ // CL-L4: 仅 available 态允许下载,防止重复触发并发下载
280+ if ( updateState !== 'available' ) {
281+ logger . warn ( `[AutoUpdater] downloadUpdate ignored (state=${ updateState } , expected=available)` ) ;
282+ return ;
283+ }
262284 isDownloading = true ;
263285 downloadRetryCount = 0 ;
286+ setUpdateState ( 'downloading' ) ;
264287 autoUpdater . downloadUpdate ( ) . catch ( ( err ) => {
265288 logger . error ( '[AutoUpdater] Download failed' , err ) ;
266289 } ) ;
267290}
268291
269292export function installUpdate ( ) : void {
293+ // CL-L4: 仅 downloaded 态允许安装——quitAndInstall 会立即退出应用并安装
294+ if ( updateState !== 'downloaded' ) {
295+ logger . warn ( `[AutoUpdater] installUpdate ignored (state=${ updateState } , expected=downloaded)` ) ;
296+ return ;
297+ }
270298 autoUpdater . quitAndInstall ( ) ;
271299}
272300
0 commit comments