warpgate/src/services/webdav.rs
grabbit 6bb7ec4d27 Web UI overhaul: interactive config editor, SSE live updates, log viewer, and SMB reload fixes
- Replace raw TOML textarea with Alpine.js interactive form editor (10 collapsible
  sections with change-tier badges, dynamic array management for connections/shares/
  warmup rules, proper input controls per field type)
- Add SSE-based live dashboard updates replacing htmx polling
- Add log viewer tab with ring buffer backend and incremental polling
- Fix SMB not seeing new shares after config reload: kill entire smbd process group
  (not just parent PID) so forked workers release port 445
- Add SIGHUP-based smbd config reload for share changes instead of full restart,
  preserving existing client connections
- Generate human-readable commented TOML from config editor instead of bare
  toml::to_string_pretty() output
- Fix Alpine.js 2.x __x.$data calls in dashboard/share templates (now Alpine 3.x)
- Fix toggle switch CSS overlap with field labels
- Fix dashboard going blank on tab switch (remove hx-swap-oob from tab content)
- Add htmx:afterSettle → Alpine.initTree() bridge for robust tab switching

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

129 lines
2.8 KiB
Rust

//! WebDAV service management via rclone serve webdav.
use crate::config::Config;
/// Build the `rclone serve webdav` command arguments.
///
/// Uses the first share's mount_point as the serve directory.
/// Multi-share WebDAV support is future work.
pub fn build_serve_args(config: &Config) -> Vec<String> {
let mount_point = config.shares[0].mount_point.display().to_string();
let addr = format!("0.0.0.0:{}", config.protocols.webdav_port);
vec![
"serve".into(),
"webdav".into(),
mount_point,
"--addr".into(),
addr,
"--read-only=false".into(),
]
}
/// Build the full command string (for systemd ExecStart).
pub fn build_serve_command(config: &Config) -> String {
let args = build_serve_args(config);
format!("/usr/bin/rclone {}", args.join(" "))
}
#[cfg(test)]
mod tests {
use super::*;
fn test_config() -> Config {
toml::from_str(
r#"
[[connections]]
name = "nas"
nas_host = "10.0.0.1"
nas_user = "admin"
[cache]
dir = "/tmp/cache"
[read]
[bandwidth]
[writeback]
[directory_cache]
[protocols]
[[shares]]
name = "photos"
connection = "nas"
remote_path = "/photos"
mount_point = "/mnt/photos"
"#,
)
.unwrap()
}
#[test]
fn test_build_serve_args() {
let config = test_config();
let args = build_serve_args(&config);
assert_eq!(args[0], "serve");
assert_eq!(args[1], "webdav");
assert_eq!(args[2], "/mnt/photos");
assert_eq!(args[3], "--addr");
assert_eq!(args[4], "0.0.0.0:8080");
assert_eq!(args[5], "--read-only=false");
}
#[test]
fn test_build_serve_args_custom_port() {
let mut config = test_config();
config.protocols.webdav_port = 9090;
let args = build_serve_args(&config);
assert_eq!(args[4], "0.0.0.0:9090");
}
#[test]
fn test_build_serve_args_uses_first_share() {
let config: Config = toml::from_str(
r#"
[[connections]]
name = "nas"
nas_host = "10.0.0.1"
nas_user = "admin"
[cache]
dir = "/tmp/cache"
[read]
[bandwidth]
[writeback]
[directory_cache]
[protocols]
[[shares]]
name = "photos"
connection = "nas"
remote_path = "/volume1/photos"
mount_point = "/mnt/photos"
[[shares]]
name = "videos"
connection = "nas"
remote_path = "/volume1/videos"
mount_point = "/mnt/videos"
"#,
)
.unwrap();
let args = build_serve_args(&config);
assert_eq!(args[2], "/mnt/photos");
}
#[test]
fn test_build_serve_command() {
let config = test_config();
let cmd = build_serve_command(&config);
assert!(cmd.starts_with("/usr/bin/rclone serve webdav"));
assert!(cmd.contains("/mnt/photos"));
assert!(cmd.contains("--addr"));
assert!(cmd.contains("0.0.0.0:8080"));
}
}