From 2395e14382f20b5267f6e3f88357f16755c61164 Mon Sep 17 00:00:00 2001 From: ARUNAVO RAY Date: Thu, 26 Feb 2026 10:13:13 +0530 Subject: [PATCH] Add pre-sync snapshot protection for mirror rewrites (#190) * add pre-sync snapshot protection * stabilize test module mocks * fix cross-test gitea mock exports * fix gitea mock strategy behavior --- src/components/config/ConfigTabs.tsx | 4 + src/components/config/GiteaConfigForm.tsx | 81 ++++++++++- src/lib/db/schema.ts | 4 + src/lib/gitea-enhanced.test.ts | 147 ++++++++++++++++++- src/lib/gitea-enhanced.ts | 60 ++++++++ src/lib/gitea-starred-repos.test.ts | 15 +- src/lib/gitea.test.ts | 50 ++++++- src/lib/repo-backup.ts | 164 ++++++++++++++++++++++ src/lib/utils/config-defaults.ts | 4 + src/lib/utils/config-mapper.ts | 8 ++ src/pages/api/job/mirror-repo.test.ts | 13 +- src/types/config.ts | 4 + 12 files changed, 546 insertions(+), 8 deletions(-) create mode 100644 src/lib/repo-backup.ts diff --git a/src/components/config/ConfigTabs.tsx b/src/components/config/ConfigTabs.tsx index 0af2ea2..f64ad3f 100644 --- a/src/components/config/ConfigTabs.tsx +++ b/src/components/config/ConfigTabs.tsx @@ -50,6 +50,10 @@ export function ConfigTabs() { starredReposOrg: 'starred', starredReposMode: 'dedicated-org', preserveOrgStructure: false, + backupBeforeSync: true, + backupRetentionCount: 20, + backupDirectory: 'data/repo-backups', + blockSyncOnBackupFailure: true, }, scheduleConfig: { enabled: false, // Don't set defaults here - will be loaded from API diff --git a/src/components/config/GiteaConfigForm.tsx b/src/components/config/GiteaConfigForm.tsx index c1a13f3..342b1ba 100644 --- a/src/components/config/GiteaConfigForm.tsx +++ b/src/components/config/GiteaConfigForm.tsx @@ -100,9 +100,16 @@ export function GiteaConfigForm({ config, setConfig, onAutoSave, isAutoSaving, g ); } + const normalizedValue = + type === "checkbox" + ? checked + : name === "backupRetentionCount" + ? Math.max(1, Number.parseInt(value, 10) || 20) + : value; + const newConfig = { ...config, - [name]: type === "checkbox" ? checked : value, + [name]: normalizedValue, }; setConfig(newConfig); @@ -286,7 +293,77 @@ export function GiteaConfigForm({ config, setConfig, onAutoSave, isAutoSaving, g if (onAutoSave) onAutoSave(newConfig); }} /> - + + + +
+

Destructive Update Protection

+ + + {config.backupBeforeSync && ( +
+
+ + +
+
+ + +
+
+ )} + + +
+ {/* Mobile: Show button at bottom */}