feat: integrate rclone WebDAV backup for PC-to-PC file sync
Source PC serves backups/ folder via rclone WebDAV server (auto-starts with app). Receiver PC pulls backups on schedule using rclone sync. Network Backup UI now has two tabs: Rclone and API Key. Rclone installed automatically via postinstall script. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,8 @@ import { backupDatabaseToPath } from "../services/databaseBackupService";
|
||||
import { cronJobLogStorage } from "../storage/cron-job-log-storage";
|
||||
import { readSyncConfig, writeSyncConfig } from "../services/networkSyncConfigService";
|
||||
import { runNetworkSync, runNetworkFilesSync } from "../services/networkSyncService";
|
||||
import { readRcloneConfig, writeRcloneConfig } from "../services/rcloneConfigService";
|
||||
import { runRclonePull } from "../services/rcloneService";
|
||||
|
||||
// Local backup folder in the app root (apps/Backend/backups)
|
||||
const LOCAL_BACKUP_DIR = path.resolve(process.cwd(), "backups");
|
||||
@@ -174,6 +176,42 @@ export const startBackupCron = () => {
|
||||
console.log("✅ [9 PM] USB backup complete.");
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// Every hour — Rclone backup (runs only when hour matches config)
|
||||
// ============================================================
|
||||
cron.schedule("0 * * * *", async () => {
|
||||
const rcloneConfig = readRcloneConfig();
|
||||
if (!rcloneConfig.receiverEnabled || !rcloneConfig.sourceIp) return;
|
||||
|
||||
const currentHour = new Date().getHours();
|
||||
if (currentHour !== rcloneConfig.receiverSyncHour) return;
|
||||
|
||||
console.log(`[${rcloneConfig.receiverSyncHour}:00] Running rclone pull from ${rcloneConfig.sourceIp}...`);
|
||||
|
||||
const admin = await getAdminUser();
|
||||
const startedAt = new Date();
|
||||
const log = await cronJobLogStorage.createJobLog("rclone-backup", startedAt);
|
||||
|
||||
try {
|
||||
await runRclonePull();
|
||||
writeRcloneConfig({ lastSyncAt: new Date().toISOString(), lastSyncStatus: "success", lastSyncError: null });
|
||||
await cronJobLogStorage.completeJobLog(log.id, "success", new Date());
|
||||
console.log(`Rclone backup complete.`);
|
||||
} catch (err) {
|
||||
const errorMessage = err instanceof Error ? err.message : String(err);
|
||||
console.error("Rclone backup failed:", err);
|
||||
writeRcloneConfig({ lastSyncAt: new Date().toISOString(), lastSyncStatus: "failed", lastSyncError: errorMessage });
|
||||
await cronJobLogStorage.completeJobLog(log.id, "failed", new Date(), errorMessage);
|
||||
if (admin) {
|
||||
await storage.createNotification(
|
||||
admin.id,
|
||||
"BACKUP",
|
||||
`Rclone backup failed: ${errorMessage}`
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// ============================================================
|
||||
// Every hour — Network sync (runs only when hour matches config)
|
||||
// ============================================================
|
||||
|
||||
Reference in New Issue
Block a user