warpgate/src/cli/status.rs
grabbit 08f8fc4667 Per-share independent mounts: each share gets its own rclone process
Replace the hierarchical single-mount design with independent mounts:
each [[shares]] entry is a (name, remote_path, mount_point) triplet
with its own rclone FUSE mount process and dedicated RC API port
(5572 + index). Remove top-level connection.remote_path and [mount]
section. Auto-warmup now runs in a background thread to avoid blocking
the supervision loop.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 12:32:18 +08:00

143 lines
3.9 KiB
Rust

//! `warpgate status` — show service status, cache stats, write-back queue, bandwidth.
use anyhow::Result;
use crate::config::Config;
use crate::rclone::{mount, rc};
pub fn run(config: &Config) -> Result<()> {
// Check mount status for each share
let mut any_mounted = false;
for share in &config.shares {
let mounted = match mount::is_mounted(&share.mount_point) {
Ok(m) => m,
Err(e) => {
eprintln!("Warning: could not check mount for '{}': {}", share.name, e);
false
}
};
let ro_tag = if share.read_only { " (ro)" } else { "" };
if mounted {
println!(
"Mount: UP {}{}{}",
share.mount_point.display(),
share.name,
ro_tag
);
any_mounted = true;
} else {
println!("Mount: DOWN {}{}", share.name, ro_tag);
}
}
if !any_mounted {
println!("\nNo rclone VFS mounts are active.");
println!("Start with: systemctl start warpgate");
return Ok(());
}
// Aggregate stats from all share RC ports
let mut total_bytes = 0u64;
let mut total_speed = 0.0f64;
let mut total_transfers = 0u64;
let mut total_errors = 0u64;
let mut total_cache_used = 0u64;
let mut total_uploading = 0u64;
let mut total_queued = 0u64;
let mut total_errored = 0u64;
let mut rc_reachable = false;
for (i, _share) in config.shares.iter().enumerate() {
let port = config.rc_port(i);
if let Ok(stats) = rc::core_stats(port) {
rc_reachable = true;
total_bytes += stats.bytes;
total_speed += stats.speed;
total_transfers += stats.transfers;
total_errors += stats.errors;
}
if let Ok(vfs) = rc::vfs_stats(port) {
if let Some(dc) = vfs.disk_cache {
total_cache_used += dc.bytes_used;
total_uploading += dc.uploads_in_progress;
total_queued += dc.uploads_queued;
total_errored += dc.errored_files;
}
}
}
if rc_reachable {
println!("Speed: {}/s", format_bytes(total_speed as u64));
println!("Moved: {}", format_bytes(total_bytes));
println!("Active: {} transfers", total_transfers);
println!("Errors: {}", total_errors);
println!("Cache: {}", format_bytes(total_cache_used));
println!(
"Dirty: {} uploading, {} queued",
total_uploading, total_queued
);
if total_errored > 0 {
println!("Errored: {} files", total_errored);
}
} else {
eprintln!("Could not reach any rclone RC API.");
}
Ok(())
}
fn format_bytes(bytes: u64) -> String {
const KIB: f64 = 1024.0;
const MIB: f64 = KIB * 1024.0;
const GIB: f64 = MIB * 1024.0;
let b = bytes as f64;
if b >= GIB {
format!("{:.1} GiB", b / GIB)
} else if b >= MIB {
format!("{:.1} MiB", b / MIB)
} else if b >= KIB {
format!("{:.1} KiB", b / KIB)
} else {
format!("{} B", bytes)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_format_bytes_zero() {
assert_eq!(format_bytes(0), "0 B");
}
#[test]
fn test_format_bytes_bytes() {
assert_eq!(format_bytes(512), "512 B");
}
#[test]
fn test_format_bytes_kib() {
assert_eq!(format_bytes(1024), "1.0 KiB");
assert_eq!(format_bytes(1536), "1.5 KiB");
}
#[test]
fn test_format_bytes_mib() {
assert_eq!(format_bytes(1048576), "1.0 MiB");
}
#[test]
fn test_format_bytes_gib() {
assert_eq!(format_bytes(1073741824), "1.0 GiB");
}
#[test]
fn test_format_bytes_boundary() {
assert_eq!(format_bytes(1023), "1023 B");
assert_eq!(format_bytes(1024), "1.0 KiB");
}
}