Rust · 5130 bytes Raw Blame History
1 use clap::Args;
2 use anyhow::{Context, Result};
3 use dialoguer::{theme::ColorfulTheme, Input, Confirm};
4 use std::path::PathBuf;
5 use tracing::{info, warn};
6
7 use crate::config::Config;
8 use crate::client::ZephyrClient;
9 use super::Command;
10
11 #[derive(Debug, Args)]
12 pub struct InitCommand {
13 /// Node name (optional)
14 #[arg(short, long)]
15 name: Option<String>,
16
17 /// Data directory path
18 #[arg(short, long)]
19 data_dir: Option<PathBuf>,
20
21 /// Listen port for the node
22 #[arg(short, long)]
23 port: Option<u16>,
24
25 /// Maximum storage allocation in GB
26 #[arg(short, long)]
27 storage: Option<u64>,
28
29 /// Skip interactive configuration
30 #[arg(long)]
31 no_interactive: bool,
32 }
33
34 #[async_trait::async_trait]
35 impl Command for InitCommand {
36 async fn execute(&self, config: &Config) -> Result<()> {
37 info!("Initializing ZephyrFS node...");
38
39 let mut node_config = config.clone();
40
41 // Interactive configuration if not skipped
42 if !self.no_interactive {
43 self.interactive_config(&mut node_config)?;
44 } else {
45 self.apply_args(&mut node_config)?;
46 }
47
48 // Ensure data directory exists
49 node_config.ensure_data_dir()
50 .context("Failed to create data directory")?;
51
52 // Save configuration
53 node_config.save(None)
54 .context("Failed to save configuration")?;
55
56 // Initialize the node
57 let client = ZephyrClient::new(&node_config);
58 client.initialize_node().await
59 .context("Failed to initialize node")?;
60
61 println!("✓ ZephyrFS node initialized successfully!");
62 println!(" Node ID: {}", node_config.node.id.unwrap_or_else(|| "auto-generated".to_string()));
63 println!(" Data directory: {:?}", node_config.node.data_dir);
64 println!(" Listen port: {}", node_config.node.listen_port);
65 println!(" Max storage: {} GB", node_config.storage.max_storage_gb);
66
67 println!("\nNext steps:");
68 println!(" 1. Start the node: zephyrfs status");
69 println!(" 2. Join a network: zephyrfs join <bootstrap-peer>");
70
71 Ok(())
72 }
73 }
74
75 impl InitCommand {
76 fn interactive_config(&self, config: &mut Config) -> Result<()> {
77 let theme = ColorfulTheme::default();
78
79 // Node name
80 if let Some(name) = &self.name {
81 config.node.name = Some(name.clone());
82 } else {
83 let name: String = Input::with_theme(&theme)
84 .with_prompt("Node name (optional)")
85 .default(config.node.name.clone().unwrap_or_else(|| "zephyr-node".to_string()))
86 .allow_empty(true)
87 .interact_text()?;
88
89 config.node.name = if name.is_empty() { None } else { Some(name) };
90 }
91
92 // Data directory
93 if let Some(data_dir) = &self.data_dir {
94 config.node.data_dir = data_dir.clone();
95 } else {
96 let data_dir_str = Input::with_theme(&theme)
97 .with_prompt("Data directory")
98 .default(config.node.data_dir.to_string_lossy().to_string())
99 .interact_text()?;
100
101 config.node.data_dir = PathBuf::from(data_dir_str);
102 }
103
104 // Listen port
105 if let Some(port) = self.port {
106 config.node.listen_port = port;
107 } else {
108 config.node.listen_port = Input::with_theme(&theme)
109 .with_prompt("Listen port")
110 .default(config.node.listen_port)
111 .interact()?;
112 }
113
114 // Storage allocation
115 if let Some(storage) = self.storage {
116 config.storage.max_storage_gb = storage;
117 } else {
118 config.storage.max_storage_gb = Input::with_theme(&theme)
119 .with_prompt("Maximum storage allocation (GB)")
120 .default(config.storage.max_storage_gb)
121 .interact()?;
122 }
123
124 // Confirm settings
125 println!("\nConfiguration summary:");
126 println!(" Name: {}", config.node.name.as_deref().unwrap_or("auto-generated"));
127 println!(" Data directory: {:?}", config.node.data_dir);
128 println!(" Listen port: {}", config.node.listen_port);
129 println!(" Max storage: {} GB", config.storage.max_storage_gb);
130
131 let confirm = Confirm::with_theme(&theme)
132 .with_prompt("Proceed with initialization?")
133 .default(true)
134 .interact()?;
135
136 if !confirm {
137 warn!("Initialization cancelled by user");
138 std::process::exit(0);
139 }
140
141 Ok(())
142 }
143
144 fn apply_args(&self, config: &mut Config) -> Result<()> {
145 if let Some(name) = &self.name {
146 config.node.name = Some(name.clone());
147 }
148
149 if let Some(data_dir) = &self.data_dir {
150 config.node.data_dir = data_dir.clone();
151 }
152
153 if let Some(port) = self.port {
154 config.node.listen_port = port;
155 }
156
157 if let Some(storage) = self.storage {
158 config.storage.max_storage_gb = storage;
159 }
160
161 Ok(())
162 }
163 }