rush_sync_server/commands/create/
command.rs1use crate::commands::command::Command;
3use crate::core::prelude::*;
4use crate::server::types::{ServerContext, ServerInfo, ServerStatus};
5use crate::server::utils::validation::validate_server_name;
6use uuid::Uuid;
7
8#[derive(Debug, Default)]
9pub struct CreateCommand;
10
11impl CreateCommand {
12 pub fn new() -> Self {
13 Self
14 }
15}
16
17impl Command for CreateCommand {
18 fn name(&self) -> &'static str {
19 "create"
20 }
21 fn description(&self) -> &'static str {
22 "Create a new web server (persistent)"
23 }
24 fn matches(&self, command: &str) -> bool {
25 command.trim().to_lowercase().starts_with("create")
26 }
27
28 fn execute_sync(&self, args: &[&str]) -> Result<String> {
29 let config = get_config()?;
30
31 let ctx = crate::server::shared::get_shared_context();
32
33 match args.len() {
34 0 => self.create_server(&config, ctx, None, None),
35 1 => {
36 if let Ok(port) = args[0].parse::<u16>() {
37 self.create_server(&config, ctx, None, Some(port))
38 } else {
39 self.create_server(&config, ctx, Some(args[0].to_string()), None)
40 }
41 }
42 2 => match args[1].parse::<u16>() {
43 Ok(port) => self.create_server(&config, ctx, Some(args[0].to_string()), Some(port)),
44 Err(_) => Err(AppError::Validation("Invalid port".to_string())),
45 },
46 _ => Err(AppError::Validation("Too many parameters".to_string())),
47 }
48 }
49
50 fn priority(&self) -> u8 {
51 65
52 }
53}
54
55impl CreateCommand {
56 fn create_server(
57 &self,
58 config: &Config,
59 ctx: &ServerContext,
60 custom_name: Option<String>,
61 custom_port: Option<u16>,
62 ) -> Result<String> {
63 let id = Uuid::new_v4().to_string();
64 let has_custom_name = custom_name.is_some();
65 let has_custom_port = custom_port.is_some();
66
67 let name = if let Some(custom_name) = custom_name {
68 validate_server_name(&custom_name)?;
69 let servers = ctx.servers.read().unwrap();
70 if servers.values().any(|s| s.name == custom_name) {
71 return Err(AppError::Validation(format!(
72 "Server-Name '{}' bereits vergeben!",
73 custom_name
74 )));
75 }
76 custom_name
77 } else {
78 let server_number = self.find_next_server_number(ctx);
79 format!("rss-{:03}", server_number)
80 };
81
82 let port = if let Some(custom_port) = custom_port {
83 let min_port = config.server.port_range_start.max(1024);
85 if custom_port < min_port {
86 return Err(AppError::Validation(format!(
87 "Port must be >= {} (configured minimum: {})",
88 min_port, config.server.port_range_start
89 )));
90 }
91
92 if custom_port > config.server.port_range_end {
94 return Err(AppError::Validation(format!(
95 "Port {} exceeds configured maximum: {}",
96 custom_port, config.server.port_range_end
97 )));
98 }
99
100 let servers = ctx.servers.read().unwrap();
101 if servers.values().any(|s| s.port == custom_port) {
102 return Err(AppError::Validation(format!(
103 "Port {} bereits verwendet!",
104 custom_port
105 )));
106 }
107 if !crate::server::utils::port::is_port_available(custom_port) {
108 return Err(AppError::Validation(format!(
109 "Port {} bereits belegt!",
110 custom_port
111 )));
112 }
113
114 custom_port
115 } else {
116 self.find_next_available_port(config)?
117 };
118
119 let current_server_count = ctx.servers.read().unwrap().len();
121 if current_server_count >= config.server.max_concurrent {
122 return Err(AppError::Validation(format!(
123 "Maximum server limit reached: {}/{}. Use 'cleanup' to remove stopped servers or increase max_concurrent in config.",
124 current_server_count, config.server.max_concurrent
125 )));
126 }
127
128 let timestamp = std::time::SystemTime::now()
129 .duration_since(std::time::UNIX_EPOCH)
130 .unwrap_or_default()
131 .as_secs();
132
133 let server_info = ServerInfo {
134 id: id.clone(),
135 name: name.clone(),
136 port,
137 status: ServerStatus::Stopped,
138 created_at: chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string(),
139 created_timestamp: timestamp,
140 };
141
142 if let Err(e) = crate::server::handlers::web::create_server_directory_and_files(&name, port)
144 {
145 return Err(AppError::Validation(format!(
146 "Failed to create server directory: {}",
147 e
148 )));
149 }
150
151 ctx.servers
153 .write()
154 .unwrap()
155 .insert(id.clone(), server_info.clone());
156
157 let registry = crate::server::shared::get_persistent_registry();
159 let server_info_clone = server_info.clone();
160 tokio::spawn(async move {
161 if let Ok(_persistent_servers) = registry.load_servers().await {
162 if let Err(e) = registry.add_server(server_info_clone).await {
163 log::error!("Failed to persist server: {}", e);
164 }
165 }
166 });
167
168 let success_msg = if has_custom_name || has_custom_port {
169 format!(
170 "Custom Server created: '{}' (ID: {}) on port {} [PERSISTENT] ({}/{} servers)",
171 name,
172 &id[0..8],
173 port,
174 current_server_count + 1,
175 config.server.max_concurrent
176 )
177 } else {
178 format!(
179 "Server created: '{}' (ID: {}) on port {} [PERSISTENT] ({}/{} servers)",
180 name,
181 &id[0..8],
182 port,
183 current_server_count + 1,
184 config.server.max_concurrent
185 )
186 };
187
188 Ok(success_msg)
189 }
190
191 fn find_next_available_port(&self, config: &Config) -> Result<u16> {
193 let ctx = crate::server::shared::get_shared_context();
194 let used_ports: std::collections::HashSet<u16> = {
195 let servers = ctx
196 .servers
197 .read()
198 .map_err(|_| AppError::Validation("Server-Context lock poisoned".to_string()))?;
199 servers.values().map(|s| s.port).collect()
200 };
201
202 let start_port = config.server.port_range_start;
203 let end_port = config.server.port_range_end;
204
205 if start_port >= end_port {
207 return Err(AppError::Validation(format!(
208 "Invalid port range: {} >= {}. Check config.",
209 start_port, end_port
210 )));
211 }
212
213 let max_attempts = ((end_port - start_port + 1) as usize).min(1000);
215
216 for i in 0..max_attempts {
217 let candidate_port = start_port + (i as u16);
218
219 if candidate_port > end_port {
220 break;
221 }
222
223 if !used_ports.contains(&candidate_port)
224 && crate::server::utils::port::is_port_available(candidate_port)
225 {
226 return Ok(candidate_port);
227 }
228 }
229
230 Err(AppError::Validation(format!(
231 "No available ports in range {}-{} after {} attempts",
232 start_port, end_port, max_attempts
233 )))
234 }
235
236 fn find_next_server_number(&self, ctx: &ServerContext) -> u32 {
237 let servers = ctx.servers.read().unwrap();
238 let mut existing_numbers = Vec::new();
239
240 for server in servers.values() {
241 if let Some(number_str) = server.name.strip_prefix("rss-") {
242 if let Ok(number) = number_str.parse::<u32>() {
243 existing_numbers.push(number);
244 }
245 }
246 }
247
248 existing_numbers.sort();
249 let mut next_number = 1;
250 for &existing in &existing_numbers {
251 if existing == next_number {
252 next_number += 1;
253 } else {
254 break;
255 }
256 }
257 next_number
258 }
259}