rush_sync_server/commands/cleanup/
command.rs1use crate::commands::command::Command;
2use crate::core::prelude::*;
3use crate::server::types::{ServerContext, ServerStatus};
4use std::time::Duration;
5use tokio::time::timeout;
6
7#[derive(Debug, Default)]
8pub struct CleanupCommand;
9
10impl CleanupCommand {
11 pub fn new() -> Self {
12 Self
13 }
14}
15
16impl Command for CleanupCommand {
17 fn name(&self) -> &'static str {
18 "cleanup"
19 }
20 fn description(&self) -> &'static str {
21 "Clean up stopped or failed servers (persistent)"
22 }
23 fn matches(&self, command: &str) -> bool {
24 command.trim().to_lowercase().starts_with("cleanup")
25 }
26
27 fn execute_sync(&self, args: &[&str]) -> Result<String> {
28 let ctx = crate::server::shared::get_shared_context();
29
30 match args.first() {
31 Some(&"failed") => Ok(self.cleanup_failed_servers(ctx)),
32 Some(&"stopped") | None => Ok(self.cleanup_stopped_servers(ctx)),
33 Some(&"logs") => {
34 tokio::spawn(async move {
35 match Self::cleanup_all_server_logs().await {
36 Ok(msg) => log::info!("Log cleanup result: {}", msg),
37 Err(e) => log::error!("Log cleanup failed: {}", e),
38 }
39 });
40 Ok("Server-Log-Bereinigung gestartet...".to_string())
41 }
42 Some(&"all") => {
43 let stopped = self.cleanup_stopped_servers(ctx);
44 let failed = self.cleanup_failed_servers(ctx);
45
46 tokio::spawn(async move {
47 match Self::cleanup_all_server_logs().await {
48 Ok(msg) => log::info!("Log cleanup result: {}", msg),
49 Err(e) => log::error!("Log cleanup failed: {}", e),
50 }
51 });
52
53 Ok(format!(
54 "{}\n{}\nServer-Logs werden bereinigt...",
55 stopped, failed
56 ))
57 }
58 _ => Err(AppError::Validation(
59 "Usage: cleanup [stopped|failed|logs|all]".to_string(),
60 )),
61 }
62 }
63
64 fn priority(&self) -> u8 {
65 50
66 }
67}
68
69impl CleanupCommand {
70 fn cleanup_stopped_servers(&self, ctx: &ServerContext) -> String {
71 let registry = crate::server::shared::get_persistent_registry();
72
73 tokio::spawn(async move {
75 if let Ok(_servers) = registry.load_servers().await {
76 if let Ok((_updated_servers, removed_count)) = registry
77 .cleanup_servers(crate::server::persistence::CleanupType::Stopped)
78 .await
79 {
80 if removed_count > 0 {
81 log::info!(
82 "Removed {} stopped servers from persistent registry",
83 removed_count
84 );
85 }
86 }
87 }
88 });
89
90 let mut servers = ctx.servers.write().unwrap();
92 let initial_count = servers.len();
93 servers.retain(|_, server| server.status != ServerStatus::Stopped);
94 let removed_count = initial_count - servers.len();
95
96 if removed_count > 0 {
97 format!(
98 "{} gestoppte Server entfernt (persistent gespeichert)",
99 removed_count
100 )
101 } else {
102 "Keine gestoppten Server zum Entfernen gefunden".to_string()
103 }
104 }
105
106 fn cleanup_failed_servers(&self, ctx: &ServerContext) -> String {
107 let registry = crate::server::shared::get_persistent_registry();
108
109 tokio::spawn(async move {
111 if let Ok(_servers) = registry.load_servers().await {
112 if let Ok((_updated_servers, removed_count)) = registry
113 .cleanup_servers(crate::server::persistence::CleanupType::Stopped)
114 .await
115 {
116 if removed_count > 0 {
117 log::info!(
118 "Removed {} failed servers from persistent registry",
119 removed_count
120 );
121 }
122 }
123 }
124 });
125
126 let mut servers = ctx.servers.write().unwrap();
128 let initial_count = servers.len();
129 servers.retain(|_, server| server.status != ServerStatus::Failed);
130 let removed_count = initial_count - servers.len();
131
132 if removed_count > 0 {
133 format!(
134 "{} fehlgeschlagene Server entfernt (persistent gespeichert)",
135 removed_count
136 )
137 } else {
138 "Keine fehlgeschlagenen Server zum Entfernen gefunden".to_string()
139 }
140 }
141
142 pub async fn shutdown_all_servers(&self, ctx: &ServerContext) -> Result<()> {
143 let handles: Vec<_> = {
144 let mut handles_guard = ctx.handles.write().unwrap();
145 handles_guard.drain().collect()
146 };
147
148 let shutdown_futures: Vec<_> = handles
149 .into_iter()
150 .map(|(id, handle)| async move {
151 match timeout(Duration::from_secs(5), handle.stop(true)).await {
152 Ok(_) => log::info!("Server {} stopped", id),
153 Err(_) => {
154 log::warn!("Force stopping server {}", id);
155 handle.stop(false).await;
156 }
157 }
158 })
159 .collect();
160
161 futures::future::join_all(shutdown_futures).await;
162
163 {
164 let mut servers = ctx.servers.write().unwrap();
165 for server in servers.values_mut() {
166 server.status = ServerStatus::Stopped;
167 }
168 }
169
170 let registry = crate::server::shared::get_persistent_registry();
172 if let Ok(mut persistent_servers) = registry.load_servers().await {
173 for server in persistent_servers.values_mut() {
174 server.status = ServerStatus::Stopped;
175 }
176 let _ = registry.save_servers(&persistent_servers).await;
177 }
178
179 Ok(())
180 }
181
182 pub async fn cleanup_all_server_logs() -> Result<String> {
183 let exe_path = std::env::current_exe().map_err(AppError::Io)?;
184 let base_dir = exe_path.parent().ok_or_else(|| {
185 AppError::Validation("Cannot determine executable directory".to_string())
186 })?;
187
188 let servers_dir = base_dir.join(".rss").join("servers");
189
190 if !servers_dir.exists() {
191 return Ok("Kein servers/ Verzeichnis gefunden".to_string());
192 }
193
194 let mut deleted_files = 0;
195 let mut total_size = 0u64;
196
197 let mut entries = tokio::fs::read_dir(&servers_dir)
198 .await
199 .map_err(AppError::Io)?;
200
201 while let Some(entry) = entries.next_entry().await.map_err(AppError::Io)? {
202 let path = entry.path();
203
204 if path.is_file() {
205 if let Some(extension) = path.extension() {
206 if extension == "log" || extension == "gz" {
207 if let Ok(metadata) = tokio::fs::metadata(&path).await {
208 total_size += metadata.len();
209 }
210
211 tokio::fs::remove_file(&path).await.map_err(AppError::Io)?;
212 deleted_files += 1;
213
214 log::info!("Deleted log file: {}", path.display());
215 }
216 }
217 }
218 }
219
220 let size_mb = total_size / (1024 * 1024);
221
222 Ok(format!(
223 "Server-Logs bereinigt: {} Dateien gelöscht, {}MB freigegeben",
224 deleted_files, size_mb
225 ))
226 }
227}