diff --git a/apps/Backend/src/cron/backupCheck.ts b/apps/Backend/src/cron/backupCheck.ts
index 0bc4f030..989aa203 100755
--- a/apps/Backend/src/cron/backupCheck.ts
+++ b/apps/Backend/src/cron/backupCheck.ts
@@ -55,26 +55,19 @@ async function getAdminUser() {
export const startBackupCron = () => {
// ============================================================
- // 8 PM — Local automatic backup to apps/Backend/backups/
+ // Every hour — Local automatic backup (runs when hour matches setting)
// ============================================================
- cron.schedule("0 20 * * *", async () => {
- console.log("🔄 [8 PM] Running local auto-backup...");
- ensureLocalBackupDir();
-
+ cron.schedule("0 * * * *", async () => {
const admin = await getAdminUser();
- if (!admin) {
- console.warn("No admin user found, skipping local backup.");
- return;
- }
+ if (!admin) return;
+ if (!admin.autoBackupEnabled) return;
- if (!admin.autoBackupEnabled) {
- console.log("✅ [8 PM] Auto-backup is disabled for admin, skipped.");
- const startedAt = new Date();
- const log = await cronJobLogStorage.createJobLog("local-backup", startedAt);
- await cronJobLogStorage.completeJobLog(log.id, "skipped", new Date());
- await storage.deleteNotificationsByType(admin.id, "BACKUP");
- return;
- }
+ const currentHour = new Date().getHours();
+ const backupHour = admin.autoBackupHour ?? 20;
+ if (currentHour !== backupHour) return;
+
+ console.log(`🔄 [${backupHour}:00] Running local auto-backup...`);
+ ensureLocalBackupDir();
const startedAt = new Date();
const log = await cronJobLogStorage.createJobLog("local-backup", startedAt);
@@ -97,30 +90,21 @@ export const startBackupCron = () => {
"❌ Automatic backup failed. Please check the server backup folder."
);
}
-
- console.log("✅ [8 PM] Local backup complete.");
});
// ============================================================
- // 9 PM — USB backup to the "USB Backup" folder on the drive
+ // Every hour — USB backup (runs when hour matches setting)
// ============================================================
- cron.schedule("0 21 * * *", async () => {
- console.log("🔄 [9 PM] Running USB backup...");
-
+ cron.schedule("0 * * * *", async () => {
const admin = await getAdminUser();
- if (!admin) {
- console.warn("No admin user found, skipping USB backup.");
- return;
- }
+ if (!admin) return;
+ if (!admin.usbBackupEnabled) return;
- if (!admin.usbBackupEnabled) {
- console.log("✅ [9 PM] USB backup is disabled for admin, skipped.");
- const startedAt = new Date();
- const log = await cronJobLogStorage.createJobLog("usb-backup", startedAt);
- await cronJobLogStorage.completeJobLog(log.id, "skipped", new Date());
- await storage.deleteNotificationsByType(admin.id, "BACKUP");
- return;
- }
+ const currentHour = new Date().getHours();
+ const backupHour = admin.usbBackupHour ?? 21;
+ if (currentHour !== backupHour) return;
+
+ console.log(`🔄 [${backupHour}:00] Running USB backup...`);
const startedAt = new Date();
const log = await cronJobLogStorage.createJobLog("usb-backup", startedAt);
@@ -172,8 +156,6 @@ export const startBackupCron = () => {
"❌ USB backup failed. Please check the USB drive and try again."
);
}
-
- console.log("✅ [9 PM] USB backup complete.");
});
// ============================================================
diff --git a/apps/Backend/src/routes/database-management.ts b/apps/Backend/src/routes/database-management.ts
index a29458bb..472d35ba 100755
--- a/apps/Backend/src/routes/database-management.ts
+++ b/apps/Backend/src/routes/database-management.ts
@@ -308,7 +308,7 @@ router.get("/usb-backup-setting", async (req, res) => {
const user = await storage.getUser(userId);
if (!user) return res.status(404).json({ error: "User not found" });
- res.json({ usbBackupEnabled: user.usbBackupEnabled });
+ res.json({ usbBackupEnabled: user.usbBackupEnabled, usbBackupHour: user.usbBackupHour ?? 21 });
});
// PUT usb backup setting
@@ -316,15 +316,15 @@ router.put("/usb-backup-setting", async (req, res) => {
const userId = req.user?.id;
if (!userId) return res.status(401).json({ error: "Unauthorized" });
- const { usbBackupEnabled } = req.body;
- if (typeof usbBackupEnabled !== "boolean") {
- return res.status(400).json({ error: "usbBackupEnabled must be a boolean" });
- }
+ const { usbBackupEnabled, usbBackupHour } = req.body;
+ const patch: any = {};
+ if (typeof usbBackupEnabled === "boolean") patch.usbBackupEnabled = usbBackupEnabled;
+ if (typeof usbBackupHour === "number") patch.usbBackupHour = usbBackupHour;
- const updated = await storage.updateUser(userId, { usbBackupEnabled });
+ const updated = await storage.updateUser(userId, patch);
if (!updated) return res.status(404).json({ error: "User not found" });
- res.json({ usbBackupEnabled: updated.usbBackupEnabled });
+ res.json({ usbBackupEnabled: updated.usbBackupEnabled, usbBackupHour: updated.usbBackupHour ?? 21 });
});
// GET auto backup setting
@@ -335,7 +335,7 @@ router.get("/auto-backup-setting", async (req, res) => {
const user = await storage.getUser(userId);
if (!user) return res.status(404).json({ error: "User not found" });
- res.json({ autoBackupEnabled: user.autoBackupEnabled });
+ res.json({ autoBackupEnabled: user.autoBackupEnabled, autoBackupHour: user.autoBackupHour ?? 20 });
});
// PUT auto backup setting
@@ -343,15 +343,15 @@ router.put("/auto-backup-setting", async (req, res) => {
const userId = req.user?.id;
if (!userId) return res.status(401).json({ error: "Unauthorized" });
- const { autoBackupEnabled } = req.body;
- if (typeof autoBackupEnabled !== "boolean") {
- return res.status(400).json({ error: "autoBackupEnabled must be a boolean" });
- }
+ const { autoBackupEnabled, autoBackupHour } = req.body;
+ const patch: any = {};
+ if (typeof autoBackupEnabled === "boolean") patch.autoBackupEnabled = autoBackupEnabled;
+ if (typeof autoBackupHour === "number") patch.autoBackupHour = autoBackupHour;
- const updated = await storage.updateUser(userId, { autoBackupEnabled });
+ const updated = await storage.updateUser(userId, patch);
if (!updated) return res.status(404).json({ error: "User not found" });
- res.json({ autoBackupEnabled: updated.autoBackupEnabled });
+ res.json({ autoBackupEnabled: updated.autoBackupEnabled, autoBackupHour: updated.autoBackupHour ?? 20 });
});
router.post("/backup-path", async (req, res) => {
diff --git a/apps/Frontend/src/components/database-management/backup-destination-manager.tsx b/apps/Frontend/src/components/database-management/backup-destination-manager.tsx
index bba3f3b4..6f3a3f45 100755
--- a/apps/Frontend/src/components/database-management/backup-destination-manager.tsx
+++ b/apps/Frontend/src/components/database-management/backup-destination-manager.tsx
@@ -48,20 +48,16 @@ export function BackupDestinationManager() {
});
const usbBackupEnabled = usbSettingData?.usbBackupEnabled ?? false;
+ const usbBackupHour = usbSettingData?.usbBackupHour ?? 21;
const usbToggleMutation = useMutation({
- mutationFn: async (enabled: boolean) => {
- const res = await apiRequest("PUT", "/api/database-management/usb-backup-setting", {
- usbBackupEnabled: enabled,
- });
+ mutationFn: async (patch: { usbBackupEnabled?: boolean; usbBackupHour?: number }) => {
+ const res = await apiRequest("PUT", "/api/database-management/usb-backup-setting", patch);
return res.json();
},
onSuccess: (data) => {
queryClient.setQueryData(["/db/usb-backup-setting"], data);
- toast({
- title: "Setting Saved",
- description: `USB backup ${data.usbBackupEnabled ? "enabled" : "disabled"}.`,
- });
+ toast({ title: "Setting Saved" });
},
onError: () => {
toast({
@@ -136,11 +132,11 @@ export function BackupDestinationManager() {
External Backup Destination
-
+
usbToggleMutation.mutate(checked)}
+ onCheckedChange={(checked) => usbToggleMutation.mutate({ usbBackupEnabled: checked })}
disabled={usbToggleMutation.isPending}
/>
-
- (daily at 9 PM → saves to the "USB Backup" folder on your drive)
-
+
+
+
+
Enter the root path of your USB drive below. The app will automatically back up to the{" "}
- USB Backup folder inside it every night at 9 PM when the toggle is on.
+ USB Backup folder inside it at the scheduled time when the toggle is on.