feat(restoreFromWebdav): make credentials and path optional (#6922)

* feat(BackupService): add feedback messages for backup operations

* feat(WebDAV): Allow optional username, password, and path for unauthenticated access
This commit is contained in:
Doekin 2025-06-07 21:25:08 +08:00 committed by GitHub
parent ea7766db44
commit c38a6cdfbf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 22 additions and 22 deletions

View File

@ -1,7 +1,8 @@
import { WebDavConfig } from '@types'
import Logger from 'electron-log'
import Stream from 'stream'
import https from 'https'
import path from 'path'
import Stream from 'stream'
import {
BufferLike,
createClient,
@ -15,7 +16,7 @@ export default class WebDav {
private webdavPath: string
constructor(params: WebDavConfig) {
this.webdavPath = params.webdavPath
this.webdavPath = params.webdavPath || '/'
this.instance = createClient(params.webdavHost, {
username: params.webdavUser,
@ -51,7 +52,7 @@ export default class WebDav {
throw error
}
const remoteFilePath = `${this.webdavPath}/${filename}`
const remoteFilePath = path.posix.join(this.webdavPath, filename)
try {
return await this.instance.putFileContents(remoteFilePath, data, options)
@ -66,7 +67,7 @@ export default class WebDav {
throw new Error('WebDAV client not initialized')
}
const remoteFilePath = `${this.webdavPath}/${filename}`
const remoteFilePath = path.posix.join(this.webdavPath, filename)
try {
return await this.instance.getFileContents(remoteFilePath, options)
@ -120,7 +121,7 @@ export default class WebDav {
throw new Error('WebDAV client not initialized')
}
const remoteFilePath = `${this.webdavPath}/${filename}`
const remoteFilePath = path.posix.join(this.webdavPath, filename)
try {
return await this.instance.deleteFile(remoteFilePath)

View File

@ -14,9 +14,9 @@ interface BackupFile {
interface WebdavConfig {
webdavHost: string
webdavUser: string
webdavPass: string
webdavPath: string
webdavUser?: string
webdavPass?: string
webdavPath?: string
}
interface WebdavBackupManagerProps {
@ -47,7 +47,7 @@ export function WebdavBackupManager({ visible, onClose, webdavConfig, restoreMet
const { webdavHost, webdavUser, webdavPass, webdavPath } = webdavConfig
const fetchBackupFiles = useCallback(async () => {
if (!webdavHost || !webdavUser || !webdavPass || !webdavPath) {
if (!webdavHost) {
message.error(t('message.error.invalid.webdav'))
return
}
@ -93,7 +93,7 @@ export function WebdavBackupManager({ visible, onClose, webdavConfig, restoreMet
return
}
if (!webdavHost || !webdavUser || !webdavPass || !webdavPath) {
if (!webdavHost) {
message.error(t('message.error.invalid.webdav'))
return
}
@ -132,7 +132,7 @@ export function WebdavBackupManager({ visible, onClose, webdavConfig, restoreMet
}
const handleDeleteSingle = async (fileName: string) => {
if (!webdavHost || !webdavUser || !webdavPass || !webdavPath) {
if (!webdavHost) {
message.error(t('message.error.invalid.webdav'))
return
}
@ -165,7 +165,7 @@ export function WebdavBackupManager({ visible, onClose, webdavConfig, restoreMet
}
const handleRestore = async (fileName: string) => {
if (!webdavHost || !webdavUser || !webdavPass || !webdavPath) {
if (!webdavHost) {
message.error(t('message.error.invalid.webdav'))
return
}

View File

@ -123,7 +123,7 @@ export function useWebdavRestoreModal({
const [backupFiles, setBackupFiles] = useState<BackupFile[]>([])
const showRestoreModal = useCallback(async () => {
if (!webdavHost || !webdavUser || !webdavPass || !webdavPath) {
if (!webdavHost) {
window.message.error({ content: t('message.error.invalid.webdav'), key: 'webdav-error' })
return
}
@ -146,7 +146,7 @@ export function useWebdavRestoreModal({
}, [webdavHost, webdavUser, webdavPass, webdavPath, t])
const handleRestore = useCallback(async () => {
if (!selectedFile || !webdavHost || !webdavUser || !webdavPass || !webdavPath) {
if (!selectedFile || !webdavHost) {
window.message.error({
content: !selectedFile ? t('message.error.no.file.selected') : t('message.error.invalid.webdav'),
key: 'restore-error'
@ -170,7 +170,7 @@ export function useWebdavRestoreModal({
}
}
})
}, [selectedFile, webdavHost, webdavUser, webdavPass, webdavPath, t, restoreMethod])
}, [selectedFile, webdavHost, t, restoreMethod])
const handleCancel = () => {
setIsRestoreModalVisible(false)

View File

@ -165,10 +165,7 @@ const WebDavSettings: FC = () => {
<Button onClick={showBackupModal} icon={<SaveOutlined />} loading={backuping}>
{t('settings.data.webdav.backup.button')}
</Button>
<Button
onClick={showBackupManager}
icon={<FolderOpenOutlined />}
disabled={!webdavHost || !webdavUser || !webdavPass || !webdavPath}>
<Button onClick={showBackupManager} icon={<FolderOpenOutlined />} disabled={!webdavHost}>
{t('settings.data.webdav.restore.button')}
</Button>
</HStack>

View File

@ -137,6 +137,7 @@ export async function backupToWebdav({
timestamp: Date.now(),
source: 'backup'
})
showMessage && window.message.success({ content: i18n.t('message.backup.success'), key: 'backup' })
// 清理旧备份文件
if (webdavMaxBackups > 0) {
@ -202,6 +203,7 @@ export async function backupToWebdav({
source: 'backup'
})
store.dispatch(setWebDAVSyncState({ lastSyncError: error.message }))
showMessage && window.message.error({ content: i18n.t('message.backup.failed'), key: 'backup' })
console.error('[Backup] backupToWebdav: Error uploading file to WebDAV:', error)
throw error
} finally {

View File

@ -347,9 +347,9 @@ export type CodeStyleVarious = 'auto' | string
export type WebDavConfig = {
webdavHost: string
webdavUser: string
webdavPass: string
webdavPath: string
webdavUser?: string
webdavPass?: string
webdavPath?: string
fileName?: string
skipBackupFile?: boolean
}