From 15f915fbee6a8a1a7d112ecfd129a8ca9e60a0af Mon Sep 17 00:00:00 2001 From: grabbit Date: Thu, 19 Feb 2026 00:21:22 +0800 Subject: [PATCH] Show active transfer count, add SFTP retry resilience, and fix config tab refresh - Use rclone transferring array to show only active transfers instead of cumulative count; zero out speed when no transfers are active - Add SFTP retry/timeout flags to rclone mount for flaky Tailscale tunnels - Skip auto-refresh on config tab to prevent editor resets Co-Authored-By: Claude Opus 4.6 --- src/rclone/mount.rs | 10 ++++++++++ src/rclone/rc.rs | 20 ++++++++++++++++++++ src/supervisor.rs | 5 +++-- templates/web/layout.html | 2 +- 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/rclone/mount.rs b/src/rclone/mount.rs index 30b7848..d9e2570 100644 --- a/src/rclone/mount.rs +++ b/src/rclone/mount.rs @@ -70,6 +70,16 @@ pub fn build_mount_args(config: &Config, share: &ShareConfig, rc_port: u16) -> V args.push("--transfers".into()); args.push(config.writeback.transfers.to_string()); + // SFTP connection resilience: increase retries for flaky tunnels (Tailscale/WireGuard) + args.push("--retries".into()); + args.push("10".into()); + args.push("--low-level-retries".into()); + args.push("20".into()); + args.push("--retries-sleep".into()); + args.push("1s".into()); + args.push("--contimeout".into()); + args.push("30s".into()); + // Bandwidth limits (only add flag if at least one direction is limited) let bw = format_bwlimit(&config.bandwidth.limit_up, &config.bandwidth.limit_down); if bw != "0" { diff --git a/src/rclone/rc.rs b/src/rclone/rc.rs index 618cfc1..d8cb322 100644 --- a/src/rclone/rc.rs +++ b/src/rclone/rc.rs @@ -16,6 +16,9 @@ pub struct CoreStats { pub errors: u64, #[serde(rename = "totalBytes")] pub total_bytes: Option, + /// Currently active transfers (present only when transfers are running). + #[serde(default)] + pub transferring: Vec, } /// Response from `vfs/stats`. @@ -233,5 +236,22 @@ mod tests { assert_eq!(stats.bytes, u64::MAX); assert_eq!(stats.transfers, 1000000); assert_eq!(stats.errors, 999); + assert!(stats.transferring.is_empty()); + } + + #[test] + fn test_core_stats_with_active_transferring() { + let json = r#"{ + "bytes": 1048576, + "speed": 524288.0, + "transfers": 5, + "errors": 0, + "transferring": [ + {"name": "file1.jpg", "size": 1024, "bytes": 512}, + {"name": "file2.jpg", "size": 2048, "bytes": 1024} + ] + }"#; + let stats: CoreStats = serde_json::from_str(json).unwrap(); + assert_eq!(stats.transferring.len(), 2); } } diff --git a/src/supervisor.rs b/src/supervisor.rs index c68d238..80ef0fb 100644 --- a/src/supervisor.rs +++ b/src/supervisor.rs @@ -732,8 +732,9 @@ fn update_status( // Fetch core stats (speed, transfers) if let Ok(core) = rc::core_stats(mc.rc_port) { - ss.speed = core.speed; - ss.transfers = core.transfers; + let active = core.transferring.len() as u64; + ss.speed = if active > 0 { core.speed } else { 0.0 }; + ss.transfers = active; ss.errors = core.errors; } } diff --git a/templates/web/layout.html b/templates/web/layout.html index f3743d3..90c0b16 100644 --- a/templates/web/layout.html +++ b/templates/web/layout.html @@ -25,7 +25,7 @@ if (this._timer) { clearInterval(this._timer); this._timer = null; } }, refreshTab() { - if (this.activeTab === 'dashboard' || this.activeTab === 'logs') return; + if (this.activeTab === 'dashboard' || this.activeTab === 'logs' || this.activeTab === 'config') return; let url = '/tabs/' + this.activeTab; if (this.activeTab === 'shares') { const el = document.querySelector('#tab-content [x-data]');