Skip to content

Commit 00fb7be

Browse files
committed
feat: enhance backup and restore functionality with improved error handling and compatibility
1 parent d98d862 commit 00fb7be

4 files changed

Lines changed: 45 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
9393
- **Database encryption disable flow** — fixed a bug where disabling encryption and restarting could corrupt or overwrite the latest plaintext with a stale encrypted backup. Disabling now preserves the current runtime plaintext and removes encrypted artifacts after verifying the plaintext is valid.
9494
- **Database open retry on restart** — added a short retry loop when opening the profile database during startup to avoid "localhost refused connection" / startup failures caused by Windows file-lock races after `app.restart()`.
9595
- **VS Code project name fallback** — when `project_name` is empty (for example, in detailed tracking where only the folder path was recorded), the Dashboard today overview and VS Code Insights project ranking now derive the display name from the opened folder path. If no folder information is available, they show a localized "Unknown project" label instead of "No data".
96+
- **Tauri dialog permissions** — added `dialog:allow-confirm`, `dialog:allow-ask`, and `dialog:allow-message` to the default capability so that `@tauri-apps/plugin-dialog` confirmations and messages no longer fail with "dialog.confirm not allowed" or "dialog.message not allowed".
97+
- **Legacy v2 backup compatibility** — older v2 backup packages created before the v2.0.0 schema expansion (which added app categories, usage goals, focus rules, VS Code sessions, API tokens, etc.) now deserialize safely. Missing tables default to empty, so validate/restore no longer fails with a generic "backup action failed" message.
98+
- **Backup error messages** — improved error extraction in the Backup & Restore UI so that backend failure details are shown instead of falling back to the generic "backup action failed" translation whenever the thrown error is not a standard `Error` instance.
9699

97100
### Security
98101

src-tauri/capabilities/default.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@
3535
"shell:allow-open",
3636
"updater:default",
3737
"dialog:allow-open",
38-
"dialog:allow-save"
38+
"dialog:allow-save",
39+
"dialog:allow-confirm",
40+
"dialog:allow-ask",
41+
"dialog:allow-message"
3942
]
4043
}

src-tauri/src/commands/data_reliability_cmd.rs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ const BACKUP_VERSION: &str = "v2";
2323
const BACKUP_PACKAGE_FILE: &str = "backup.json";
2424
const BACKUP_MANIFEST_FILE: &str = "manifest.json";
2525

26-
#[derive(Debug, Clone, Serialize, Deserialize)]
26+
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
27+
#[serde(default)]
2728
pub struct BackupBundleCounts {
2829
pub app_usage: usize,
2930
pub browser_sessions: usize,
@@ -52,6 +53,7 @@ pub struct BackupManifest {
5253
pub locale: String,
5354
pub created_at: String,
5455
pub checksum: String,
56+
#[serde(default)]
5557
pub counts: BackupBundleCounts,
5658
#[serde(default)]
5759
pub encrypted: bool,
@@ -72,6 +74,7 @@ struct SettingEntry {
7274
}
7375

7476
#[derive(Debug, Clone, Serialize, Deserialize)]
77+
#[serde(default)]
7578
struct BackupBundle {
7679
app_usage: Vec<AppUsageRecord>,
7780
browser_sessions: Vec<BrowserSession>,
@@ -92,6 +95,30 @@ struct BackupBundle {
9295
api_client_allowlist: Vec<ApiClientAllowlistEntry>,
9396
}
9497

98+
impl Default for BackupBundle {
99+
fn default() -> Self {
100+
Self {
101+
app_usage: Vec::new(),
102+
browser_sessions: Vec::new(),
103+
todos: Vec::new(),
104+
widget_configs: Vec::new(),
105+
ignored_apps: Vec::new(),
106+
app_settings: Vec::new(),
107+
app_categories: Vec::new(),
108+
usage_goals: Vec::new(),
109+
focus_sessions: Vec::new(),
110+
focus_rules: Vec::new(),
111+
browser_ignored_domains: Vec::new(),
112+
browser_domain_limits: Vec::new(),
113+
widget_permissions: Vec::new(),
114+
widget_permission_audit_log: Vec::new(),
115+
vscode_sessions: Vec::new(),
116+
api_tokens: Vec::new(),
117+
api_client_allowlist: Vec::new(),
118+
}
119+
}
120+
}
121+
95122
#[derive(Debug, Clone, Serialize, Deserialize)]
96123
struct WidgetPermissionBackupEntry {
97124
widget_id: String,

src/pages/Settings/BackupSection.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,19 @@ export default function BackupSection() {
6868
};
6969

7070
const handleBackupError = (error: unknown) => {
71-
const text = error instanceof Error ? error.message : t("backup.failed");
71+
const extractText = (err: unknown): string => {
72+
if (err instanceof Error) return err.message;
73+
if (typeof err === "string") return err;
74+
if (err && typeof err === "object" && "message" in err && typeof (err as { message?: unknown }).message === "string") {
75+
return (err as { message: string }).message;
76+
}
77+
return t("backup.failed");
78+
};
79+
const text = extractText(error);
7280
if (text.toLowerCase().includes("passphrase")) {
7381
setBackupNeedsPassphrase(true);
7482
}
75-
setBackupMessage({ type: "error", text });
83+
setBackupMessage({ type: "error", text: text || t("backup.failed") });
7684
};
7785

7886
const clearBackupSelection = () => {

0 commit comments

Comments
 (0)