- 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>
129 lines
2.8 KiB
Rust
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"));
|
|
}
|
|
}
|