Added the backup feature

This commit is contained in:
2025-07-17 00:12:00 +08:00
parent b4b0ad9bba
commit 9fff7bcfca
39 changed files with 1876 additions and 1018 deletions

View File

@@ -0,0 +1,125 @@
import {defineStore} from 'pinia'
import {computed, readonly, ref} from 'vue'
import type {GitBackupConfig} from '@/../bindings/voidraft/internal/models'
import {BackupService} from '@/../bindings/voidraft/internal/services'
import {useConfigStore} from '@/stores/configStore'
/**
* Minimalist Backup Store
*/
export const useBackupStore = defineStore('backup', () => {
// Core state
const config = ref<GitBackupConfig | null>(null)
const isPushing = ref(false)
const error = ref<string | null>(null)
const isInitialized = ref(false)
// Backup result states
const pushSuccess = ref(false)
const pushError = ref(false)
// Timers for auto-hiding status icons and error messages
let pushStatusTimer: number | null = null
let errorTimer: number | null = null
// 获取configStore
const configStore = useConfigStore()
// Computed properties
const isEnabled = computed(() => configStore.config.backup.enabled)
const isConfigured = computed(() => configStore.config.backup.repo_url)
// 清除状态显示
const clearPushStatus = () => {
if (pushStatusTimer !== null) {
window.clearTimeout(pushStatusTimer)
pushStatusTimer = null
}
pushSuccess.value = false
pushError.value = false
}
// 清除错误信息
const clearError = () => {
if (errorTimer !== null) {
window.clearTimeout(errorTimer)
errorTimer = null
}
error.value = null
}
// 设置错误信息并自动清除
const setErrorWithAutoHide = (errorMessage: string, hideAfter: number = 5000) => {
clearError() // 清除之前的错误定时器
error.value = errorMessage
errorTimer = window.setTimeout(() => {
error.value = null
errorTimer = null
}, hideAfter)
}
// Push to remote repository
const pushToRemote = async () => {
if (isPushing.value || !isConfigured.value) return
isPushing.value = true
clearError() // 清除之前的错误信息
clearPushStatus()
try {
await BackupService.PushToRemote()
// 显示成功状态并设置3秒后自动消失
pushSuccess.value = true
pushStatusTimer = window.setTimeout(() => {
pushSuccess.value = false
pushStatusTimer = null
}, 3000)
} catch (err: any) {
setErrorWithAutoHide(err?.message || 'Backup operation failed')
// 显示错误状态并设置3秒后自动消失
pushError.value = true
pushStatusTimer = window.setTimeout(() => {
pushError.value = false
pushStatusTimer = null
}, 3000)
} finally {
isPushing.value = false
}
}
// 初始化备份服务(只在应用启动时调用一次)
const initialize = async () => {
if (!isEnabled.value) return
// 避免重复初始化
if (isInitialized.value) return
clearError() // 清除之前的错误信息
try {
await BackupService.Initialize()
isInitialized.value = true
} catch (err: any) {
setErrorWithAutoHide(err?.message || 'Failed to initialize backup service')
}
}
return {
// State
config: readonly(config),
isPushing: readonly(isPushing),
error: readonly(error),
isInitialized: readonly(isInitialized),
pushSuccess: readonly(pushSuccess),
pushError: readonly(pushError),
// Computed
isEnabled,
isConfigured,
// Methods
pushToRemote,
initialize,
clearError
}
})

View File

@@ -11,14 +11,14 @@ import {
TabType,
UpdatesConfig,
UpdateSourceType,
GitSyncConfig,
AuthMethod,
SyncStrategy
GitBackupConfig,
AuthMethod
} from '@/../bindings/voidraft/internal/models/models';
import {useI18n} from 'vue-i18n';
import {ConfigUtils} from '@/utils/configUtils';
import {WindowController} from '@/utils/windowController';
import * as runtime from '@wailsio/runtime';
import {useBackupStore} from '@/stores/backupStore';
// 国际化相关导入
export type SupportedLocaleType = 'zh-CN' | 'en-US';
@@ -51,8 +51,8 @@ type UpdatesConfigKeyMap = {
readonly [K in keyof UpdatesConfig]: string;
};
type SyncConfigKeyMap = {
readonly [K in keyof GitSyncConfig]: string;
type BackupConfigKeyMap = {
readonly [K in keyof GitBackupConfig]: string;
};
type NumberConfigKey = 'fontSize' | 'tabSize' | 'lineHeight';
@@ -95,22 +95,18 @@ const UPDATES_CONFIG_KEY_MAP: UpdatesConfigKeyMap = {
gitea: 'updates.gitea'
} as const;
const SYNC_CONFIG_KEY_MAP: SyncConfigKeyMap = {
enabled: 'sync.enabled',
repo_url: 'sync.repo_url',
branch: 'sync.branch',
auth_method: 'sync.auth_method',
username: 'sync.username',
password: 'sync.password',
token: 'sync.token',
ssh_key_path: 'sync.ssh_key_path',
ssh_key_passphrase: 'sync.ssh_key_passphrase',
sync_interval: 'sync.sync_interval',
last_sync_time: 'sync.last_sync_time',
auto_sync: 'sync.auto_sync',
local_repo_path: 'sync.local_repo_path',
sync_strategy: 'sync.sync_strategy',
files_to_sync: 'sync.files_to_sync'
const BACKUP_CONFIG_KEY_MAP: BackupConfigKeyMap = {
enabled: 'backup.enabled',
repo_url: 'backup.repo_url',
auth_method: 'backup.auth_method',
username: 'backup.username',
password: 'backup.password',
token: 'backup.token',
ssh_key_path: 'backup.ssh_key_path',
ssh_key_passphrase: 'backup.ssh_key_passphrase',
backup_interval: 'backup.backup_interval',
auto_backup: 'backup.auto_backup',
} as const;
// 配置限制
@@ -286,22 +282,17 @@ const DEFAULT_CONFIG: AppConfig = {
repo: "voidraft",
}
},
sync: {
backup: {
enabled: false,
repo_url: "",
branch: "main",
auth_method: AuthMethod.Token,
auth_method: AuthMethod.UserPass,
username: "",
password: "",
token: "",
ssh_key_path: "",
ssh_key_passphrase: "",
sync_interval: 60,
last_sync_time: null,
auto_sync: true,
local_repo_path: "",
sync_strategy: SyncStrategy.LocalFirst,
files_to_sync: ["voidraft.db"]
backup_interval: 60,
auto_backup: true,
},
metadata: {
version: '1.0.0',
@@ -390,19 +381,19 @@ export const useConfigStore = defineStore('config', () => {
state.config.updates[key] = value;
};
const updateSyncConfig = async <K extends keyof GitSyncConfig>(key: K, value: GitSyncConfig[K]): Promise<void> => {
const updateBackupConfig = async <K extends keyof GitBackupConfig>(key: K, value: GitBackupConfig[K]): Promise<void> => {
// 确保配置已加载
if (!state.configLoaded && !state.isLoading) {
await initConfig();
}
const backendKey = SYNC_CONFIG_KEY_MAP[key];
const backendKey = BACKUP_CONFIG_KEY_MAP[key];
if (!backendKey) {
throw new Error(`No backend key mapping found for sync.${key.toString()}`);
throw new Error(`No backend key mapping found for backup.${key.toString()}`);
}
await ConfigService.Set(backendKey, value);
state.config.sync[key] = value;
state.config.backup[key] = value;
};
// 加载配置
@@ -419,7 +410,7 @@ export const useConfigStore = defineStore('config', () => {
if (appConfig.editing) Object.assign(state.config.editing, appConfig.editing);
if (appConfig.appearance) Object.assign(state.config.appearance, appConfig.appearance);
if (appConfig.updates) Object.assign(state.config.updates, appConfig.updates);
if (appConfig.sync) Object.assign(state.config.sync, appConfig.sync);
if (appConfig.backup) Object.assign(state.config.backup, appConfig.backup);
if (appConfig.metadata) Object.assign(state.config.metadata, appConfig.metadata);
}
@@ -654,18 +645,16 @@ export const useConfigStore = defineStore('config', () => {
// 更新配置相关方法
setAutoUpdate: async (value: boolean) => await updateUpdatesConfig('autoUpdate', value),
// Git同步配置相关方法
setGitSyncEnabled: (value: boolean) => updateSyncConfig('enabled', value),
setGitRepoUrl: (value: string) => updateSyncConfig('repo_url', value),
setGitBranch: (value: string) => updateSyncConfig('branch', value),
setAuthMethod: (value: AuthMethod) => updateSyncConfig('auth_method', value),
setGitUsername: (value: string) => updateSyncConfig('username', value),
setGitPassword: (value: string) => updateSyncConfig('password', value),
setGitToken: (value: string) => updateSyncConfig('token', value),
setSSHKeyPath: (value: string) => updateSyncConfig('ssh_key_path', value),
setSyncInterval: (value: number) => updateSyncConfig('sync_interval', value),
setAutoSync: (value: boolean) => updateSyncConfig('auto_sync', value),
setSyncStrategy: (value: SyncStrategy) => updateSyncConfig('sync_strategy', value),
setFilesToSync: (value: string[]) => updateSyncConfig('files_to_sync', value),
// 备份配置相关方法
setEnableBackup: async (value: boolean) => {await updateBackupConfig('enabled', value);},
setAutoBackup: async (value: boolean) => {await updateBackupConfig('auto_backup', value);},
setRepoUrl: async (value: string) => await updateBackupConfig('repo_url', value),
setAuthMethod: async (value: AuthMethod) => await updateBackupConfig('auth_method', value),
setUsername: async (value: string) => await updateBackupConfig('username', value),
setPassword: async (value: string) => await updateBackupConfig('password', value),
setToken: async (value: string) => await updateBackupConfig('token', value),
setSshKeyPath: async (value: string) => await updateBackupConfig('ssh_key_path', value),
setSshKeyPassphrase: async (value: string) => await updateBackupConfig('ssh_key_passphrase', value),
setBackupInterval: async (value: number) => await updateBackupConfig('backup_interval', value),
};
});