diff --git a/apps/Backend/src/services/rcloneService.ts b/apps/Backend/src/services/rcloneService.ts index 6993c697..f5428ca9 100644 --- a/apps/Backend/src/services/rcloneService.ts +++ b/apps/Backend/src/services/rcloneService.ts @@ -1,8 +1,10 @@ import { spawn, ChildProcess } from "child_process"; import path from "path"; +import fs from "fs"; import { readRcloneConfig, RCLONE_USER, RCLONE_PASS } from "./rcloneConfigService"; -const LOCAL_BACKUP_DIR = path.resolve(process.cwd(), "backups"); +const APP_ROOT = process.cwd(); +const SYNC_FOLDERS = ["backups", "chat-history", "uploads"]; let serverProcess: ChildProcess | null = null; @@ -46,7 +48,7 @@ export async function startWebDavServer(): Promise { const args = [ "serve", "webdav", - LOCAL_BACKUP_DIR, + APP_ROOT, "--addr", `:${config.serverPort}`, "--user", RCLONE_USER, "--pass", RCLONE_PASS, @@ -85,25 +87,17 @@ export function stopWebDavServer(): void { } } -// Receiver PC: rclone sync :webdav:/ ./backups --webdav-url http://IP:PORT --webdav-user MyDentalApp --webdav-pass OBSCURED -export async function runRclonePull(): Promise { - const config = readRcloneConfig(); - if (!config.sourceIp || !config.sourcePort) { - throw new Error("Rclone receiver configuration is incomplete — source IP and port are required"); +// Sync a single folder from the source PC via WebDAV +function syncFolder(folder: string, webdavUrl: string, obscuredPass: string): Promise { + const localDir = path.join(APP_ROOT, folder); + if (!fs.existsSync(localDir)) { + fs.mkdirSync(localDir, { recursive: true }); } - const installed = await checkRcloneInstalled(); - if (!installed) { - throw new Error("rclone is not installed. Run: curl https://rclone.org/install.sh | sudo bash"); - } - - const obscuredPass = await obscurePassword(RCLONE_PASS); - const webdavUrl = `http://${config.sourceIp}:${config.sourcePort}`; - const args = [ "sync", - `:webdav:/`, - LOCAL_BACKUP_DIR, + `:webdav:/${folder}`, + localDir, "--webdav-url", webdavUrl, "--webdav-user", RCLONE_USER, "--webdav-pass", obscuredPass, @@ -122,12 +116,33 @@ export async function runRclonePull(): Promise { }); proc.on("error", (err) => reject(new Error(`Failed to start rclone: ${err.message}`))); proc.on("close", (code) => { - if (code !== 0) return reject(new Error(`rclone exited with code ${code}: ${stderr}`)); + if (code !== 0) return reject(new Error(`rclone sync ${folder} failed (exit ${code}): ${stderr}`)); resolve(); }); }); } +// Receiver PC: pull backups, chat-history, and uploads from source +export async function runRclonePull(): Promise { + const config = readRcloneConfig(); + if (!config.sourceIp || !config.sourcePort) { + throw new Error("Rclone receiver configuration is incomplete — source IP and port are required"); + } + + const installed = await checkRcloneInstalled(); + if (!installed) { + throw new Error("rclone is not installed. Run: curl https://rclone.org/install.sh | sudo bash"); + } + + const obscuredPass = await obscurePassword(RCLONE_PASS); + const webdavUrl = `http://${config.sourceIp}:${config.sourcePort}`; + + for (const folder of SYNC_FOLDERS) { + console.log(`[rclone-pull] Syncing ${folder}...`); + await syncFolder(folder, webdavUrl, obscuredPass); + } +} + export async function autoStartServer(): Promise { const config = readRcloneConfig(); if (config.serverEnabled) {