rush_sync_server/server/
persistence.rs1use crate::core::prelude::*;
2use crate::server::types::{ServerInfo, ServerStatus};
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5use std::path::PathBuf;
6
7#[derive(Debug, Serialize, Deserialize, Clone)]
8pub struct PersistentServerInfo {
9 pub id: String,
10 pub name: String,
11 pub port: u16,
12 pub status: ServerStatus,
13 pub created_at: String,
14 pub created_timestamp: u64,
15 pub auto_start: bool,
16 pub last_started: Option<String>,
17 pub start_count: u32,
18}
19
20impl From<ServerInfo> for PersistentServerInfo {
21 fn from(info: ServerInfo) -> Self {
22 Self {
23 id: info.id,
24 name: info.name,
25 port: info.port,
26 status: info.status,
27 created_at: info.created_at,
28 created_timestamp: info.created_timestamp,
29 auto_start: false,
30 last_started: None,
31 start_count: 0,
32 }
33 }
34}
35
36impl From<PersistentServerInfo> for ServerInfo {
37 fn from(info: PersistentServerInfo) -> Self {
38 Self {
39 id: info.id,
40 name: info.name,
41 port: info.port,
42 status: info.status,
43 created_at: info.created_at,
44 created_timestamp: info.created_timestamp,
45 }
46 }
47}
48
49pub struct ServerRegistry {
50 file_path: PathBuf,
51}
52
53impl ServerRegistry {
54 pub fn new() -> Result<Self> {
55 let exe_path = std::env::current_exe().map_err(AppError::Io)?;
56 let base_dir = exe_path.parent().ok_or_else(|| {
57 AppError::Validation("Cannot determine executable directory".to_string())
58 })?;
59
60 let file_path = base_dir.join(".rss").join("servers.list");
61
62 if let Some(parent) = file_path.parent() {
63 std::fs::create_dir_all(parent).map_err(AppError::Io)?;
64 }
65
66 Ok(Self { file_path })
67 }
68
69 pub fn get_file_path(&self) -> &PathBuf {
70 &self.file_path
71 }
72
73 pub async fn load_servers(&self) -> Result<HashMap<String, PersistentServerInfo>> {
74 if !self.file_path.exists() {
75 return Ok(HashMap::new());
76 }
77
78 let content = tokio::fs::read_to_string(&self.file_path)
79 .await
80 .map_err(AppError::Io)?;
81 if content.trim().is_empty() {
82 return Ok(HashMap::new());
83 }
84
85 let servers: Vec<PersistentServerInfo> = serde_json::from_str(&content)
86 .map_err(|e| AppError::Validation(format!("Failed to parse server registry: {}", e)))?;
87
88 Ok(servers.into_iter().map(|s| (s.id.clone(), s)).collect())
89 }
90
91 pub async fn save_servers(
92 &self,
93 servers: &HashMap<String, PersistentServerInfo>,
94 ) -> Result<()> {
95 let mut server_list: Vec<PersistentServerInfo> = servers.values().cloned().collect();
96 server_list.sort_by(|a, b| a.created_timestamp.cmp(&b.created_timestamp));
97
98 let content = serde_json::to_string_pretty(&server_list)
99 .map_err(|e| AppError::Validation(format!("Failed to serialize servers: {}", e)))?;
100
101 let temp_path = self.file_path.with_extension("tmp");
102 tokio::fs::write(&temp_path, content)
103 .await
104 .map_err(AppError::Io)?;
105 tokio::fs::rename(&temp_path, &self.file_path)
106 .await
107 .map_err(AppError::Io)?;
108
109 Ok(())
110 }
111
112 pub async fn add_server(
113 &self,
114 mut servers: HashMap<String, PersistentServerInfo>,
115 server_info: ServerInfo,
116 ) -> Result<HashMap<String, PersistentServerInfo>> {
117 let persistent_info = PersistentServerInfo::from(server_info);
118 servers.insert(persistent_info.id.clone(), persistent_info);
119 self.save_servers(&servers).await?;
120 Ok(servers)
121 }
122
123 pub async fn remove_server(
124 &self,
125 mut servers: HashMap<String, PersistentServerInfo>,
126 server_id: &str,
127 ) -> Result<HashMap<String, PersistentServerInfo>> {
128 servers.remove(server_id);
129 self.save_servers(&servers).await?;
130 Ok(servers)
131 }
132
133 pub async fn update_server_status(
134 &self,
135 mut servers: HashMap<String, PersistentServerInfo>,
136 server_id: &str,
137 status: ServerStatus,
138 ) -> Result<HashMap<String, PersistentServerInfo>> {
139 if let Some(server) = servers.get_mut(server_id) {
140 server.status = status;
141 if status == ServerStatus::Running {
142 server.last_started =
143 Some(chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string());
144 server.start_count += 1;
145 }
146 }
147 self.save_servers(&servers).await?;
148 Ok(servers)
149 }
150
151 pub async fn cleanup_servers(
152 &self,
153 mut servers: HashMap<String, PersistentServerInfo>,
154 cleanup_type: CleanupType,
155 ) -> Result<(HashMap<String, PersistentServerInfo>, usize)> {
156 let initial_count = servers.len();
157
158 match cleanup_type {
159 CleanupType::Stopped => servers.retain(|_, s| s.status != ServerStatus::Stopped),
160 CleanupType::Failed => servers.retain(|_, s| s.status != ServerStatus::Failed),
161 CleanupType::All => servers.retain(|_, s| s.status == ServerStatus::Running),
162 }
163
164 let removed_count = initial_count - servers.len();
165 if removed_count > 0 {
166 self.save_servers(&servers).await?;
167 }
168
169 Ok((servers, removed_count))
170 }
171
172 pub fn get_auto_start_servers(
173 &self,
174 servers: &HashMap<String, PersistentServerInfo>,
175 ) -> Vec<PersistentServerInfo> {
176 servers
177 .values()
178 .filter(|s| s.auto_start && s.status != ServerStatus::Failed)
179 .cloned()
180 .collect()
181 }
182
183 pub async fn set_auto_start(
184 &self,
185 mut servers: HashMap<String, PersistentServerInfo>,
186 server_id: &str,
187 auto_start: bool,
188 ) -> Result<HashMap<String, PersistentServerInfo>> {
189 if let Some(server) = servers.get_mut(server_id) {
190 server.auto_start = auto_start;
191 self.save_servers(&servers).await?;
192 }
193 Ok(servers)
194 }
195}
196
197#[derive(Debug)]
198pub enum CleanupType {
199 Stopped,
200 Failed,
201 All,
202}