rush_sync_server/commands/create/
command.rs

1// Fixed src/commands/create/command.rs
2use 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            // Use configurable minimum port from config
84            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            // Check if port is within configured range
93            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        // Check server count limit
120        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        // Verzeichnis sofort bei CREATE erstellen
143        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        // Add to runtime context
152        ctx.servers
153            .write()
154            .unwrap()
155            .insert(id.clone(), server_info.clone());
156
157        // Persist to file (async)
158        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
163                    .add_server(persistent_servers, server_info_clone)
164                    .await
165                {
166                    log::error!("Failed to persist server: {}", e);
167                }
168            }
169        });
170
171        let success_msg = if has_custom_name || has_custom_port {
172            format!(
173                "Custom Server created: '{}' (ID: {}) on port {} [PERSISTENT] ({}/{} servers)",
174                name,
175                &id[0..8],
176                port,
177                current_server_count + 1,
178                config.server.max_concurrent
179            )
180        } else {
181            format!(
182                "Server created: '{}' (ID: {}) on port {} [PERSISTENT] ({}/{} servers)",
183                name,
184                &id[0..8],
185                port,
186                current_server_count + 1,
187                config.server.max_concurrent
188            )
189        };
190
191        Ok(success_msg)
192    }
193
194    // Updated to use config instead of context
195    fn find_next_available_port(&self, config: &Config) -> Result<u16> {
196        let ctx = crate::server::shared::get_shared_context();
197        let used_ports: std::collections::HashSet<u16> = {
198            let servers = ctx
199                .servers
200                .read()
201                .map_err(|_| AppError::Validation("Server-Context lock poisoned".to_string()))?;
202            servers.values().map(|s| s.port).collect()
203        };
204
205        let start_port = config.server.port_range_start;
206        let end_port = config.server.port_range_end;
207
208        // Sicherheitscheck für Port-Range
209        if start_port >= end_port {
210            return Err(AppError::Validation(format!(
211                "Invalid port range: {} >= {}. Check config.",
212                start_port, end_port
213            )));
214        }
215
216        // Effizienter: Nicht mehr als 1000 Ports testen
217        let max_attempts = ((end_port - start_port + 1) as usize).min(1000);
218
219        for i in 0..max_attempts {
220            let candidate_port = start_port + (i as u16);
221
222            if candidate_port > end_port {
223                break;
224            }
225
226            if !used_ports.contains(&candidate_port)
227                && crate::server::utils::port::is_port_available(candidate_port)
228            {
229                return Ok(candidate_port);
230            }
231        }
232
233        Err(AppError::Validation(format!(
234            "No available ports in range {}-{} after {} attempts",
235            start_port, end_port, max_attempts
236        )))
237    }
238
239    fn find_next_server_number(&self, ctx: &ServerContext) -> u32 {
240        let servers = ctx.servers.read().unwrap();
241        let mut existing_numbers = Vec::new();
242
243        for server in servers.values() {
244            if let Some(number_str) = server.name.strip_prefix("rss-") {
245                if let Ok(number) = number_str.parse::<u32>() {
246                    existing_numbers.push(number);
247                }
248            }
249        }
250
251        existing_numbers.sort();
252        let mut next_number = 1;
253        for &existing in &existing_numbers {
254            if existing == next_number {
255                next_number += 1;
256            } else {
257                break;
258            }
259        }
260        next_number
261    }
262}