feat: add auto-import toggle to restore latest backup after rclone pull

Adds autoImportEnabled/autoImportHour to rclone config. A separate
hourly cron finds the latest .zip in backups/ and restores it to the
database (drop schema, psql restore, apply migrations). Frontend shows
toggle + time picker + Import Now button in the Receiver PC section.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
ff
2026-06-24 23:45:15 -04:00
co-authored by Claude Sonnet 4.6
parent 70b5e2ba47
commit cbe7d13dd2
5 changed files with 288 additions and 1 deletions
+17 -1
View File
@@ -17,6 +17,7 @@ import {
import { runNetworkSync, runNetworkFilesSync } from "../services/networkSyncService";
import { readRcloneConfig, writeRcloneConfig } from "../services/rcloneConfigService";
import { checkRcloneInstalled, isServerRunning, startWebDavServer, stopWebDavServer, runRclonePull } from "../services/rcloneService";
import { importLatestBackup } from "../services/autoImportService";
const UPLOADS_DIR = path.join(process.cwd(), "uploads");
@@ -618,7 +619,7 @@ router.get("/rclone-config", (req, res) => {
router.put("/rclone-config", (req: Request, res: Response): any => {
if (!req.user?.id) return res.status(401).json({ error: "Unauthorized" });
const { serverEnabled, serverPort, receiverEnabled, receiverSyncHour, sourceIp, sourcePort } = req.body;
const { serverEnabled, serverPort, receiverEnabled, receiverSyncHour, sourceIp, sourcePort, autoImportEnabled, autoImportHour } = req.body;
const patch: any = {};
if (typeof serverEnabled === "boolean") patch.serverEnabled = serverEnabled;
if (typeof serverPort === "number") patch.serverPort = serverPort;
@@ -626,6 +627,8 @@ router.put("/rclone-config", (req: Request, res: Response): any => {
if (typeof receiverSyncHour === "number") patch.receiverSyncHour = receiverSyncHour;
if (typeof sourceIp === "string") patch.sourceIp = sourceIp.trim();
if (typeof sourcePort === "number") patch.sourcePort = sourcePort;
if (typeof autoImportEnabled === "boolean") patch.autoImportEnabled = autoImportEnabled;
if (typeof autoImportHour === "number") patch.autoImportHour = autoImportHour;
const updated = writeRcloneConfig(patch);
res.json(updated);
});
@@ -666,4 +669,17 @@ router.post("/rclone-pull-now", async (req: Request, res: Response): Promise<any
}
});
router.post("/auto-import-now", async (req: Request, res: Response): Promise<any> => {
if (!req.user?.id) return res.status(401).json({ error: "Unauthorized" });
try {
await importLatestBackup();
writeRcloneConfig({ lastImportAt: new Date().toISOString(), lastImportStatus: "success", lastImportError: null });
res.json({ success: true, importedAt: new Date() });
} catch (err: any) {
writeRcloneConfig({ lastImportAt: new Date().toISOString(), lastImportStatus: "failed", lastImportError: err.message });
res.status(500).json({ error: "Auto-import failed", details: err.message });
}
});
export default router;