diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index 37573b11..86c3079f 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -222,6 +222,7 @@ "assistant.added.content": "Assistant added successfully", "backup.failed": "Backup failed", "backup.success": "Backup successful", + "backup.start.success": "Backup started", "chat.completion.paused": "Chat completion paused", "copied": "Copied!", "error.enter.api.host": "Please enter your API host first", @@ -363,6 +364,8 @@ "webdav.password": "WebDAV Password", "webdav.path": "WebDAV Path", "webdav.path.placeholder": "/backup", + "webdav.autoSync": "Auto Sync", + "webdav.minutes": "Minutes", "webdav.restore.button": "Restore from WebDAV", "webdav.title": "WebDAV", "webdav.user": "WebDAV User" diff --git a/src/renderer/src/i18n/locales/ru-ru.json b/src/renderer/src/i18n/locales/ru-ru.json index 781a29ed..1147ee4b 100644 --- a/src/renderer/src/i18n/locales/ru-ru.json +++ b/src/renderer/src/i18n/locales/ru-ru.json @@ -222,6 +222,7 @@ "assistant.added.content": "Ассистент успешно добавлен", "backup.failed": "Создание резервной копии не удалось", "backup.success": "Резервная копия успешно создана", + "backup.start.success": "Создание резервной копии начато", "chat.completion.paused": "Завершение чата приостановлено", "copied": "Скопировано!", "error.enter.api.host": "Пожалуйста, введите ваш API хост", @@ -363,6 +364,8 @@ "webdav.password": "Пароль WebDAV", "webdav.path": "Путь WebDAV", "webdav.path.placeholder": "/backup", + "webdav.autoSync": "Автоматическая синхронизация", + "webdav.minutes": "минут", "webdav.restore.button": "Восстановление с WebDAV", "webdav.title": "WebDAV", "webdav.user": "Пользователь WebDAV" diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index c3c7fbdf..7de42e6d 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -222,6 +222,7 @@ "assistant.added.content": "智能体添加成功", "backup.failed": "备份失败", "backup.success": "备份成功", + "backup.start.success": "开始备份", "chat.completion.paused": "会话已停止", "copied": "已复制", "error.enter.api.host": "请输入您的 API 地址", @@ -363,6 +364,8 @@ "webdav.password": "WebDAV 密码", "webdav.path": "WebDAV 路径", "webdav.path.placeholder": "/backup", + "webdav.autoSync": "自动同步", + "webdav.minutes": "分钟", "webdav.restore.button": "从 WebDAV 恢复", "webdav.title": "WebDAV", "webdav.user": "WebDAV 用户名" @@ -513,4 +516,4 @@ "visualization": "可视化" } } -} +} \ No newline at end of file diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index 097b267e..bd04a4ed 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -222,6 +222,7 @@ "assistant.added.content": "智能體添加成功", "backup.failed": "備份失敗", "backup.success": "備份成功", + "backup.start.success": "開始備份", "chat.completion.paused": "聊天完成已暫停", "copied": "已複製", "error.enter.api.host": "請先輸入您的 API 主機地址", @@ -363,6 +364,8 @@ "webdav.password": "WebDAV 密碼", "webdav.path": "WebDAV Path", "webdav.path.placeholder": "/backup", + "webdav.autoSync": "自動同步", + "webdav.minutes": "分鐘", "webdav.restore.button": "從 WebDAV 恢復", "webdav.title": "WebDAV", "webdav.user": "WebDAV 使用者名稱" diff --git a/src/renderer/src/pages/settings/DataSettings/WebDavSettings.tsx b/src/renderer/src/pages/settings/DataSettings/WebDavSettings.tsx index e0fbad4e..89a58edf 100644 --- a/src/renderer/src/pages/settings/DataSettings/WebDavSettings.tsx +++ b/src/renderer/src/pages/settings/DataSettings/WebDavSettings.tsx @@ -1,15 +1,17 @@ import { FolderOpenOutlined, SaveOutlined } from '@ant-design/icons' import { HStack } from '@renderer/components/Layout' import { useSettings } from '@renderer/hooks/useSettings' -import { backupToWebdav, restoreFromWebdav } from '@renderer/services/BackupService' +import { backupToWebdav, restoreFromWebdav, startAutoSync, stopAutoSync } from '@renderer/services/BackupService' import { useAppDispatch } from '@renderer/store' import { + setWebdavAutoSync, setWebdavHost as _setWebdavHost, setWebdavPass as _setWebdavPass, setWebdavPath as _setWebdavPath, + setWebdavSyncInterval as _setWebdavSyncInterval, setWebdavUser as _setWebdavUser } from '@renderer/store/settings' -import { Button, Input } from 'antd' +import { Button, Input, Select, Switch } from 'antd' import { FC, useState } from 'react' import { useTranslation } from 'react-i18next' @@ -20,7 +22,9 @@ const WebDavSettings: FC = () => { webdavHost: webDAVHost, webdavUser: webDAVUser, webdavPass: webDAVPass, - webdavPath: webDAVPath + webdavPath: webDAVPath, + webdavAutoSync: webDAVAutoSync, + webdavSyncInterval: webDAVSyncInterval } = useSettings() const [webdavHost, setWebdavHost] = useState(webDAVHost) @@ -28,6 +32,9 @@ const WebDavSettings: FC = () => { const [webdavPass, setWebdavPass] = useState(webDAVPass) const [webdavPath, setWebdavPath] = useState(webDAVPath) + const [autoSync, setAutoSync] = useState(webDAVAutoSync) + const [syncInterval, setSyncInterval] = useState(webDAVSyncInterval) + const [backuping, setBackuping] = useState(false) const [restoring, setRestoring] = useState(false) @@ -56,6 +63,19 @@ const WebDavSettings: FC = () => { await restoreFromWebdav() setRestoring(false) } + const onToggleAutoSync = (checked: boolean) => { + dispatch(setWebdavAutoSync(checked)) + + if (checked) { + startAutoSync() + } else { + stopAutoSync() + } + } + const onSyncIntervalChange = (value: number) => { + setSyncInterval(value) + dispatch(_setWebdavSyncInterval(value)) + } return ( <> @@ -106,6 +126,32 @@ const WebDavSettings: FC = () => { /> + + {t('settings.data.webdav.autoSync')} + + { + setAutoSync(checked) + onToggleAutoSync(checked) + }} + disabled={!webdavHost} + /> + + + + {t('settings.general.backup.title')} diff --git a/src/renderer/src/services/BackupService.ts b/src/renderer/src/services/BackupService.ts index ba2ab8cc..a037dc0f 100644 --- a/src/renderer/src/services/BackupService.ts +++ b/src/renderer/src/services/BackupService.ts @@ -108,6 +108,49 @@ export async function restoreFromWebdav() { } } +let syncInterval: NodeJS.Timeout | null = null +export function startAutoSync() { + const { webdavAutoSync, webdavHost, webdavSyncInterval } = store.getState().settings + + if (syncInterval) { + stopAutoSync() + } + + if (webdavAutoSync && webdavHost) { + console.log('[AutoSync] Starting auto sync with interval:', webdavSyncInterval, 'minutes') + + const performBackup = async () => { + try { + console.log('[AutoSync] Performing backup...') + await backupToWebdav() + window.message.success({ + content: i18n.t('message.backup.start.success'), + key: 'webdav-sync' + }) + } catch (error) { + console.error('[AutoSync] Backup failed:', error) + window.message.error({ + content: i18n.t('message.backup.failed'), + key: 'webdav-sync' + }) + } + } + + performBackup() + + syncInterval = setInterval(performBackup, webdavSyncInterval * 60 * 1000) + console.log('[AutoSync] Sync interval set up:', syncInterval) + } +} + +export function stopAutoSync() { + if (syncInterval) { + console.log('[AutoSync] Stopping auto sync') + clearInterval(syncInterval) + syncInterval = null + } +} + async function getBackupData() { return JSON.stringify({ time: new Date().getTime(), diff --git a/src/renderer/src/store/settings.ts b/src/renderer/src/store/settings.ts index e22120ad..1c72dc9f 100644 --- a/src/renderer/src/store/settings.ts +++ b/src/renderer/src/store/settings.ts @@ -36,6 +36,8 @@ export interface SettingsState { webdavUser: string webdavPass: string webdavPath: string + webdavAutoSync: boolean + webdavSyncInterval: number translateModelPrompt: string autoTranslateWithSpace: boolean enableTopicNaming: boolean @@ -75,6 +77,8 @@ const initialState: SettingsState = { webdavUser: '', webdavPass: '', webdavPath: '/cherry-studio', + webdavAutoSync: false, + webdavSyncInterval: 5, translateModelPrompt: TRANSLATE_PROMPT, autoTranslateWithSpace: false, enableTopicNaming: true, @@ -165,6 +169,12 @@ const settingsSlice = createSlice({ setWebdavPath: (state, action: PayloadAction) => { state.webdavPath = action.payload }, + setWebdavAutoSync: (state, action: PayloadAction) => { + state.webdavAutoSync = action.payload + }, + setWebdavSyncInterval: (state, action: PayloadAction) => { + state.webdavSyncInterval = action.payload + }, setCodeShowLineNumbers: (state, action: PayloadAction) => { state.codeShowLineNumbers = action.payload }, @@ -228,6 +238,8 @@ export const { setWebdavUser, setWebdavPass, setWebdavPath, + setWebdavAutoSync, + setWebdavSyncInterval, setCodeShowLineNumbers, setCodeCollapsible, setMathEngine,