mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-29 23:12:38 +08:00
fix: add compatibility for webdav servers that do not support streaming (#7992)
* fix: add compatibility for webdav servers that do not support streaming * fix: fix grammar error * fix: fix linter error * fix: remove unnecessary changes * revert: restore tolerance for failing to remove temp file after webdav backup failed * fix: add migration support
This commit is contained in:
parent
f155b98a92
commit
27c39415c2
@ -321,14 +321,22 @@ class BackupManager {
|
||||
async backupToWebdav(_: Electron.IpcMainInvokeEvent, data: string, webdavConfig: WebDavConfig) {
|
||||
const filename = webdavConfig.fileName || 'cherry-studio.backup.zip'
|
||||
const backupedFilePath = await this.backup(_, filename, data, undefined, webdavConfig.skipBackupFile)
|
||||
const contentLength = (await fs.stat(backupedFilePath)).size
|
||||
const webdavClient = new WebDav(webdavConfig)
|
||||
try {
|
||||
const result = await webdavClient.putFileContents(filename, fs.createReadStream(backupedFilePath), {
|
||||
overwrite: true,
|
||||
contentLength
|
||||
})
|
||||
// 上传成功后删除本地备份文件
|
||||
let result
|
||||
if (webdavConfig.disableStream) {
|
||||
const fileContent = await fs.readFile(backupedFilePath)
|
||||
result = await webdavClient.putFileContents(filename, fileContent, {
|
||||
overwrite: true
|
||||
})
|
||||
} else {
|
||||
const contentLength = (await fs.stat(backupedFilePath)).size
|
||||
result = await webdavClient.putFileContents(filename, fs.createReadStream(backupedFilePath), {
|
||||
overwrite: true,
|
||||
contentLength
|
||||
})
|
||||
}
|
||||
|
||||
await fs.remove(backupedFilePath)
|
||||
return result
|
||||
} catch (error) {
|
||||
|
||||
@ -27,6 +27,7 @@ interface WebdavBackupManagerProps {
|
||||
webdavUser?: string
|
||||
webdavPass?: string
|
||||
webdavPath?: string
|
||||
webdavDisableStream?: boolean
|
||||
}
|
||||
restoreMethod?: (fileName: string) => Promise<void>
|
||||
}
|
||||
|
||||
@ -1676,7 +1676,11 @@
|
||||
"syncError": "Backup Error",
|
||||
"syncStatus": "Backup Status",
|
||||
"title": "WebDAV",
|
||||
"user": "WebDAV User"
|
||||
"user": "WebDAV User",
|
||||
"disableStream": {
|
||||
"title": "Disable Stream Upload",
|
||||
"help": "When enabled, loads the file into memory before uploading. This can solve incompatibility issues with some WebDAV servers that do not support chunked uploads, but it will increase memory usage."
|
||||
}
|
||||
},
|
||||
"yuque": {
|
||||
"check": {
|
||||
|
||||
@ -1676,7 +1676,11 @@
|
||||
"syncError": "バックアップエラー",
|
||||
"syncStatus": "バックアップ状態",
|
||||
"title": "WebDAV",
|
||||
"user": "WebDAVユーザー"
|
||||
"user": "WebDAVユーザー",
|
||||
"disableStream": {
|
||||
"title": "ストリーミングアップロードを無効にする",
|
||||
"help": "有効にすると、アップロード前にファイルがメモリに読み込まれます。これにより、チャンクアップロードをサポートしていない一部のWebDAVサーバーとの互換性の問題を解決できますが、メモリ使用量が増加します。"
|
||||
}
|
||||
},
|
||||
"yuque": {
|
||||
"check": {
|
||||
|
||||
@ -1676,7 +1676,11 @@
|
||||
"syncError": "Ошибка резервного копирования",
|
||||
"syncStatus": "Статус резервного копирования",
|
||||
"title": "WebDAV",
|
||||
"user": "Пользователь WebDAV"
|
||||
"user": "Пользователь WebDAV",
|
||||
"disableStream": {
|
||||
"title": "Отключить потоковую загрузку",
|
||||
"help": "При включении файл загружается в память перед отправкой. Это может решить проблемы совместимости с некоторыми серверами WebDAV, не поддерживающими фрагментированную (chunked) загрузку, но увеличит потребление памяти."
|
||||
}
|
||||
},
|
||||
"yuque": {
|
||||
"check": {
|
||||
|
||||
@ -1676,7 +1676,11 @@
|
||||
"syncError": "备份错误",
|
||||
"syncStatus": "备份状态",
|
||||
"title": "WebDAV",
|
||||
"user": "WebDAV 用户名"
|
||||
"user": "WebDAV 用户名",
|
||||
"disableStream": {
|
||||
"title": "禁用流式上传",
|
||||
"help": "开启后,将文件加载到内存中再上传,可解决部分WebDAV服务不兼容chunked上传的问题,但会增加内存占用。"
|
||||
}
|
||||
},
|
||||
"yuque": {
|
||||
"check": {
|
||||
|
||||
@ -1676,7 +1676,11 @@
|
||||
"syncError": "備份錯誤",
|
||||
"syncStatus": "備份狀態",
|
||||
"title": "WebDAV",
|
||||
"user": "WebDAV 使用者名稱"
|
||||
"user": "WebDAV 使用者名稱",
|
||||
"disableStream": {
|
||||
"title": "禁用串流上傳",
|
||||
"help": "開啟後,將檔案載入到記憶體中再上傳,可解決部分 WebDAV 服務不相容 chunked 上傳的問題,但會增加記憶體佔用。"
|
||||
}
|
||||
},
|
||||
"yuque": {
|
||||
"check": {
|
||||
|
||||
@ -9,6 +9,7 @@ import { startAutoSync, stopAutoSync } from '@renderer/services/BackupService'
|
||||
import { useAppDispatch, useAppSelector } from '@renderer/store'
|
||||
import {
|
||||
setWebdavAutoSync,
|
||||
setWebdavDisableStream as _setWebdavDisableStream,
|
||||
setWebdavHost as _setWebdavHost,
|
||||
setWebdavMaxBackups as _setWebdavMaxBackups,
|
||||
setWebdavPass as _setWebdavPass,
|
||||
@ -32,7 +33,8 @@ const WebDavSettings: FC = () => {
|
||||
webdavPath: webDAVPath,
|
||||
webdavSyncInterval: webDAVSyncInterval,
|
||||
webdavMaxBackups: webDAVMaxBackups,
|
||||
webdavSkipBackupFile: webdDAVSkipBackupFile
|
||||
webdavSkipBackupFile: webdDAVSkipBackupFile,
|
||||
webdavDisableStream: webDAVDisableStream
|
||||
} = useSettings()
|
||||
|
||||
const [webdavHost, setWebdavHost] = useState<string | undefined>(webDAVHost)
|
||||
@ -40,6 +42,7 @@ const WebDavSettings: FC = () => {
|
||||
const [webdavPass, setWebdavPass] = useState<string | undefined>(webDAVPass)
|
||||
const [webdavPath, setWebdavPath] = useState<string | undefined>(webDAVPath)
|
||||
const [webdavSkipBackupFile, setWebdavSkipBackupFile] = useState<boolean>(webdDAVSkipBackupFile)
|
||||
const [webdavDisableStream, setWebdavDisableStream] = useState<boolean>(webDAVDisableStream)
|
||||
const [backupManagerVisible, setBackupManagerVisible] = useState(false)
|
||||
|
||||
const [syncInterval, setSyncInterval] = useState<number>(webDAVSyncInterval)
|
||||
@ -76,6 +79,11 @@ const WebDavSettings: FC = () => {
|
||||
dispatch(_setWebdavSkipBackupFile(value))
|
||||
}
|
||||
|
||||
const onDisableStreamChange = (value: boolean) => {
|
||||
setWebdavDisableStream(value)
|
||||
dispatch(_setWebdavDisableStream(value))
|
||||
}
|
||||
|
||||
const renderSyncStatus = () => {
|
||||
if (!webdavHost) return null
|
||||
|
||||
@ -220,6 +228,14 @@ const WebDavSettings: FC = () => {
|
||||
<SettingRow>
|
||||
<SettingHelpText>{t('settings.data.backup.skip_file_data_help')}</SettingHelpText>
|
||||
</SettingRow>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.data.webdav.disableStream.title')}</SettingRowTitle>
|
||||
<Switch checked={webdavDisableStream} onChange={onDisableStreamChange} />
|
||||
</SettingRow>
|
||||
<SettingRow>
|
||||
<SettingHelpText>{t('settings.data.webdav.disableStream.help')}</SettingHelpText>
|
||||
</SettingRow>
|
||||
{webdavSync && syncInterval > 0 && (
|
||||
<>
|
||||
<SettingDivider />
|
||||
@ -246,7 +262,8 @@ const WebDavSettings: FC = () => {
|
||||
webdavHost,
|
||||
webdavUser,
|
||||
webdavPass,
|
||||
webdavPath
|
||||
webdavPath,
|
||||
webdavDisableStream
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
|
||||
@ -154,8 +154,15 @@ export async function backupToWebdav({
|
||||
|
||||
store.dispatch(setWebDAVSyncState({ syncing: true, lastSyncError: null }))
|
||||
|
||||
const { webdavHost, webdavUser, webdavPass, webdavPath, webdavMaxBackups, webdavSkipBackupFile } =
|
||||
store.getState().settings
|
||||
const {
|
||||
webdavHost,
|
||||
webdavUser,
|
||||
webdavPass,
|
||||
webdavPath,
|
||||
webdavMaxBackups,
|
||||
webdavSkipBackupFile,
|
||||
webdavDisableStream
|
||||
} = store.getState().settings
|
||||
let deviceType = 'unknown'
|
||||
let hostname = 'unknown'
|
||||
try {
|
||||
@ -177,7 +184,8 @@ export async function backupToWebdav({
|
||||
webdavPass,
|
||||
webdavPath,
|
||||
fileName: finalFileName,
|
||||
skipBackupFile: webdavSkipBackupFile
|
||||
skipBackupFile: webdavSkipBackupFile,
|
||||
disableStream: webdavDisableStream
|
||||
})
|
||||
if (success) {
|
||||
store.dispatch(
|
||||
|
||||
@ -1799,6 +1799,16 @@ const migrateConfig = {
|
||||
} catch (error) {
|
||||
return state
|
||||
}
|
||||
},
|
||||
'122': (state: RootState) => {
|
||||
try {
|
||||
if (state.settings && typeof state.settings.webdavDisableStream === 'undefined') {
|
||||
state.settings.webdavDisableStream = false
|
||||
}
|
||||
return state
|
||||
} catch (error) {
|
||||
return state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -112,6 +112,7 @@ export interface SettingsState {
|
||||
webdavSyncInterval: number
|
||||
webdavMaxBackups: number
|
||||
webdavSkipBackupFile: boolean
|
||||
webdavDisableStream: boolean
|
||||
translateModelPrompt: string
|
||||
autoTranslateWithSpace: boolean
|
||||
showTranslateConfirm: boolean
|
||||
@ -273,6 +274,7 @@ export const initialState: SettingsState = {
|
||||
webdavSyncInterval: 0,
|
||||
webdavMaxBackups: 0,
|
||||
webdavSkipBackupFile: false,
|
||||
webdavDisableStream: false,
|
||||
translateModelPrompt: TRANSLATE_PROMPT,
|
||||
autoTranslateWithSpace: false,
|
||||
showTranslateConfirm: true,
|
||||
@ -501,6 +503,9 @@ const settingsSlice = createSlice({
|
||||
setWebdavSkipBackupFile: (state, action: PayloadAction<boolean>) => {
|
||||
state.webdavSkipBackupFile = action.payload
|
||||
},
|
||||
setWebdavDisableStream: (state, action: PayloadAction<boolean>) => {
|
||||
state.webdavDisableStream = action.payload
|
||||
},
|
||||
setCodeExecution: (state, action: PayloadAction<{ enabled?: boolean; timeoutMinutes?: number }>) => {
|
||||
if (action.payload.enabled !== undefined) {
|
||||
state.codeExecution.enabled = action.payload.enabled
|
||||
@ -801,6 +806,7 @@ export const {
|
||||
setWebdavSyncInterval,
|
||||
setWebdavMaxBackups,
|
||||
setWebdavSkipBackupFile,
|
||||
setWebdavDisableStream,
|
||||
setCodeExecution,
|
||||
setCodeEditor,
|
||||
setCodePreview,
|
||||
|
||||
@ -356,6 +356,7 @@ export type WebDavConfig = {
|
||||
webdavPath?: string
|
||||
fileName?: string
|
||||
skipBackupFile?: boolean
|
||||
disableStream?: boolean
|
||||
}
|
||||
|
||||
export type AppInfo = {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user