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 std::future::Future;
8use std::pin::Pin;
9
10#[derive(Debug, Default)]
11pub struct StartCommand;
12
13impl StartCommand {
14    pub fn new() -> Self {
15        Self
16    }
17}
18
19impl Command for StartCommand {
20    fn name(&self) -> &'static str {
21        "start"
22    }
23    fn description(&self) -> &'static str {
24        "Start a web server (persistent)"
25    }
26    fn matches(&self, command: &str) -> bool {
27        command.trim().to_lowercase().starts_with("start")
28    }
29
30    fn execute_sync(&self, args: &[&str]) -> Result<String> {
31        if args.is_empty() {
32            return Err(AppError::Validation(
33                "Server-ID/Name fehlt! Verwende 'start <ID>'".to_string(),
34            ));
35        }
36
37        // DON'T use block_on - instead use spawn_blocking for config loading
38        let config_result = std::thread::spawn(|| {
39            let rt = tokio::runtime::Runtime::new().unwrap();
40            rt.block_on(Config::load())
41        })
42        .join()
43        .map_err(|_| AppError::Validation("Failed to load config".to_string()))??;
44
45        let ctx = crate::server::shared::get_shared_context();
46
47        self.start_server_sync(&config_result, ctx, args[0])
48    }
49
50    fn execute_async<'a>(
51        &'a self,
52        args: &'a [&'a str],
53    ) -> Pin<Box<dyn Future<Output = Result<String>> + Send + 'a>> {
54        Box::pin(async move {
55            if args.is_empty() {
56                return Err(AppError::Validation(
57                    "Server-ID/Name fehlt! Verwende 'start <ID>'".to_string(),
58                ));
59            }
60
61            let config = Config::load().await?;
62            let ctx = crate::server::shared::get_shared_context();
63
64            self.start_server_async(&config, ctx, args[0]).await
65        })
66    }
67
68    fn supports_async(&self) -> bool {
69        true
70    }
71    fn priority(&self) -> u8 {
72        66
73    }
74}
75
76impl StartCommand {
77    fn start_server_sync(
78        &self,
79        config: &Config,
80        ctx: &ServerContext,
81        identifier: &str,
82    ) -> Result<String> {
83        let server_info = {
84            let servers = ctx.servers.read().unwrap();
85            find_server(&servers, identifier)?.clone()
86        };
87
88        self.validate_and_start_server(config, ctx, server_info)
89    }
90
91    async fn start_server_async(
92        &self,
93        config: &Config,
94        ctx: &ServerContext,
95        identifier: &str,
96    ) -> Result<String> {
97        let server_info = {
98            let servers = ctx.servers.read().unwrap();
99            find_server(&servers, identifier)?.clone()
100        };
101
102        self.validate_and_start_server(config, ctx, server_info)
103    }
104
105    fn validate_and_start_server(
106        &self,
107        config: &Config,
108        ctx: &ServerContext,
109        server_info: crate::server::types::ServerInfo,
110    ) -> Result<String> {
111        // Enhanced status validation
112        if server_info.status == ServerStatus::Running {
113            if !is_port_available(server_info.port) {
114                // Port is occupied but not by us - correct status
115                self.update_server_status(ctx, &server_info.id, ServerStatus::Failed);
116
117                // Persistence update
118                let server_id = server_info.id.clone();
119                tokio::spawn(async move {
120                    crate::server::shared::persist_server_update(&server_id, ServerStatus::Failed)
121                        .await;
122                });
123
124                return Ok(format!(
125                    "Port {} is occupied by another process! Server status corrected to FAILED. Use different port.",
126                    server_info.port
127                ));
128            } else {
129                // Port is free but status is Running -> correct to Stopped
130                self.update_server_status(ctx, &server_info.id, ServerStatus::Stopped);
131
132                let server_id = server_info.id.clone();
133                tokio::spawn(async move {
134                    crate::server::shared::persist_server_update(&server_id, ServerStatus::Stopped)
135                        .await;
136                });
137
138                log::info!(
139                    "Corrected server status from Running to Stopped for {}",
140                    server_info.name
141                );
142            }
143        }
144
145        // Check port availability
146        if !is_port_available(server_info.port) {
147            return Ok(format!("Port {} already occupied!", server_info.port));
148        }
149
150        // Check server limits
151        let running_servers = {
152            let servers = ctx.servers.read().unwrap();
153            servers
154                .values()
155                .filter(|s| s.status == ServerStatus::Running)
156                .count()
157        };
158
159        if running_servers >= config.server.max_concurrent {
160            return Err(AppError::Validation(format!(
161                "Cannot start server: Running servers limit reached ({}/{}). Stop other servers first or increase max_concurrent in config.",
162                running_servers, config.server.max_concurrent
163            )));
164        }
165
166        // Validate port is within configured range
167        if server_info.port < config.server.port_range_start
168            || server_info.port > config.server.port_range_end
169        {
170            log::warn!(
171                "Server {} port {} is outside configured range {}-{}, but starting anyway",
172                server_info.name,
173                server_info.port,
174                config.server.port_range_start,
175                config.server.port_range_end
176            );
177        }
178
179        // Start the server
180        match self.spawn_server(config, ctx, server_info.clone()) {
181            Ok(handle) => {
182                {
183                    let mut handles = ctx.handles.write().unwrap();
184                    handles.insert(server_info.id.clone(), handle);
185                }
186                self.update_server_status(ctx, &server_info.id, ServerStatus::Running);
187
188                // Persistence update
189                let server_id = server_info.id.clone();
190                tokio::spawn(async move {
191                    crate::server::shared::persist_server_update(&server_id, ServerStatus::Running)
192                        .await;
193                });
194
195                Ok(format!(
196                    "Server '{}' successfully started on http://127.0.0.1:{} [PERSISTENT] ({}/{} running)",
197                    server_info.name,
198                    server_info.port,
199                    running_servers + 1,
200                    config.server.max_concurrent
201                ))
202            }
203            Err(e) => {
204                self.update_server_status(ctx, &server_info.id, ServerStatus::Failed);
205
206                // Persistence update
207                let server_id = server_info.id.clone();
208                tokio::spawn(async move {
209                    crate::server::shared::persist_server_update(&server_id, ServerStatus::Failed)
210                        .await;
211                });
212
213                Err(AppError::Validation(format!("Server start failed: {}", e)))
214            }
215        }
216    }
217
218    // Updated to use config
219    fn spawn_server(
220        &self,
221        config: &Config,
222        ctx: &ServerContext,
223        server_info: crate::server::types::ServerInfo,
224    ) -> std::result::Result<actix_web::dev::ServerHandle, String> {
225        // Pass config to create_web_server
226        crate::server::handlers::web::create_web_server(ctx, server_info, config)
227    }
228
229    fn update_server_status(&self, ctx: &ServerContext, server_id: &str, status: ServerStatus) {
230        if let Ok(mut servers) = ctx.servers.write() {
231            if let Some(server) = servers.get_mut(server_id) {
232                server.status = status;
233            }
234        }
235    }
236}