feat: add configurable backup time for local and USB backups
Replace hardcoded 8 PM / 9 PM backup schedules with user-selectable hour dropdowns. Adds autoBackupHour and usbBackupHour fields to User model. Cron jobs now check every hour against the configured time. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -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.");
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
Reference in New Issue
Block a user