rush_sync_server/commands/stop/
command.rs1use crate::commands::command::Command;
2use crate::core::prelude::*;
3use crate::server::types::{ServerContext, ServerStatus};
4use crate::server::utils::validation::find_server;
5use std::future::Future;
6use std::pin::Pin;
7use std::time::Duration;
8use tokio::time::timeout;
9
10#[derive(Debug, Default)]
11pub struct StopCommand;
12
13impl StopCommand {
14 pub fn new() -> Self {
15 Self
16 }
17}
18
19impl Command for StopCommand {
20 fn name(&self) -> &'static str {
21 "stop"
22 }
23
24 fn description(&self) -> &'static str {
25 "Stop a web server"
26 }
27
28 fn matches(&self, command: &str) -> bool {
29 command.trim().to_lowercase().starts_with("stop")
30 }
31
32 fn execute_sync(&self, args: &[&str]) -> Result<String> {
33 if args.is_empty() {
34 return Err(AppError::Validation(
35 "Server-ID/Name fehlt! Verwende 'stop <ID>'".to_string(),
36 ));
37 }
38
39 let ctx = crate::server::shared::get_shared_context();
40 self.stop_server(ctx, args[0])
41 }
42
43 fn execute_async<'a>(
44 &'a self,
45 args: &'a [&'a str],
46 ) -> Pin<Box<dyn Future<Output = Result<String>> + Send + 'a>> {
47 Box::pin(async move { self.execute_sync(args) })
48 }
49
50 fn supports_async(&self) -> bool {
51 true
52 }
53
54 fn priority(&self) -> u8 {
55 67
56 }
57}
58
59impl StopCommand {
60 fn stop_server(&self, ctx: &ServerContext, identifier: &str) -> Result<String> {
61 let server_info = {
62 let servers = ctx.servers.read().unwrap();
63 find_server(&servers, identifier)?.clone()
64 };
65
66 if server_info.status != ServerStatus::Running {
67 return Ok(format!(
68 "Server '{}' ist nicht aktiv (Status: {})",
69 server_info.name, server_info.status
70 ));
71 }
72
73 log::info!(
74 "Stopping server {} on port {}",
75 server_info.id,
76 server_info.port
77 );
78
79 let handle_removed = {
80 let mut handles = ctx.handles.write().unwrap();
81 handles.remove(&server_info.id)
82 };
83
84 if let Some(handle) = handle_removed {
85 self.shutdown_server_gracefully(handle, server_info.id.clone());
86 self.update_server_status(ctx, &server_info.id, ServerStatus::Stopped);
87 std::thread::sleep(Duration::from_millis(1000));
88 Ok(format!("Server '{}' gestoppt", server_info.name))
89 } else {
90 self.update_server_status(ctx, &server_info.id, ServerStatus::Stopped);
91 Ok(format!(
92 "Server '{}' war bereits gestoppt",
93 server_info.name
94 ))
95 }
96 }
97
98 fn shutdown_server_gracefully(&self, handle: actix_web::dev::ServerHandle, server_id: String) {
99 std::thread::spawn(move || {
100 let rt = tokio::runtime::Runtime::new().unwrap();
101 rt.block_on(async move {
102 match timeout(Duration::from_secs(3), handle.stop(true)).await {
103 Ok(_) => log::info!("Server {} stopped gracefully", server_id),
104 Err(_) => {
105 log::warn!("Server {} shutdown timeout, forcing stop", server_id);
106 handle.stop(false).await;
107 }
108 }
109 });
110 });
111 }
112
113 fn update_server_status(&self, ctx: &ServerContext, server_id: &str, status: ServerStatus) {
114 if let Ok(mut servers) = ctx.servers.write() {
115 if let Some(server) = servers.get_mut(server_id) {
116 server.status = status;
117 }
118 }
119 }
120}