mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-24 10:40:07 +08:00
feat(backup): add single-file overwrite options for backups
This commit is contained in:
parent
13093bb821
commit
ae392eb2ef
@ -30,7 +30,9 @@ const S3Settings: FC = () => {
|
||||
root: s3RootInit = '',
|
||||
syncInterval: s3SyncIntervalInit = 0,
|
||||
maxBackups: s3MaxBackupsInit = 5,
|
||||
skipBackupFile: s3SkipBackupFileInit = false
|
||||
skipBackupFile: s3SkipBackupFileInit = false,
|
||||
singleFileOverwrite: s3SingleFileOverwriteInit = false,
|
||||
singleFileName: s3SingleFileNameInit = ''
|
||||
} = s3
|
||||
|
||||
const [endpoint, setEndpoint] = useState<string | undefined>(s3EndpointInit)
|
||||
@ -40,6 +42,8 @@ const S3Settings: FC = () => {
|
||||
const [secretAccessKey, setSecretAccessKey] = useState<string | undefined>(s3SecretAccessKeyInit)
|
||||
const [root, setRoot] = useState<string | undefined>(s3RootInit)
|
||||
const [skipBackupFile, setSkipBackupFile] = useState<boolean>(s3SkipBackupFileInit)
|
||||
const [singleFileOverwrite, setSingleFileOverwrite] = useState<boolean>(!!s3SingleFileOverwriteInit)
|
||||
const [singleFileName, setSingleFileName] = useState<string | undefined>(s3SingleFileNameInit)
|
||||
const [backupManagerVisible, setBackupManagerVisible] = useState(false)
|
||||
|
||||
const [syncInterval, setSyncInterval] = useState<number>(s3SyncIntervalInit)
|
||||
@ -81,6 +85,15 @@ const S3Settings: FC = () => {
|
||||
dispatch(setS3Partial({ skipBackupFile: value }))
|
||||
}
|
||||
|
||||
const onSingleFileOverwriteChange = (value: boolean) => {
|
||||
setSingleFileOverwrite(value)
|
||||
dispatch(setS3Partial({ singleFileOverwrite: value }))
|
||||
}
|
||||
|
||||
const onSingleFileNameBlur = () => {
|
||||
dispatch(setS3Partial({ singleFileName: singleFileName || '' }))
|
||||
}
|
||||
|
||||
const renderSyncStatus = () => {
|
||||
if (!endpoint) return null
|
||||
|
||||
|
||||
@ -168,7 +168,9 @@ export async function backupToWebdav({
|
||||
webdavPath,
|
||||
webdavMaxBackups,
|
||||
webdavSkipBackupFile,
|
||||
webdavDisableStream
|
||||
webdavDisableStream,
|
||||
webdavSingleFileOverwrite,
|
||||
webdavSingleFileName
|
||||
} = store.getState().settings
|
||||
let deviceType = 'unknown'
|
||||
let hostname = 'unknown'
|
||||
@ -179,7 +181,12 @@ export async function backupToWebdav({
|
||||
logger.error('Failed to get device type or hostname:', error as Error)
|
||||
}
|
||||
const timestamp = dayjs().format('YYYYMMDDHHmmss')
|
||||
const backupFileName = customFileName || `cherry-studio.${timestamp}.${hostname}.${deviceType}.zip`
|
||||
let backupFileName = customFileName || `cherry-studio.${timestamp}.${hostname}.${deviceType}.zip`
|
||||
// 覆盖式单文件备份(仅在自动备份流程且保留份数=1时生效)
|
||||
if (autoBackupProcess && webdavMaxBackups === 1 && webdavSingleFileOverwrite) {
|
||||
const base = (webdavSingleFileName || `cherry-studio.${hostname}.${deviceType}`).trim()
|
||||
backupFileName = base.endsWith('.zip') ? base : `${base}.zip`
|
||||
}
|
||||
const finalFileName = backupFileName.endsWith('.zip') ? backupFileName : `${backupFileName}.zip`
|
||||
const backupData = await getBackupData()
|
||||
|
||||
@ -212,8 +219,8 @@ export async function backupToWebdav({
|
||||
})
|
||||
showMessage && window.toast.success(i18n.t('message.backup.success'))
|
||||
|
||||
// 清理旧备份文件
|
||||
if (webdavMaxBackups > 0) {
|
||||
// 覆盖式单文件备份启用时(且=1),不进行清理
|
||||
if (webdavMaxBackups > 0 && !(autoBackupProcess && webdavMaxBackups === 1 && webdavSingleFileOverwrite)) {
|
||||
try {
|
||||
// 获取所有备份文件
|
||||
const files = await window.api.backup.listWebdavFiles({
|
||||
@ -353,7 +360,12 @@ export async function backupToS3({
|
||||
logger.error('Failed to get device type or hostname:', error as Error)
|
||||
}
|
||||
const timestamp = dayjs().format('YYYYMMDDHHmmss')
|
||||
const backupFileName = customFileName || `cherry-studio.${timestamp}.${hostname}.${deviceType}.zip`
|
||||
let backupFileName = customFileName || `cherry-studio.${timestamp}.${hostname}.${deviceType}.zip`
|
||||
// 覆盖式单文件备份(仅在自动备份流程且保留份数=1时生效)
|
||||
if (autoBackupProcess && s3Config.maxBackups === 1 && s3Config.singleFileOverwrite) {
|
||||
const base = (s3Config.singleFileName || `cherry-studio.${hostname}.${deviceType}`).trim()
|
||||
backupFileName = base.endsWith('.zip') ? base : `${base}.zip`
|
||||
}
|
||||
const finalFileName = backupFileName.endsWith('.zip') ? backupFileName : `${backupFileName}.zip`
|
||||
const backupData = await getBackupData()
|
||||
|
||||
|
||||
@ -119,6 +119,9 @@ export interface SettingsState {
|
||||
webdavMaxBackups: number
|
||||
webdavSkipBackupFile: boolean
|
||||
webdavDisableStream: boolean
|
||||
// 覆盖式单文件备份(WebDAV)
|
||||
webdavSingleFileOverwrite?: boolean
|
||||
webdavSingleFileName?: string
|
||||
translateModelPrompt: string
|
||||
autoTranslateWithSpace: boolean
|
||||
showTranslateConfirm: boolean
|
||||
@ -210,6 +213,9 @@ export interface SettingsState {
|
||||
localBackupSyncInterval: number
|
||||
localBackupMaxBackups: number
|
||||
localBackupSkipBackupFile: boolean
|
||||
// 覆盖式单文件备份(Local)
|
||||
localSingleFileOverwrite?: boolean
|
||||
localSingleFileName?: string
|
||||
defaultPaintingProvider: PaintingProvider
|
||||
s3: S3Config
|
||||
// Developer mode
|
||||
@ -306,6 +312,8 @@ export const initialState: SettingsState = {
|
||||
webdavMaxBackups: 0,
|
||||
webdavSkipBackupFile: false,
|
||||
webdavDisableStream: false,
|
||||
webdavSingleFileOverwrite: false,
|
||||
webdavSingleFileName: '',
|
||||
translateModelPrompt: TRANSLATE_PROMPT,
|
||||
autoTranslateWithSpace: false,
|
||||
showTranslateConfirm: true,
|
||||
@ -389,6 +397,8 @@ export const initialState: SettingsState = {
|
||||
localBackupSyncInterval: 0,
|
||||
localBackupMaxBackups: 0,
|
||||
localBackupSkipBackupFile: false,
|
||||
localSingleFileOverwrite: false,
|
||||
localSingleFileName: '',
|
||||
defaultPaintingProvider: 'zhipu',
|
||||
s3: {
|
||||
endpoint: '',
|
||||
@ -400,7 +410,9 @@ export const initialState: SettingsState = {
|
||||
autoSync: false,
|
||||
syncInterval: 0,
|
||||
maxBackups: 0,
|
||||
skipBackupFile: false
|
||||
skipBackupFile: false,
|
||||
singleFileOverwrite: false,
|
||||
singleFileName: ''
|
||||
},
|
||||
|
||||
// Developer mode
|
||||
@ -556,6 +568,12 @@ const settingsSlice = createSlice({
|
||||
setWebdavDisableStream: (state, action: PayloadAction<boolean>) => {
|
||||
state.webdavDisableStream = action.payload
|
||||
},
|
||||
setWebdavSingleFileOverwrite: (state, action: PayloadAction<boolean>) => {
|
||||
state.webdavSingleFileOverwrite = action.payload
|
||||
},
|
||||
setWebdavSingleFileName: (state, action: PayloadAction<string>) => {
|
||||
state.webdavSingleFileName = action.payload
|
||||
},
|
||||
setCodeExecution: (state, action: PayloadAction<{ enabled?: boolean; timeoutMinutes?: number }>) => {
|
||||
if (action.payload.enabled !== undefined) {
|
||||
state.codeExecution.enabled = action.payload.enabled
|
||||
@ -816,6 +834,12 @@ const settingsSlice = createSlice({
|
||||
setLocalBackupSkipBackupFile: (state, action: PayloadAction<boolean>) => {
|
||||
state.localBackupSkipBackupFile = action.payload
|
||||
},
|
||||
setLocalSingleFileOverwrite: (state, action: PayloadAction<boolean>) => {
|
||||
state.localSingleFileOverwrite = action.payload
|
||||
},
|
||||
setLocalSingleFileName: (state, action: PayloadAction<string>) => {
|
||||
state.localSingleFileName = action.payload
|
||||
},
|
||||
setDefaultPaintingProvider: (state, action: PayloadAction<PaintingProvider>) => {
|
||||
state.defaultPaintingProvider = action.payload
|
||||
},
|
||||
@ -903,6 +927,8 @@ export const {
|
||||
setWebdavMaxBackups,
|
||||
setWebdavSkipBackupFile,
|
||||
setWebdavDisableStream,
|
||||
setWebdavSingleFileOverwrite,
|
||||
setWebdavSingleFileName,
|
||||
setCodeExecution,
|
||||
setCodeEditor,
|
||||
setCodeViewer,
|
||||
@ -974,6 +1000,8 @@ export const {
|
||||
setLocalBackupSyncInterval,
|
||||
setLocalBackupMaxBackups,
|
||||
setLocalBackupSkipBackupFile,
|
||||
setLocalSingleFileOverwrite,
|
||||
setLocalSingleFileName,
|
||||
setDefaultPaintingProvider,
|
||||
setS3,
|
||||
setS3Partial,
|
||||
|
||||
@ -873,6 +873,10 @@ export type S3Config = {
|
||||
autoSync: boolean
|
||||
syncInterval: number
|
||||
maxBackups: number
|
||||
/** 当自动备份且保留份数=1时,是否启用覆盖式单文件备份 */
|
||||
singleFileOverwrite?: boolean
|
||||
/** 覆盖式单文件备份的自定义文件名(可选,默认使用不带时间戳的设备名+主机名) */
|
||||
singleFileName?: string
|
||||
}
|
||||
|
||||
export type { Message } from './newMessage'
|
||||
|
||||
Loading…
Reference in New Issue
Block a user