1use crate::core::config::Config;
2use crate::proxy::ProxyManager;
3use crate::server::persistence::ServerRegistry;
4use crate::server::types::{ServerContext, ServerStatus};
5use crate::server::utils::port::is_port_available;
6use std::sync::{Arc, OnceLock};
7
8static SHARED_CONTEXT: OnceLock<ServerContext> = OnceLock::new();
9static PERSISTENT_REGISTRY: OnceLock<ServerRegistry> = OnceLock::new();
10static PROXY_MANAGER: OnceLock<Arc<ProxyManager>> = OnceLock::new();
11
12pub fn get_shared_context() -> &'static ServerContext {
13 SHARED_CONTEXT.get_or_init(ServerContext::default)
14}
15
16pub fn get_persistent_registry() -> &'static ServerRegistry {
17 PERSISTENT_REGISTRY
18 .get_or_init(|| ServerRegistry::new().expect("Failed to initialize server registry"))
19}
20
21pub fn get_proxy_manager() -> &'static Arc<ProxyManager> {
22 PROXY_MANAGER.get_or_init(|| {
23 let config = tokio::task::block_in_place(|| {
25 tokio::runtime::Handle::current().block_on(async {
26 crate::core::config::Config::load()
27 .await
28 .unwrap_or_default()
29 })
30 });
31
32 Arc::new(ProxyManager::new(config.proxy))
33 })
34}
35
36async fn start_proxy_system(config: &Config) -> crate::core::error::Result<()> {
38 if !config.proxy.enabled {
39 log::info!("Proxy system disabled in config");
40 return Ok(());
41 }
42
43 let proxy_manager = get_proxy_manager();
44
45 Arc::clone(proxy_manager).start_proxy_server().await?;
47
48 log::info!("Proxy system started:");
49 log::info!(" HTTP: http://{{name}}.localhost:{}", config.proxy.port);
50
51 let https_port = config.proxy.port + config.proxy.https_port_offset;
52 log::info!("HTTPS: https://{{name}}.localhost:{}", https_port);
53
54 Ok(())
55}
56
57pub async fn initialize_server_system() -> crate::core::error::Result<()> {
94 let config = Config::load().await?;
95
96 crate::server::handlers::web::set_global_config(config.clone());
97
98 let registry = get_persistent_registry();
99 let context = get_shared_context();
100
101 let mut persistent_servers = registry.load_servers().await?;
102 let mut corrected_servers = 0;
103
104 for (_server_id, persistent_info) in persistent_servers.iter_mut() {
105 if persistent_info.status == ServerStatus::Running {
106 if !is_port_available(persistent_info.port) {
107 log::warn!(
108 "Server {} claims to be running on port {}, but port is occupied",
109 persistent_info.name,
110 persistent_info.port
111 );
112 persistent_info.status = ServerStatus::Failed;
113 corrected_servers += 1;
114 } else {
115 log::info!(
116 "Server {} was running but is no longer active, correcting status",
117 persistent_info.name
118 );
119 persistent_info.status = ServerStatus::Stopped;
120 corrected_servers += 1;
121 }
122 }
123 }
124
125 if corrected_servers > 0 {
126 registry.save_servers(&persistent_servers).await?;
127 log::info!(
128 "Corrected {} server statuses after program restart",
129 corrected_servers
130 );
131 }
132
133 {
134 let mut servers = context.servers.write().unwrap();
135 servers.clear();
136 for (id, persistent_info) in persistent_servers.iter() {
137 let server_info = crate::server::types::ServerInfo::from(persistent_info.clone());
138 servers.insert(id.clone(), server_info);
139 }
140 }
141
142 log::info!(
143 "Server system initialized with {} persistent servers",
144 persistent_servers.len()
145 );
146 log::info!(
147 "Server Config: Port Range {}-{}, Max Concurrent: {}, Workers: {}",
148 config.server.port_range_start,
149 config.server.port_range_end,
150 config.server.max_concurrent,
151 config.server.workers
152 );
153 log::info!(
154 "Logging Config: Max Size {}MB, Archives: {}, Compression: {}, Request Logging: {}",
155 config.logging.max_file_size_mb,
156 config.logging.max_archive_files,
157 config.logging.compress_archives,
158 config.logging.log_requests
159 );
160
161 let auto_start_servers = registry.get_auto_start_servers(&persistent_servers);
162 if !auto_start_servers.is_empty() {
163 log::info!(
164 "Found {} servers marked for auto-start",
165 auto_start_servers.len()
166 );
167
168 if auto_start_servers.len() > config.server.max_concurrent {
169 log::warn!(
170 "Auto-start servers ({}) exceed max_concurrent ({}), some will be skipped",
171 auto_start_servers.len(),
172 config.server.max_concurrent
173 );
174 }
175 }
176
177 if config.proxy.enabled {
179 if let Err(e) = start_proxy_system(&config).await {
181 log::error!("Failed to start proxy system: {}", e);
182 } else {
184 if let Err(e) = start_http_redirect_server(&config).await {
186 log::warn!("Failed to start HTTP redirect: {}", e);
187 }
189
190 let proxy_manager = get_proxy_manager();
192 for (_id, persistent_info) in persistent_servers.iter() {
193 if persistent_info.status == ServerStatus::Running {
194 if let Err(e) = proxy_manager
195 .add_route(
196 &persistent_info.name,
197 &persistent_info.id,
198 persistent_info.port,
199 )
200 .await
201 {
202 log::error!(
203 "Failed to register server {} with proxy: {}",
204 persistent_info.name,
205 e
206 );
207 } else {
208 log::info!(
209 "Registered existing server {} with proxy",
210 persistent_info.name
211 );
212 }
213 }
214 }
215 }
216
217 if is_port_available(80) {
218 log::info!(" With sudo: http://{{name}}.localhost → redirects to HTTPS");
219 }
220 log::info!(" Add to /etc/hosts: 127.0.0.1 {{name}}.localhost");
221 } else {
222 log::info!("Reverse Proxy disabled in configuration");
223 }
224
225 Ok(())
226}
227
228pub async fn persist_server_update(server_id: &str, status: crate::server::types::ServerStatus) {
229 let registry = get_persistent_registry();
230 if let Err(e) = registry.update_server_status(server_id, status).await {
231 log::error!("Failed to persist server status update: {}", e);
232 }
233}
234
235pub async fn shutdown_all_servers_on_exit() -> crate::core::error::Result<()> {
236 let config = Config::load().await.unwrap_or_default();
237 let registry = get_persistent_registry();
238 let context = get_shared_context();
239
240 let server_handles: Vec<_> = {
241 let mut handles = context.handles.write().unwrap();
242 handles.drain().collect()
243 };
244
245 log::info!("Shutting down {} active servers...", server_handles.len());
246
247 let shutdown_timeout = std::time::Duration::from_secs(config.server.shutdown_timeout);
248
249 for (server_id, handle) in server_handles {
250 log::info!("Stopping server {}", server_id);
251
252 if tokio::time::timeout(shutdown_timeout, handle.stop(true))
253 .await
254 .is_err()
255 {
256 log::warn!("Server {} shutdown timeout, forcing stop", server_id);
257 handle.stop(false).await;
258 }
259
260 let _ = registry
262 .update_server_status(&server_id, ServerStatus::Stopped)
263 .await;
264 }
265
266 log::info!("Server system shutdown complete");
267 Ok(())
268}
269
270pub async fn validate_server_creation(
271 name: &str,
272 port: Option<u16>,
273) -> crate::core::error::Result<()> {
274 let config = Config::load().await?;
275 let context = get_shared_context();
276 let servers = context.servers.read().unwrap();
277
278 if servers.len() >= config.server.max_concurrent {
279 return Err(crate::core::error::AppError::Validation(format!(
280 "Server limit reached: {}/{}. Use 'cleanup' command to remove stopped servers.",
281 servers.len(),
282 config.server.max_concurrent
283 )));
284 }
285
286 if let Some(port) = port {
287 if port < config.server.port_range_start || port > config.server.port_range_end {
288 return Err(crate::core::error::AppError::Validation(format!(
289 "Port {} outside configured range {}-{}",
290 port, config.server.port_range_start, config.server.port_range_end
291 )));
292 }
293 }
294
295 if servers.values().any(|s| s.name == name) {
296 return Err(crate::core::error::AppError::Validation(format!(
297 "Server name '{}' already exists",
298 name
299 )));
300 }
301
302 Ok(())
303}
304
305pub async fn get_server_system_stats() -> serde_json::Value {
306 let config = Config::load().await.unwrap_or_default();
307 let context = get_shared_context();
308 let servers = context.servers.read().unwrap();
309
310 let running_count = servers
311 .values()
312 .filter(|s| s.status == ServerStatus::Running)
313 .count();
314 let stopped_count = servers
315 .values()
316 .filter(|s| s.status == ServerStatus::Stopped)
317 .count();
318 let failed_count = servers
319 .values()
320 .filter(|s| s.status == ServerStatus::Failed)
321 .count();
322
323 serde_json::json!({
324 "total_servers": servers.len(),
325 "running": running_count,
326 "stopped": stopped_count,
327 "failed": failed_count,
328 "max_concurrent": config.server.max_concurrent,
329 "utilization_percent": (servers.len() as f64 / config.server.max_concurrent as f64 * 100.0),
330 "port_range": format!("{}-{}", config.server.port_range_start, config.server.port_range_end),
331 "available_ports": config.server.port_range_end - config.server.port_range_start + 1,
332 "proxy": {
333 "enabled": config.proxy.enabled,
334 "http_port": config.proxy.port,
335 "https_port": 8443,
336 "redirect_port": if is_port_available(80) { None } else { Some(80) }
337 },
338 "config": {
339 "workers_per_server": config.server.workers,
340 "shutdown_timeout_sec": config.server.shutdown_timeout,
341 "startup_delay_ms": config.server.startup_delay_ms,
342 "logging": {
343 "max_file_size_mb": config.logging.max_file_size_mb,
344 "max_archives": config.logging.max_archive_files,
345 "compression": config.logging.compress_archives,
346 "request_logging": config.logging.log_requests,
347 "security_alerts": config.logging.log_security_alerts,
348 "performance_monitoring": config.logging.log_performance
349 }
350 }
351 })
352}
353
354pub async fn auto_start_servers() -> crate::core::error::Result<Vec<String>> {
355 let config = Config::load().await?;
356 let registry = get_persistent_registry();
357 let auto_start_servers = {
358 let servers = registry.load_servers().await?;
359 registry.get_auto_start_servers(&servers)
360 };
361
362 if auto_start_servers.is_empty() {
363 return Ok(vec![]);
364 }
365
366 let max_to_start = config.server.max_concurrent.min(auto_start_servers.len());
367 let mut started_servers = Vec::new();
368
369 for server in auto_start_servers.iter().take(max_to_start) {
370 log::info!(
371 "Auto-starting server: {} on port {}",
372 server.name,
373 server.port
374 );
375 started_servers.push(format!("{}:{}", server.name, server.port));
376 }
377
378 if auto_start_servers.len() > max_to_start {
379 log::warn!(
380 "Skipped {} auto-start servers due to max_concurrent limit of {}",
381 auto_start_servers.len() - max_to_start,
382 config.server.max_concurrent
383 );
384 }
385
386 Ok(started_servers)
387}
388
389async fn start_http_redirect_server(_config: &Config) -> crate::core::error::Result<()> {
393 let redirect_port = 80;
394 let target_https_port = 8443;
395
396 if !crate::server::utils::port::is_port_available(redirect_port) {
397 log::warn!(
398 "Port {} already in use - HTTP redirect disabled",
399 redirect_port
400 );
401 return Ok(());
402 }
403
404 log::info!(
405 "Starting HTTP→HTTPS redirect server on port {}",
406 redirect_port
407 );
408
409 std::thread::spawn(move || {
411 let rt = tokio::runtime::Builder::new_current_thread()
413 .enable_all()
414 .build()
415 .expect("Failed to build single-thread runtime for redirect server");
416
417 rt.block_on(async move {
418 let redirect_server =
419 crate::server::redirect::HttpRedirectServer::new(redirect_port, target_https_port);
420
421 if let Err(e) = redirect_server.run().await {
422 log::error!("HTTP redirect server error: {}", e);
423 }
424 });
425 });
426
427 tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
429 log::info!(
430 "HTTP redirect active: Port {} → HTTPS Port {}",
431 redirect_port,
432 target_https_port
433 );
434
435 Ok(())
436}