fix: webdav backup resume and modal problem

This commit is contained in:
fullex 2025-04-01 19:04:45 +08:00 committed by 亢奋猫
parent d0cb333f3c
commit 8cb11e6d55

View File

@ -60,14 +60,25 @@ export async function reset() {
} }
// 备份到 webdav // 备份到 webdav
/**
* @param autoBackupProcess
* if call in auto backup process, not show any message, any error will be thrown
*/
export async function backupToWebdav({ export async function backupToWebdav({
showMessage = false, showMessage = false,
customFileName = '' customFileName = '',
}: { showMessage?: boolean; customFileName?: string } = {}) { autoBackupProcess = false
}: { showMessage?: boolean; customFileName?: string; autoBackupProcess?: boolean } = {}) {
if (isManualBackupRunning) { if (isManualBackupRunning) {
Logger.log('[Backup] Manual backup already in progress') Logger.log('[Backup] Manual backup already in progress')
return return
} }
// force set showMessage to false when auto backup process
if (autoBackupProcess) {
showMessage = false
}
isManualBackupRunning = true
store.dispatch(setWebDAVSyncState({ syncing: true, lastSyncError: null })) store.dispatch(setWebDAVSyncState({ syncing: true, lastSyncError: null }))
@ -98,25 +109,41 @@ export async function backupToWebdav({
lastSyncError: null lastSyncError: null
}) })
) )
showMessage && window.message.success({ content: i18n.t('message.backup.success'), key: 'backup' }) if (showMessage && !autoBackupProcess) {
window.message.success({ content: i18n.t('message.backup.success'), key: 'backup' })
}
} else { } else {
// if auto backup process, throw error
if (autoBackupProcess) {
throw new Error(i18n.t('message.backup.failed'))
}
store.dispatch(setWebDAVSyncState({ lastSyncError: 'Backup failed' })) store.dispatch(setWebDAVSyncState({ lastSyncError: 'Backup failed' }))
window.message.error({ content: i18n.t('message.backup.failed'), key: 'backup' }) showMessage && window.message.error({ content: i18n.t('message.backup.failed'), key: 'backup' })
} }
} catch (error: any) { } catch (error: any) {
// if auto backup process, throw error
if (autoBackupProcess) {
throw error
}
store.dispatch(setWebDAVSyncState({ lastSyncError: error.message })) store.dispatch(setWebDAVSyncState({ lastSyncError: error.message }))
console.error('[Backup] backupToWebdav: Error uploading file to WebDAV:', error) console.error('[Backup] backupToWebdav: Error uploading file to WebDAV:', error)
window.modal.error({ showMessage &&
title: i18n.t('message.backup.failed'), window.modal.error({
content: error.message title: i18n.t('message.backup.failed'),
}) content: error.message
} finally {
store.dispatch(
setWebDAVSyncState({
lastSyncTime: Date.now(),
syncing: false
}) })
) throw error
} finally {
if (!autoBackupProcess) {
store.dispatch(
setWebDAVSyncState({
lastSyncTime: Date.now(),
syncing: false
})
)
}
isManualBackupRunning = false isManualBackupRunning = false
} }
} }
@ -149,7 +176,7 @@ let syncTimeout: NodeJS.Timeout | null = null
let isAutoBackupRunning = false let isAutoBackupRunning = false
let isManualBackupRunning = false let isManualBackupRunning = false
export function startAutoSync() { export function startAutoSync(immediate = false) {
if (autoSyncStarted) { if (autoSyncStarted) {
return return
} }
@ -165,9 +192,15 @@ export function startAutoSync() {
stopAutoSync() stopAutoSync()
scheduleNextBackup() scheduleNextBackup(immediate ? 'immediate' : 'fromLastSyncTime')
function scheduleNextBackup() { /**
* @param type 'immediate' | 'fromLastSyncTime' | 'fromNow'
* 'immediate', first backup right now
* 'fromLastSyncTime', schedule next backup from last sync time
* 'fromNow', schedule next backup from now
*/
function scheduleNextBackup(type: 'immediate' | 'fromLastSyncTime' | 'fromNow' = 'fromLastSyncTime') {
if (syncTimeout) { if (syncTimeout) {
clearTimeout(syncTimeout) clearTimeout(syncTimeout)
syncTimeout = null syncTimeout = null
@ -185,10 +218,15 @@ export function startAutoSync() {
// 用户指定的自动备份时间间隔(毫秒) // 用户指定的自动备份时间间隔(毫秒)
const requiredInterval = webdavSyncInterval * 60 * 1000 const requiredInterval = webdavSyncInterval * 60 * 1000
// 如果存在最后一次同步WebDAV的时间以它为参考计算下一次同步的时间 let timeUntilNextSync = 1000 //also immediate
const timeUntilNextSync = webdavSync?.lastSyncTime switch (type) {
? Math.max(1000, webdavSync.lastSyncTime + requiredInterval - Date.now()) case 'fromLastSyncTime': // 如果存在最后一次同步WebDAV的时间以它为参考计算下一次同步的时间
: requiredInterval timeUntilNextSync = Math.max(1000, (webdavSync?.lastSyncTime || 0) + requiredInterval - Date.now())
break
case 'fromNow':
timeUntilNextSync = requiredInterval
break
}
syncTimeout = setTimeout(performAutoBackup, timeUntilNextSync) syncTimeout = setTimeout(performAutoBackup, timeUntilNextSync)
@ -207,14 +245,62 @@ export function startAutoSync() {
} }
isAutoBackupRunning = true isAutoBackupRunning = true
try { const maxRetries = 4
console.log('[AutoSync] Starting auto backup...') let retryCount = 0
await backupToWebdav({ showMessage: false })
} catch (error) { while (retryCount < maxRetries) {
console.error('[AutoSync] Auto backup failed:', error) try {
} finally { console.log(`[AutoSync] Starting auto backup... (attempt ${retryCount + 1}/${maxRetries})`)
isAutoBackupRunning = false
scheduleNextBackup() await backupToWebdav({ autoBackupProcess: true })
store.dispatch(
setWebDAVSyncState({
lastSyncError: null,
lastSyncTime: Date.now(),
syncing: false
})
)
isAutoBackupRunning = false
scheduleNextBackup()
break
} catch (error: any) {
retryCount++
if (retryCount === maxRetries) {
console.error('[AutoSync] Auto backup failed after all retries:', error)
store.dispatch(
setWebDAVSyncState({
lastSyncError: 'Auto backup failed',
lastSyncTime: Date.now(),
syncing: false
})
)
//only show 1 time error modal, and autoback stopped until user click ok
await window.modal.error({
title: i18n.t('message.backup.failed'),
content: `[WebDAV Auto Backup] ${new Date().toLocaleString()} ` + error.message
})
scheduleNextBackup('fromNow')
isAutoBackupRunning = false
} else {
//Exponential Backoff with Base 2 7s、17s、37s
const backoffDelay = Math.pow(2, retryCount - 1) * 10000 - 3000
console.log(`[AutoSync] Failed, retry ${retryCount}/${maxRetries} after ${backoffDelay / 1000}s`)
await new Promise((resolve) => setTimeout(resolve, backoffDelay))
//in case auto backup is stopped by user
if (!isAutoBackupRunning) {
console.log('[AutoSync] retry cancelled by user, exit')
break
}
}
}
} }
} }
} }