rush_sync_server/commands/start/
command.rs

1// Fixed src/commands/start/command.rs
2use crate::commands::command::Command;
3use crate::core::prelude::*;
4use crate::server::types::{ServerContext, ServerStatus};
5use crate::server::utils::port::is_port_available;
6use crate::server::utils::validation::find_server;
7use opener;
8
9#[derive(Debug, Default)]
10pub struct StartCommand;
11
12impl StartCommand {
13    pub fn new() -> Self {
14        Self
15    }
16}
17
18impl Command for StartCommand {
19    fn name(&self) -> &'static str {
20        "start"
21    }
22    fn description(&self) -> &'static str {
23        "Start a web server (persistent)"
24    }
25    fn matches(&self, command: &str) -> bool {
26        command.trim().to_lowercase().starts_with("start")
27    }
28
29    fn execute_sync(&self, args: &[&str]) -> Result<String> {
30        if args.is_empty() {
31            return Err(AppError::Validation(
32                "Server-ID/Name fehlt! Verwende 'start <ID>'".to_string(),
33            ));
34        }
35
36        let _server_id = args[0];
37        let _browser_override = StartCommand::parse_browser_override(&args[1..]);
38
39        let config = get_config()?;
40        let ctx = crate::server::shared::get_shared_context();
41
42        // FIX: config statt config_result verwenden
43        self.start_server_sync(&config, ctx, args[0])
44    }
45
46    fn priority(&self) -> u8 {
47        66
48    }
49}
50
51impl StartCommand {
52    fn start_server_sync(
53        &self,
54        config: &Config,
55        ctx: &ServerContext,
56        identifier: &str,
57    ) -> Result<String> {
58        let server_info = {
59            let servers = ctx.servers.read().unwrap();
60            find_server(&servers, identifier)?.clone()
61        };
62
63        self.validate_and_start_server(config, ctx, server_info)
64    }
65
66    fn validate_and_start_server(
67        &self,
68        config: &Config,
69        ctx: &ServerContext,
70        server_info: crate::server::types::ServerInfo,
71    ) -> Result<String> {
72        // Enhanced status validation
73        if server_info.status == ServerStatus::Running {
74            if !is_port_available(server_info.port) {
75                // Port is occupied but not by us - correct status
76                self.update_server_status(ctx, &server_info.id, ServerStatus::Failed);
77
78                // Persistence update
79                let server_id = server_info.id.clone();
80                tokio::spawn(async move {
81                    crate::server::shared::persist_server_update(&server_id, ServerStatus::Failed)
82                        .await;
83                });
84
85                return Ok(format!(
86                    "Port {} is occupied by another process! Server status corrected to FAILED. Use different port.",
87                    server_info.port
88                ));
89            } else {
90                // Port is free but status is Running -> correct to Stopped
91                self.update_server_status(ctx, &server_info.id, ServerStatus::Stopped);
92
93                let server_id = server_info.id.clone();
94                tokio::spawn(async move {
95                    crate::server::shared::persist_server_update(&server_id, ServerStatus::Stopped)
96                        .await;
97                });
98
99                log::info!(
100                    "Corrected server status from Running to Stopped for {}",
101                    server_info.name
102                );
103            }
104        }
105
106        // Check port availability
107        // Robuste Port-Validierung
108        match crate::server::utils::port::check_port_status(server_info.port) {
109            crate::server::utils::port::PortStatus::Available => {
110                // Port ist frei - weiter
111            }
112            crate::server::utils::port::PortStatus::OccupiedByUs => {
113                // Port wird bereits von unserem Server verwendet
114                return Ok(format!(
115                    "Port {} wird bereits von Server '{}' verwendet!",
116                    server_info.port, server_info.name
117                ));
118            }
119            crate::server::utils::port::PortStatus::OccupiedByOther => {
120                // Port wird von anderem Prozess verwendet
121                return Ok(format!(
122                    "Port {} ist von anderem Prozess belegt! Verwende anderen Port.",
123                    server_info.port
124                ));
125            }
126        }
127
128        // Check server limits
129        let running_servers = {
130            let servers = ctx.servers.read().unwrap();
131            servers
132                .values()
133                .filter(|s| s.status == ServerStatus::Running)
134                .count()
135        };
136
137        if running_servers >= config.server.max_concurrent {
138            return Err(AppError::Validation(format!(
139                "Cannot start server: Running servers limit reached ({}/{}). Stop other servers first or increase max_concurrent in config.",
140                running_servers, config.server.max_concurrent
141            )));
142        }
143
144        // Validate port is within configured range
145        if server_info.port < config.server.port_range_start
146            || server_info.port > config.server.port_range_end
147        {
148            log::warn!(
149                "Server {} port {} is outside configured range {}-{}, but starting anyway",
150                server_info.name,
151                server_info.port,
152                config.server.port_range_start,
153                config.server.port_range_end
154            );
155        }
156
157        // Start the server
158        match self.spawn_server(config, ctx, server_info.clone()) {
159            Ok(handle) => {
160                {
161                    let mut handles = ctx.handles.write().unwrap();
162                    handles.insert(server_info.id.clone(), handle);
163                }
164                self.update_server_status(ctx, &server_info.id, ServerStatus::Running);
165
166                // Persistence update
167                let server_id = server_info.id.clone();
168                tokio::spawn(async move {
169                    crate::server::shared::persist_server_update(&server_id, ServerStatus::Running)
170                        .await;
171                });
172
173                let server_url = format!("http://127.0.0.1:{}", server_info.port);
174                if config.server.auto_open_browser {
175                    let url_clone = server_url.clone();
176                    let server_name_clone = server_info.name.clone();
177                    tokio::spawn(async move {
178                        tokio::time::sleep(tokio::time::Duration::from_millis(1200)).await;
179                        match opener::open(&url_clone) {
180                            Ok(_) => {
181                                log::info!(
182                                    "Browser opened for '{}': {}",
183                                    server_name_clone,
184                                    url_clone
185                                );
186                            }
187                            Err(e) => {
188                                log::warn!(
189                                    "Failed to open browser for '{}': {} (URL: {})",
190                                    server_name_clone,
191                                    e,
192                                    url_clone
193                                );
194                            }
195                        }
196                    });
197                }
198
199                Ok(format!(
200                    "Server '{}' successfully started on {} [PERSISTENT] ({}/{} running){}",
201                    server_info.name,
202                    server_url, // URL statt nur Port
203                    running_servers + 1,
204                    config.server.max_concurrent,
205                    if config.server.auto_open_browser {
206                        " - Browser opening..."
207                    } else {
208                        ""
209                    }
210                ))
211            }
212            Err(e) => {
213                self.update_server_status(ctx, &server_info.id, ServerStatus::Failed);
214
215                // Persistence update
216                let server_id = server_info.id.clone();
217                tokio::spawn(async move {
218                    crate::server::shared::persist_server_update(&server_id, ServerStatus::Failed)
219                        .await;
220                });
221
222                Err(AppError::Validation(format!("Server start failed: {}", e)))
223            }
224        }
225    }
226
227    // Updated to use config
228    fn spawn_server(
229        &self,
230        config: &Config,
231        ctx: &ServerContext,
232        server_info: crate::server::types::ServerInfo,
233    ) -> std::result::Result<actix_web::dev::ServerHandle, String> {
234        // Pass config to create_web_server
235        crate::server::handlers::web::create_web_server(ctx, server_info, config)
236    }
237
238    fn update_server_status(&self, ctx: &ServerContext, server_id: &str, status: ServerStatus) {
239        if let Ok(mut servers) = ctx.servers.write() {
240            if let Some(server) = servers.get_mut(server_id) {
241                server.status = status;
242            }
243        }
244    }
245
246    // Helper für Browser-Override Parsing
247    fn parse_browser_override(args: &[&str]) -> Option<bool> {
248        for arg in args {
249            match *arg {
250                "--no-browser" => return Some(false),
251                "--browser" => return Some(true),
252                _ => continue,
253            }
254        }
255        None
256    }
257}