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 std::future::Future;
7use std::pin::Pin;
8use uuid::Uuid;
9
10#[derive(Debug, Default)]
11pub struct CreateCommand;
12
13impl CreateCommand {
14 pub fn new() -> Self {
15 Self
16 }
17}
18
19impl Command for CreateCommand {
20 fn name(&self) -> &'static str {
21 "create"
22 }
23 fn description(&self) -> &'static str {
24 "Create a new web server (persistent)"
25 }
26 fn matches(&self, command: &str) -> bool {
27 command.trim().to_lowercase().starts_with("create")
28 }
29
30 fn execute_sync(&self, args: &[&str]) -> Result<String> {
31 let config_result = std::thread::spawn(|| {
33 let rt = tokio::runtime::Runtime::new().unwrap();
34 rt.block_on(Config::load())
35 })
36 .join()
37 .map_err(|_| AppError::Validation("Failed to load config".to_string()))??;
38
39 let ctx = crate::server::shared::get_shared_context();
40
41 match args.len() {
42 0 => self.create_server(&config_result, ctx, None, None),
43 1 => {
44 if let Ok(port) = args[0].parse::<u16>() {
45 self.create_server(&config_result, ctx, None, Some(port))
46 } else {
47 self.create_server(&config_result, ctx, Some(args[0].to_string()), None)
48 }
49 }
50 2 => match args[1].parse::<u16>() {
51 Ok(port) => {
52 self.create_server(&config_result, ctx, Some(args[0].to_string()), Some(port))
53 }
54 Err(_) => Err(AppError::Validation("Invalid port".to_string())),
55 },
56 _ => Err(AppError::Validation("Too many parameters".to_string())),
57 }
58 }
59
60 fn execute_async<'a>(
61 &'a self,
62 args: &'a [&'a str],
63 ) -> Pin<Box<dyn Future<Output = Result<String>> + Send + 'a>> {
64 Box::pin(async move {
65 let config = Config::load().await?;
66 let ctx = crate::server::shared::get_shared_context();
67
68 match args.len() {
69 0 => self.create_server(&config, ctx, None, None),
70 1 => {
71 if let Ok(port) = args[0].parse::<u16>() {
72 self.create_server(&config, ctx, None, Some(port))
73 } else {
74 self.create_server(&config, ctx, Some(args[0].to_string()), None)
75 }
76 }
77 2 => match args[1].parse::<u16>() {
78 Ok(port) => {
79 self.create_server(&config, ctx, Some(args[0].to_string()), Some(port))
80 }
81 Err(_) => Err(AppError::Validation("Invalid port".to_string())),
82 },
83 _ => Err(AppError::Validation("Too many parameters".to_string())),
84 }
85 })
86 }
87
88 fn supports_async(&self) -> bool {
89 true
90 }
91 fn priority(&self) -> u8 {
92 65
93 }
94}
95
96impl CreateCommand {
97 fn create_server(
98 &self,
99 config: &Config,
100 ctx: &ServerContext,
101 custom_name: Option<String>,
102 custom_port: Option<u16>,
103 ) -> Result<String> {
104 let id = Uuid::new_v4().to_string();
105 let has_custom_name = custom_name.is_some();
106 let has_custom_port = custom_port.is_some();
107
108 let name = if let Some(custom_name) = custom_name {
109 validate_server_name(&custom_name)?;
110 let servers = ctx.servers.read().unwrap();
111 if servers.values().any(|s| s.name == custom_name) {
112 return Err(AppError::Validation(format!(
113 "Server-Name '{}' bereits vergeben!",
114 custom_name
115 )));
116 }
117 custom_name
118 } else {
119 let server_number = self.find_next_server_number(ctx);
120 format!("rush-sync-server-{:03}", server_number)
121 };
122
123 let port = if let Some(custom_port) = custom_port {
124 let min_port = config.server.port_range_start.max(1024);
126 if custom_port < min_port {
127 return Err(AppError::Validation(format!(
128 "Port must be >= {} (configured minimum: {})",
129 min_port, config.server.port_range_start
130 )));
131 }
132
133 if custom_port > config.server.port_range_end {
135 return Err(AppError::Validation(format!(
136 "Port {} exceeds configured maximum: {}",
137 custom_port, config.server.port_range_end
138 )));
139 }
140
141 let servers = ctx.servers.read().unwrap();
142 if servers.values().any(|s| s.port == custom_port) {
143 return Err(AppError::Validation(format!(
144 "Port {} bereits verwendet!",
145 custom_port
146 )));
147 }
148 if !self.is_port_available(custom_port) {
149 return Err(AppError::Validation(format!(
150 "Port {} bereits belegt!",
151 custom_port
152 )));
153 }
154 custom_port
155 } else {
156 self.find_next_available_port(config)?
157 };
158
159 let current_server_count = ctx.servers.read().unwrap().len();
161 if current_server_count >= config.server.max_concurrent {
162 return Err(AppError::Validation(format!(
163 "Maximum server limit reached: {}/{}. Use 'cleanup' to remove stopped servers or increase max_concurrent in config.",
164 current_server_count, config.server.max_concurrent
165 )));
166 }
167
168 let timestamp = std::time::SystemTime::now()
169 .duration_since(std::time::UNIX_EPOCH)
170 .unwrap_or_default()
171 .as_secs();
172
173 let server_info = ServerInfo {
174 id: id.clone(),
175 name: name.clone(),
176 port,
177 status: ServerStatus::Stopped,
178 created_at: chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string(),
179 created_timestamp: timestamp,
180 };
181
182 ctx.servers
184 .write()
185 .unwrap()
186 .insert(id.clone(), server_info.clone());
187
188 let registry = crate::server::shared::get_persistent_registry();
190 let server_info_clone = server_info.clone();
191 tokio::spawn(async move {
192 if let Ok(persistent_servers) = registry.load_servers().await {
193 if let Err(e) = registry
194 .add_server(persistent_servers, server_info_clone)
195 .await
196 {
197 log::error!("Failed to persist server: {}", e);
198 }
199 }
200 });
201
202 let success_msg = if has_custom_name || has_custom_port {
203 format!(
204 "Custom Server created: '{}' (ID: {}) on port {} [PERSISTENT] ({}/{} servers)",
205 name,
206 &id[0..8],
207 port,
208 current_server_count + 1,
209 config.server.max_concurrent
210 )
211 } else {
212 format!(
213 "Server created: '{}' (ID: {}) on port {} [PERSISTENT] ({}/{} servers)",
214 name,
215 &id[0..8],
216 port,
217 current_server_count + 1,
218 config.server.max_concurrent
219 )
220 };
221
222 Ok(success_msg)
223 }
224
225 fn find_next_available_port(&self, config: &Config) -> Result<u16> {
227 let ctx = crate::server::shared::get_shared_context();
228 let servers = ctx.servers.read().unwrap();
229 let mut used_ports: Vec<u16> = servers.values().map(|s| s.port).collect();
230 used_ports.sort();
231
232 let mut candidate_port = config.server.port_range_start;
233 let max_port = config.server.port_range_end;
234
235 loop {
236 if candidate_port > max_port {
237 return Err(AppError::Validation(format!(
238 "No available ports in configured range {}-{}",
239 config.server.port_range_start, config.server.port_range_end
240 )));
241 }
242
243 if !used_ports.contains(&candidate_port) && self.is_port_available(candidate_port) {
244 return Ok(candidate_port);
245 }
246
247 candidate_port += 1;
248 }
249 }
250
251 fn is_port_available(&self, port: u16) -> bool {
252 use std::net::TcpListener;
253 use std::time::Duration;
254
255 match TcpListener::bind(("127.0.0.1", port)) {
256 Ok(listener) => {
257 drop(listener);
258 std::thread::sleep(Duration::from_millis(10));
259 TcpListener::bind(("127.0.0.1", port)).is_ok()
260 }
261 Err(_) => false,
262 }
263 }
264
265 fn find_next_server_number(&self, ctx: &ServerContext) -> u32 {
266 let servers = ctx.servers.read().unwrap();
267 let mut existing_numbers = Vec::new();
268
269 for server in servers.values() {
270 if let Some(number_str) = server.name.strip_prefix("rush-sync-server-") {
271 if let Ok(number) = number_str.parse::<u32>() {
272 existing_numbers.push(number);
273 }
274 }
275 }
276
277 existing_numbers.sort();
278 let mut next_number = 1;
279 for &existing in &existing_numbers {
280 if existing == next_number {
281 next_number += 1;
282 } else {
283 break;
284 }
285 }
286 next_number
287 }
288}