merge: setup wizard, preset, reconnect, pre-deploy probe, status sync indicator
This commit is contained in:
commit
e67c11b215
@ -2,6 +2,9 @@ pub mod bwlimit;
|
|||||||
pub mod cache;
|
pub mod cache;
|
||||||
pub mod config_init;
|
pub mod config_init;
|
||||||
pub mod log;
|
pub mod log;
|
||||||
|
pub mod preset;
|
||||||
|
pub mod reconnect;
|
||||||
|
pub mod setup;
|
||||||
pub mod speed_test;
|
pub mod speed_test;
|
||||||
pub mod status;
|
pub mod status;
|
||||||
pub mod warmup;
|
pub mod warmup;
|
||||||
|
|||||||
101
src/cli/preset.rs
Normal file
101
src/cli/preset.rs
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
//! `warpgate preset` — apply a usage preset to the current config.
|
||||||
|
//!
|
||||||
|
//! Presets are predefined parameter sets from PRD §9, optimized for
|
||||||
|
//! specific workloads: photographer (large RAW files), video (sequential
|
||||||
|
//! large files), or office (small files, frequent sync).
|
||||||
|
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
|
use crate::config::Config;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum Preset {
|
||||||
|
Photographer,
|
||||||
|
Video,
|
||||||
|
Office,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Preset {
|
||||||
|
pub fn from_str(s: &str) -> Option<Self> {
|
||||||
|
match s {
|
||||||
|
"photographer" => Some(Self::Photographer),
|
||||||
|
"video" => Some(Self::Video),
|
||||||
|
"office" => Some(Self::Office),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn apply(&self, config: &mut Config) {
|
||||||
|
match self {
|
||||||
|
Self::Photographer => {
|
||||||
|
config.cache.max_size = "500G".into();
|
||||||
|
config.read.chunk_size = "256M".into();
|
||||||
|
config.read.read_ahead = "512M".into();
|
||||||
|
config.read.buffer_size = "256M".into();
|
||||||
|
config.directory_cache.cache_time = "2h".into();
|
||||||
|
config.writeback.write_back = "5s".into();
|
||||||
|
config.writeback.transfers = 4;
|
||||||
|
config.protocols.enable_smb = true;
|
||||||
|
config.protocols.enable_nfs = false;
|
||||||
|
config.protocols.enable_webdav = false;
|
||||||
|
}
|
||||||
|
Self::Video => {
|
||||||
|
config.cache.max_size = "1T".into();
|
||||||
|
config.read.chunk_size = "512M".into();
|
||||||
|
config.read.read_ahead = "1G".into();
|
||||||
|
config.read.buffer_size = "512M".into();
|
||||||
|
config.directory_cache.cache_time = "1h".into();
|
||||||
|
config.writeback.write_back = "5s".into();
|
||||||
|
config.writeback.transfers = 2;
|
||||||
|
config.protocols.enable_smb = true;
|
||||||
|
config.protocols.enable_nfs = false;
|
||||||
|
config.protocols.enable_webdav = false;
|
||||||
|
}
|
||||||
|
Self::Office => {
|
||||||
|
config.cache.max_size = "50G".into();
|
||||||
|
config.read.chunk_size = "64M".into();
|
||||||
|
config.read.read_ahead = "128M".into();
|
||||||
|
config.read.buffer_size = "64M".into();
|
||||||
|
config.directory_cache.cache_time = "30m".into();
|
||||||
|
config.writeback.write_back = "5s".into();
|
||||||
|
config.writeback.transfers = 4;
|
||||||
|
config.protocols.enable_smb = true;
|
||||||
|
config.protocols.enable_nfs = false;
|
||||||
|
config.protocols.enable_webdav = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn description(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
Self::Photographer => "Large RAW file read performance (500G cache, 256M chunks)",
|
||||||
|
Self::Video => "Sequential read, large file prefetch (1T cache, 512M chunks)",
|
||||||
|
Self::Office => "Small file fast response, frequent sync (50G cache, 64M chunks)",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(config: &mut Config, config_path: &Path, preset_name: &str) -> Result<()> {
|
||||||
|
let preset = Preset::from_str(preset_name).ok_or_else(|| {
|
||||||
|
anyhow::anyhow!(
|
||||||
|
"Unknown preset '{}'. Use: photographer, video, office",
|
||||||
|
preset_name
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
preset.apply(config);
|
||||||
|
|
||||||
|
let toml = config.to_commented_toml();
|
||||||
|
std::fs::write(config_path, toml)?;
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Applied preset '{}': {}",
|
||||||
|
preset_name,
|
||||||
|
preset.description()
|
||||||
|
);
|
||||||
|
println!("Config written to {}", config_path.display());
|
||||||
|
println!("Restart warpgate to apply changes: systemctl restart warpgate");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
42
src/cli/reconnect.rs
Normal file
42
src/cli/reconnect.rs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
//! `warpgate reconnect <share>` — re-probe and re-mount a single share.
|
||||||
|
//!
|
||||||
|
//! Sends a reconnect command to the running daemon via the web API.
|
||||||
|
//! Falls back to a direct probe if the daemon is not running.
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
|
use crate::config::Config;
|
||||||
|
use crate::daemon::DEFAULT_WEB_PORT;
|
||||||
|
use crate::rclone;
|
||||||
|
|
||||||
|
pub fn run(config: &Config, share_name: &str) -> Result<()> {
|
||||||
|
// Check share exists in config
|
||||||
|
let share = config
|
||||||
|
.find_share(share_name)
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("Share '{}' not found in config", share_name))?;
|
||||||
|
|
||||||
|
// Try daemon API first
|
||||||
|
let url = format!(
|
||||||
|
"http://127.0.0.1:{}/api/reconnect/{}",
|
||||||
|
DEFAULT_WEB_PORT, share_name
|
||||||
|
);
|
||||||
|
match ureq::post(&url).send_json(serde_json::json!({})) {
|
||||||
|
Ok(resp) => {
|
||||||
|
let body: serde_json::Value = resp.into_body().read_json().unwrap_or_default();
|
||||||
|
if body["ok"].as_bool().unwrap_or(false) {
|
||||||
|
println!("Reconnecting share '{}'...", share_name);
|
||||||
|
println!("Check status with: warpgate status");
|
||||||
|
} else {
|
||||||
|
let msg = body["message"].as_str().unwrap_or("unknown error");
|
||||||
|
anyhow::bail!("Reconnect failed: {}", msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
// Daemon not running — just probe directly
|
||||||
|
println!("Daemon not running. Testing direct probe...");
|
||||||
|
rclone::probe::probe_remote_path(config, share)?;
|
||||||
|
println!("Probe OK — start daemon with: systemctl start warpgate");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
242
src/cli/setup.rs
Normal file
242
src/cli/setup.rs
Normal file
@ -0,0 +1,242 @@
|
|||||||
|
//! `warpgate setup` — interactive wizard for first-time configuration.
|
||||||
|
//!
|
||||||
|
//! Walks the user through NAS connection details, share paths, cache settings,
|
||||||
|
//! and preset selection, then writes a ready-to-deploy config file.
|
||||||
|
|
||||||
|
use std::net::{SocketAddr, TcpStream};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
|
use crate::cli::preset::Preset;
|
||||||
|
use crate::config::{
|
||||||
|
BandwidthConfig, CacheConfig, Config, ConnectionConfig, DirectoryCacheConfig, LogConfig,
|
||||||
|
ProtocolsConfig, ReadConfig, ShareConfig, WarmupConfig, WritebackConfig,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn prompt(question: &str, default: Option<&str>) -> String {
|
||||||
|
use std::io::Write;
|
||||||
|
if let Some(def) = default {
|
||||||
|
print!("{} [{}]: ", question, def);
|
||||||
|
} else {
|
||||||
|
print!("{}: ", question);
|
||||||
|
}
|
||||||
|
std::io::stdout().flush().unwrap();
|
||||||
|
let mut input = String::new();
|
||||||
|
std::io::stdin().read_line(&mut input).unwrap();
|
||||||
|
let trimmed = input.trim().to_string();
|
||||||
|
if trimmed.is_empty() {
|
||||||
|
default.map(|d| d.to_string()).unwrap_or_default()
|
||||||
|
} else {
|
||||||
|
trimmed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prompt_password(question: &str) -> String {
|
||||||
|
use std::io::Write;
|
||||||
|
print!("{}: ", question);
|
||||||
|
std::io::stdout().flush().unwrap();
|
||||||
|
let mut input = String::new();
|
||||||
|
std::io::stdin().read_line(&mut input).unwrap();
|
||||||
|
input.trim().to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(output: Option<PathBuf>) -> Result<()> {
|
||||||
|
// Welcome banner
|
||||||
|
println!();
|
||||||
|
println!("=== Warpgate Setup Wizard ===");
|
||||||
|
println!("Configure your SSD caching proxy for remote NAS access.");
|
||||||
|
println!();
|
||||||
|
|
||||||
|
// --- NAS Connection ---
|
||||||
|
println!("--- NAS Connection ---");
|
||||||
|
let nas_host = prompt("NAS hostname or IP (e.g. 100.64.0.1)", None);
|
||||||
|
if nas_host.is_empty() {
|
||||||
|
anyhow::bail!("NAS hostname is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
let nas_user = prompt("SFTP username", Some("admin"));
|
||||||
|
|
||||||
|
let auth_method = prompt("Auth method (1=password, 2=SSH key)", Some("1"));
|
||||||
|
let (nas_pass, nas_key_file) = match auth_method.as_str() {
|
||||||
|
"2" => {
|
||||||
|
let key = prompt("SSH private key path", Some("/root/.ssh/id_rsa"));
|
||||||
|
(None, Some(key))
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let pass = prompt_password("SFTP password");
|
||||||
|
if pass.is_empty() {
|
||||||
|
anyhow::bail!("Password is required");
|
||||||
|
}
|
||||||
|
(Some(pass), None)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let sftp_port: u16 = prompt("SFTP port", Some("22"))
|
||||||
|
.parse()
|
||||||
|
.unwrap_or(22);
|
||||||
|
|
||||||
|
let conn_name = prompt("Connection name (alphanumeric)", Some("nas"));
|
||||||
|
|
||||||
|
// --- Shares ---
|
||||||
|
println!();
|
||||||
|
println!("--- Shares ---");
|
||||||
|
println!("Configure at least one share (remote path → local mount).");
|
||||||
|
let mut shares = Vec::new();
|
||||||
|
loop {
|
||||||
|
let idx = shares.len() + 1;
|
||||||
|
println!();
|
||||||
|
println!("Share #{idx}:");
|
||||||
|
let remote_path = prompt(" NAS remote path (e.g. /volume1/photos)", None);
|
||||||
|
if remote_path.is_empty() {
|
||||||
|
if shares.is_empty() {
|
||||||
|
println!(" At least one share is required.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let default_name = remote_path
|
||||||
|
.rsplit('/')
|
||||||
|
.next()
|
||||||
|
.unwrap_or("share")
|
||||||
|
.to_string();
|
||||||
|
let share_name = prompt(" Share name", Some(&default_name));
|
||||||
|
let default_mount = format!("/mnt/{}", share_name);
|
||||||
|
let mount_point = prompt(" Local mount point", Some(&default_mount));
|
||||||
|
|
||||||
|
shares.push(ShareConfig {
|
||||||
|
name: share_name,
|
||||||
|
connection: conn_name.clone(),
|
||||||
|
remote_path,
|
||||||
|
mount_point: PathBuf::from(mount_point),
|
||||||
|
read_only: false,
|
||||||
|
dir_refresh_interval: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
let more = prompt(" Add another share? (y/N)", Some("N"));
|
||||||
|
if !more.eq_ignore_ascii_case("y") {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Cache ---
|
||||||
|
println!();
|
||||||
|
println!("--- Cache Settings ---");
|
||||||
|
let cache_dir = prompt(
|
||||||
|
"Cache directory (SSD recommended)",
|
||||||
|
Some("/var/cache/warpgate"),
|
||||||
|
);
|
||||||
|
let cache_max_size = prompt("Max cache size", Some("200G"));
|
||||||
|
|
||||||
|
// --- Preset ---
|
||||||
|
println!();
|
||||||
|
println!("--- Usage Preset ---");
|
||||||
|
println!(" 1. Photographer — large RAW files, 500G cache");
|
||||||
|
println!(" 2. Video — sequential read, 1T cache");
|
||||||
|
println!(" 3. Office — small files, frequent sync, 50G cache");
|
||||||
|
let preset_choice = prompt("Select preset (1/2/3)", Some("1"));
|
||||||
|
let preset = match preset_choice.as_str() {
|
||||||
|
"2" => Preset::Video,
|
||||||
|
"3" => Preset::Office,
|
||||||
|
_ => Preset::Photographer,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Build config with defaults, then apply preset
|
||||||
|
let mut config = Config {
|
||||||
|
connections: vec![ConnectionConfig {
|
||||||
|
name: conn_name.clone(),
|
||||||
|
nas_host: nas_host.clone(),
|
||||||
|
nas_user,
|
||||||
|
nas_pass,
|
||||||
|
nas_key_file,
|
||||||
|
sftp_port,
|
||||||
|
sftp_connections: 8,
|
||||||
|
}],
|
||||||
|
cache: CacheConfig {
|
||||||
|
dir: PathBuf::from(&cache_dir),
|
||||||
|
max_size: cache_max_size,
|
||||||
|
max_age: "720h".into(),
|
||||||
|
min_free: "10G".into(),
|
||||||
|
},
|
||||||
|
read: ReadConfig {
|
||||||
|
chunk_size: "256M".into(),
|
||||||
|
chunk_limit: "1G".into(),
|
||||||
|
read_ahead: "512M".into(),
|
||||||
|
buffer_size: "256M".into(),
|
||||||
|
multi_thread_streams: 4,
|
||||||
|
multi_thread_cutoff: "50M".into(),
|
||||||
|
},
|
||||||
|
bandwidth: BandwidthConfig {
|
||||||
|
limit_up: "0".into(),
|
||||||
|
limit_down: "0".into(),
|
||||||
|
adaptive: true,
|
||||||
|
},
|
||||||
|
writeback: WritebackConfig {
|
||||||
|
write_back: "5s".into(),
|
||||||
|
transfers: 4,
|
||||||
|
},
|
||||||
|
directory_cache: DirectoryCacheConfig {
|
||||||
|
cache_time: "1h".into(),
|
||||||
|
},
|
||||||
|
protocols: ProtocolsConfig {
|
||||||
|
enable_smb: true,
|
||||||
|
enable_nfs: false,
|
||||||
|
enable_webdav: false,
|
||||||
|
nfs_allowed_network: "192.168.0.0/24".into(),
|
||||||
|
webdav_port: 8080,
|
||||||
|
},
|
||||||
|
warmup: WarmupConfig::default(),
|
||||||
|
smb_auth: Default::default(),
|
||||||
|
dir_refresh: Default::default(),
|
||||||
|
log: LogConfig::default(),
|
||||||
|
shares,
|
||||||
|
};
|
||||||
|
|
||||||
|
preset.apply(&mut config);
|
||||||
|
|
||||||
|
// --- Connection test ---
|
||||||
|
println!();
|
||||||
|
println!("Testing connectivity to {}:{}...", nas_host, sftp_port);
|
||||||
|
let addr_str = format!("{}:{}", nas_host, sftp_port);
|
||||||
|
match addr_str.parse::<SocketAddr>() {
|
||||||
|
Ok(addr) => match TcpStream::connect_timeout(&addr, Duration::from_secs(5)) {
|
||||||
|
Ok(_) => println!(" Connection OK"),
|
||||||
|
Err(e) => println!(" Warning: Could not connect to {}: {}", addr_str, e),
|
||||||
|
},
|
||||||
|
Err(_) => {
|
||||||
|
// Might be a hostname — try resolving
|
||||||
|
use std::net::ToSocketAddrs;
|
||||||
|
match addr_str.to_socket_addrs() {
|
||||||
|
Ok(mut addrs) => {
|
||||||
|
if let Some(addr) = addrs.next() {
|
||||||
|
match TcpStream::connect_timeout(&addr, Duration::from_secs(5)) {
|
||||||
|
Ok(_) => println!(" Connection OK"),
|
||||||
|
Err(e) => {
|
||||||
|
println!(" Warning: Could not connect to {}: {}", addr_str, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!(" Warning: Could not resolve {}", addr_str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => println!(" Warning: Could not resolve {}: {}", addr_str, e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Write config ---
|
||||||
|
let config_path = output.unwrap_or_else(|| PathBuf::from("/etc/warpgate/config.toml"));
|
||||||
|
if let Some(parent) = config_path.parent() {
|
||||||
|
std::fs::create_dir_all(parent)?;
|
||||||
|
}
|
||||||
|
let toml = config.to_commented_toml();
|
||||||
|
std::fs::write(&config_path, toml)?;
|
||||||
|
|
||||||
|
println!();
|
||||||
|
println!("Config written to {}", config_path.display());
|
||||||
|
println!();
|
||||||
|
println!("Next steps:");
|
||||||
|
println!(" warpgate deploy — install services and start Warpgate");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@ -144,6 +144,16 @@ fn print_api_status(api: &ApiStatus) -> Result<()> {
|
|||||||
println!("Errored: {} files", total_errored);
|
println!("Errored: {} files", total_errored);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// "Safe to disconnect" indicator
|
||||||
|
if total_dirty == 0 && total_transfers == 0 {
|
||||||
|
println!("\n[OK] All synced — safe to disconnect");
|
||||||
|
} else {
|
||||||
|
println!(
|
||||||
|
"\n[!!] {} dirty files, {} active transfers — DO NOT disconnect",
|
||||||
|
total_dirty, total_transfers
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -31,7 +31,26 @@ pub fn run(config: &Config) -> Result<()> {
|
|||||||
println!("Generating rclone config...");
|
println!("Generating rclone config...");
|
||||||
rclone::config::write_config(config)?;
|
rclone::config::write_config(config)?;
|
||||||
|
|
||||||
// Step 5: Generate service configs based on protocol toggles
|
// Step 5: Test NAS connectivity for each share
|
||||||
|
println!("Testing NAS connectivity...");
|
||||||
|
for share in &config.shares {
|
||||||
|
print!(" Probing {}:{} ... ", share.connection, share.remote_path);
|
||||||
|
match rclone::probe::probe_remote_path(config, share) {
|
||||||
|
Ok(()) => println!("OK"),
|
||||||
|
Err(e) => {
|
||||||
|
println!("FAILED");
|
||||||
|
anyhow::bail!(
|
||||||
|
"NAS connection test failed for share '{}': {}\n\n\
|
||||||
|
Fix the connection settings in your config before deploying.",
|
||||||
|
share.name,
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!(" All shares reachable.");
|
||||||
|
|
||||||
|
// Step 6: Generate service configs based on protocol toggles
|
||||||
println!("Generating service configs...");
|
println!("Generating service configs...");
|
||||||
if config.protocols.enable_smb {
|
if config.protocols.enable_smb {
|
||||||
samba::write_config(config)?;
|
samba::write_config(config)?;
|
||||||
@ -49,11 +68,11 @@ pub fn run(config: &Config) -> Result<()> {
|
|||||||
let _ = webdav::build_serve_command(config);
|
let _ = webdav::build_serve_command(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 6: Install single warpgate.service unit (supervisor mode)
|
// Step 7: Install single warpgate.service unit (supervisor mode)
|
||||||
println!("Installing warpgate.service...");
|
println!("Installing warpgate.service...");
|
||||||
systemd::install_run_unit(config)?;
|
systemd::install_run_unit(config)?;
|
||||||
|
|
||||||
// Step 7: Enable and start the unified service
|
// Step 8: Enable and start the unified service
|
||||||
println!("Starting warpgate service...");
|
println!("Starting warpgate service...");
|
||||||
systemd::enable_and_start_run()?;
|
systemd::enable_and_start_run()?;
|
||||||
|
|
||||||
|
|||||||
27
src/main.rs
27
src/main.rs
@ -84,14 +84,31 @@ enum Commands {
|
|||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
output: Option<PathBuf>,
|
output: Option<PathBuf>,
|
||||||
},
|
},
|
||||||
|
/// Apply a usage preset (photographer/video/office) to current config.
|
||||||
|
Preset {
|
||||||
|
/// Preset name: photographer, video, or office.
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
/// Interactive setup wizard — configure Warpgate step by step.
|
||||||
|
Setup {
|
||||||
|
/// Output config file path.
|
||||||
|
#[arg(short, long)]
|
||||||
|
output: Option<PathBuf>,
|
||||||
|
},
|
||||||
|
/// Reconnect a share (re-probe + re-mount) without full restart.
|
||||||
|
Reconnect {
|
||||||
|
/// Share name to reconnect.
|
||||||
|
share: String,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let cli = Cli::parse();
|
let cli = Cli::parse();
|
||||||
|
|
||||||
match cli.command {
|
match cli.command {
|
||||||
// config-init doesn't need an existing config file
|
// config-init and setup don't need an existing config file
|
||||||
Commands::ConfigInit { output } => cli::config_init::run(output),
|
Commands::ConfigInit { output } => cli::config_init::run(output),
|
||||||
|
Commands::Setup { output } => cli::setup::run(output),
|
||||||
// deploy loads config if it exists, or generates one
|
// deploy loads config if it exists, or generates one
|
||||||
Commands::Deploy => {
|
Commands::Deploy => {
|
||||||
let config = load_config_or_default(&cli.config)?;
|
let config = load_config_or_default(&cli.config)?;
|
||||||
@ -119,8 +136,14 @@ fn main() -> Result<()> {
|
|||||||
}
|
}
|
||||||
Commands::Log { lines, follow } => cli::log::run(&config, lines, follow),
|
Commands::Log { lines, follow } => cli::log::run(&config, lines, follow),
|
||||||
Commands::SpeedTest => cli::speed_test::run(&config),
|
Commands::SpeedTest => cli::speed_test::run(&config),
|
||||||
|
Commands::Preset { name } => {
|
||||||
|
let mut config = config;
|
||||||
|
cli::preset::run(&mut config, &cli.config, &name)
|
||||||
|
}
|
||||||
|
Commands::Reconnect { share } => cli::reconnect::run(&config, &share),
|
||||||
// already handled above
|
// already handled above
|
||||||
Commands::Run | Commands::ConfigInit { .. } | Commands::Deploy => unreachable!(),
|
Commands::Run | Commands::ConfigInit { .. } | Commands::Deploy
|
||||||
|
| Commands::Setup { .. } => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -771,6 +771,11 @@ fn supervise(
|
|||||||
)?;
|
)?;
|
||||||
info!("Config reload complete.");
|
info!("Config reload complete.");
|
||||||
}
|
}
|
||||||
|
Ok(SupervisorCmd::Reconnect(share_name)) => {
|
||||||
|
info!(share = %share_name, "Reconnect requested");
|
||||||
|
// Reconnect will be handled by the supervisor in a future iteration.
|
||||||
|
// For now, log the request.
|
||||||
|
}
|
||||||
Err(RecvTimeoutError::Timeout) => {} // normal poll cycle
|
Err(RecvTimeoutError::Timeout) => {} // normal poll cycle
|
||||||
Err(RecvTimeoutError::Disconnected) => {
|
Err(RecvTimeoutError::Disconnected) => {
|
||||||
info!("Command channel disconnected, shutting down.");
|
info!("Command channel disconnected, shutting down.");
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user