1use std::sync::Arc;
2use crate::{YrsManager, SyncService};
3use crate::sync_service::{RoomInfo, RoomStatus};
4use warp::ws::{WebSocket, Ws};
5use warp::{Filter, Rejection, Reply};
6use yrs_warp::broadcast::BroadcastGroup;
7use yrs_warp::ws::{WarpSink, WarpStream};
8use tokio::sync::Mutex;
9use futures_util::StreamExt;
10use serde_json::json;
11
12#[derive(Debug)]
14pub struct RoomNotFoundError {
15 room_id: String,
16}
17
18impl warp::reject::Reject for RoomNotFoundError {}
19
20impl RoomNotFoundError {
21 pub fn new(room_id: String) -> Self {
22 Self { room_id }
23 }
24
25 pub fn room_id(&self) -> &str {
26 &self.room_id
27 }
28}
29
30#[derive(Clone)]
32pub struct CollaborationServer {
33 yrs_manager: Arc<YrsManager>,
34 sync_service: Arc<SyncService>,
35 port: u16,
36}
37
38impl CollaborationServer {
39 pub fn new(
40 yrs_manager: Arc<YrsManager>,
41 port: u16,
42 ) -> Self {
43 let sync_service = Arc::new(SyncService::new(yrs_manager.clone()));
44 Self { yrs_manager, sync_service, port }
45 }
46
47 pub fn with_sync_service(
49 yrs_manager: Arc<YrsManager>,
50 sync_service: Arc<SyncService>,
51 port: u16,
52 ) -> Self {
53 Self { yrs_manager, sync_service, port }
54 }
55
56 pub async fn handle_rejection(
58 err: Rejection
59 ) -> Result<impl Reply, std::convert::Infallible> {
60 if let Some(room_error) = err.find::<RoomNotFoundError>() {
61 let error_response = json!({
62 "error": "ROOM_NOT_FOUND",
63 "message": format!("房间 '{}' 不存在", room_error.room_id()),
64 "room_id": room_error.room_id(),
65 "code": 404
66 });
67
68 let reply = warp::reply::with_status(
69 warp::reply::json(&error_response),
70 warp::http::StatusCode::NOT_FOUND,
71 );
72
73 return Ok(reply.into_response());
74 }
75
76 if err.is_not_found() {
78 let error_response = json!({
79 "error": "NOT_FOUND",
80 "message": "请求的资源不存在",
81 "code": 404
82 });
83
84 let reply = warp::reply::with_status(
85 warp::reply::json(&error_response),
86 warp::http::StatusCode::NOT_FOUND,
87 );
88
89 return Ok(reply.into_response());
90 }
91
92 let error_response = json!({
94 "error": "INTERNAL_SERVER_ERROR",
95 "message": "服务器内部错误",
96 "code": 500
97 });
98
99 let reply = warp::reply::with_status(
100 warp::reply::json(&error_response),
101 warp::http::StatusCode::INTERNAL_SERVER_ERROR,
102 );
103
104 Ok(reply.into_response())
105 }
106
107
108 pub async fn offline_room(
114 &self,
115 room_id: &str,
116 save_data: bool,
117 ) -> crate::Result<bool> {
118 tracing::info!("🔄 开始下线房间: {}", room_id);
119
120 let room_status = self.sync_service.get_room_status(room_id).await;
122 match room_status {
123 RoomStatus::NotExists => {
124 tracing::warn!("❌ 尝试下线不存在的房间: {}", room_id);
125 return Ok(false);
126 },
127 RoomStatus::Offline => {
128 tracing::info!("ℹ️ 房间 {} 已经下线", room_id);
129 return Ok(true);
130 },
131 _ => {},
132 }
133
134 if let Some(room_info) = self.sync_service.get_room_info(room_id).await
136 {
137 tracing::info!(
138 "📊 房间信息 - 节点数: {}, 客户端数: {}",
139 room_info.node_count,
140 room_info.client_count
141 );
142 }
143
144 match self.sync_service.offline_room(room_id, save_data).await {
146 Ok(snapshot) => {
147 if let Some(_snapshot) = snapshot {
148 tracing::info!("💾 房间 {} 数据已保存", room_id);
149 }
150 tracing::info!("✅ 房间 {} 成功下线", room_id);
151 Ok(true)
152 },
153 Err(e) => {
154 tracing::error!("❌ 房间 {} 下线失败: {}", room_id, e);
155 Err(e)
156 },
157 }
158 }
159
160 pub async fn force_offline_room(
162 &self,
163 room_id: &str,
164 ) -> crate::Result<bool> {
165 tracing::warn!("⚠️ 强制下线房间: {}", room_id);
166
167 match self.sync_service.force_offline_room(room_id).await {
168 Ok(success) => {
169 if success {
170 tracing::info!("✅ 房间 {} 强制下线成功", room_id);
171 } else {
172 tracing::error!("❌ 房间 {} 强制下线失败", room_id);
173 }
174 Ok(success)
175 },
176 Err(e) => {
177 tracing::error!(
178 "❌ 强制下线房间 {} 时发生错误: {}",
179 room_id,
180 e
181 );
182 Err(e)
183 },
184 }
185 }
186
187 pub async fn offline_rooms(
189 &self,
190 room_ids: &[String],
191 save_data: bool,
192 ) -> crate::Result<Vec<(String, bool)>> {
193 tracing::info!("🔄 批量下线 {} 个房间", room_ids.len());
194
195 let mut results = Vec::new();
196
197 for room_id in room_ids {
198 match self.offline_room(room_id, save_data).await {
199 Ok(success) => results.push((room_id.clone(), success)),
200 Err(e) => {
201 tracing::error!("❌ 下线房间 {} 失败: {}", room_id, e);
202 results.push((room_id.clone(), false));
203 },
204 }
205 }
206
207 let successful_count =
208 results.iter().filter(|(_, success)| *success).count();
209 tracing::info!(
210 "📊 批量下线完成: {}/{} 个房间成功下线",
211 successful_count,
212 room_ids.len()
213 );
214
215 Ok(results)
216 }
217
218 pub fn get_active_rooms(&self) -> Vec<String> {
220 self.sync_service.get_active_rooms()
221 }
222
223 pub async fn get_rooms_stats(&self) -> Vec<RoomInfo> {
225 self.sync_service.get_rooms_stats().await
226 }
227
228 pub async fn offline_rooms_by_condition<F>(
231 &self,
232 condition: F,
233 save_data: bool,
234 ) -> crate::Result<Vec<String>>
235 where
236 F: Fn(&RoomInfo) -> bool,
237 {
238 let room_stats = self.get_rooms_stats().await;
239 let rooms_to_offline: Vec<String> = room_stats
240 .into_iter()
241 .filter(|info| condition(info))
242 .map(|info| info.room_id)
243 .collect();
244
245 if rooms_to_offline.is_empty() {
246 tracing::info!("🔍 没有房间满足下线条件");
247 return Ok(vec![]);
248 }
249
250 tracing::info!("🎯 找到 {} 个房间满足下线条件", rooms_to_offline.len());
251
252 let results = self.offline_rooms(&rooms_to_offline, save_data).await?;
253 let successful_rooms: Vec<String> = results
254 .into_iter()
255 .filter_map(
256 |(room_id, success)| if success { Some(room_id) } else { None },
257 )
258 .collect();
259
260 Ok(successful_rooms)
261 }
262
263 pub async fn offline_empty_rooms(
265 &self,
266 save_data: bool,
267 ) -> crate::Result<Vec<String>> {
268 tracing::info!("🔍 搜索并下线空房间");
269
270 self.offline_rooms_by_condition(
271 |room_info| room_info.client_count == 0,
272 save_data,
273 )
274 .await
275 }
276
277 pub async fn offline_inactive_rooms(
279 &self,
280 inactive_duration: std::time::Duration,
281 save_data: bool,
282 ) -> crate::Result<Vec<String>> {
283 let now = std::time::SystemTime::now();
284 tracing::info!(
285 "🔍 搜索并下线超过 {:?} 未活动的房间",
286 inactive_duration
287 );
288
289 self.offline_rooms_by_condition(
290 |room_info| {
291 now.duration_since(room_info.last_activity).unwrap_or_default()
292 > inactive_duration
293 },
294 save_data,
295 )
296 .await
297 }
298
299 pub async fn shutdown(
301 &self,
302 save_all_data: bool,
303 ) -> crate::Result<()> {
304 tracing::info!("🔴 开始服务器关闭流程");
305
306 let all_rooms = self.get_active_rooms();
307 if all_rooms.is_empty() {
308 tracing::info!("ℹ️ 没有活跃房间需要关闭");
309 return Ok(());
310 }
311
312 tracing::info!("📊 准备关闭 {} 个房间", all_rooms.len());
313
314 let results = self.offline_rooms(&all_rooms, save_all_data).await?;
316 let successful_count =
317 results.iter().filter(|(_, success)| *success).count();
318
319 tracing::info!(
320 "✅ 服务器关闭完成: {}/{} 个房间成功下线",
321 successful_count,
322 all_rooms.len()
323 );
324
325 if successful_count != all_rooms.len() {
326 tracing::warn!("⚠️ 部分房间下线失败,可能需要手动清理");
327 }
328
329 Ok(())
330 }
331
332 pub async fn start(self) {
334 let server = self.clone(); let ws_route = warp::path("collaboration")
338 .and(warp::path::param::<String>()) .and(warp::ws())
340 .and(warp::addr::remote()) .and(warp::any().map(move || server.clone()))
342 .and_then(Self::ws_handler);
343
344 let server_for_http = self.clone();
346 let room_check_route = warp::path("collaboration")
347 .and(warp::path("room-check"))
348 .and(warp::path::param::<String>()) .and(warp::get())
350 .and(warp::any().map(move || server_for_http.clone()))
351 .and_then(Self::room_check_handler);
352
353 let server_for_health = self.clone();
355 let health_route = warp::path("health")
356 .and(warp::get())
357 .and(warp::any().map(move || server_for_health.clone()))
358 .and_then(Self::health_check_handler);
359
360 let server_for_status = self.clone();
362 let room_status_route = warp::path("collaboration")
363 .and(warp::path("rooms"))
364 .and(warp::path::param::<String>()) .and(warp::path("status"))
366 .and(warp::get())
367 .and(warp::any().map(move || server_for_status.clone()))
368 .and_then(Self::room_status_handler);
369
370 let routes = ws_route
372 .or(room_check_route)
373 .or(health_route)
374 .or(room_status_route)
375 .recover(Self::handle_rejection) .with(
377 warp::cors()
378 .allow_any_origin()
379 .allow_headers(vec!["content-type"])
380 .allow_methods(vec![
381 "GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS",
382 ]),
383 );
384
385 let addr = ([0, 0, 0, 0], self.port);
386 tracing::info!(
387 "🌐 协作服务器启动于 http://{}:{}",
388 addr.0.iter().map(|&o| o.to_string()).collect::<Vec<_>>().join("."),
389 addr.1
390 );
391 tracing::info!(
392 "📡 WebSocket: ws://{}:{}/collaboration/{{room_id}}",
393 addr.0.iter().map(|&o| o.to_string()).collect::<Vec<_>>().join("."),
394 addr.1
395 );
396 tracing::info!(
397 "🔍 房间检查: http://{}:{}/collaboration/room-check/{{room_id}}",
398 addr.0.iter().map(|&o| o.to_string()).collect::<Vec<_>>().join("."),
399 addr.1
400 );
401 tracing::info!(
402 "💚 健康检查: http://{}:{}/health",
403 addr.0.iter().map(|&o| o.to_string()).collect::<Vec<_>>().join("."),
404 addr.1
405 );
406 tracing::info!(
407 "📊 房间状态: http://{}:{}/collaboration/rooms/{{room_id}}/status",
408 addr.0.iter().map(|&o| o.to_string()).collect::<Vec<_>>().join("."),
409 addr.1
410 );
411
412 warp::serve(routes).run(addr).await;
413 }
414
415 async fn ws_handler(
417 room_id: String,
418 ws: Ws,
419 remote_addr: Option<std::net::SocketAddr>,
420 server: CollaborationServer,
421 ) -> Result<impl Reply, Rejection> {
422 let yrs_manager = server.yrs_manager.clone();
423 let awareness_ref = yrs_manager.get_or_create_awareness(&room_id);
425 Ok(ws.on_upgrade(move |socket| async move {
426 tracing::info!("✅ 客户端成功连接到现有房间: {}", room_id);
427 let client_addr = remote_addr
428 .map(|addr| addr.to_string())
429 .unwrap_or_else(|| "unknown".to_string());
430 let bcast = Arc::new(BroadcastGroup::new(awareness_ref, 128).await);
432 Self::peer(socket, bcast, room_id.clone(), client_addr).await;
433 }))
434 }
435
436 async fn peer(
438 ws: WebSocket,
439 bcast: Arc<BroadcastGroup>,
440 room_id: String,
441 client_addr: String,
442 ) {
443 let (sink, stream) = ws.split();
444 let sink = Arc::new(Mutex::new(WarpSink::from(sink)));
445 let stream = WarpStream::from(stream);
446 tracing::info!(
448 "🔗 新客户端连接到房间: {} (地址: {})",
449 room_id,
450 client_addr
451 );
452
453 let sub = bcast.subscribe(sink, stream);
454
455 match sub.completed().await {
456 Ok(_) => {
457 tracing::info!(
458 "✅ 客户端正常断开连接 - 房间: {} (地址: {})",
459 room_id,
460 client_addr
461 );
462 },
463 Err(e) => {
464 let error_msg = format!("{}", e);
466
467 if error_msg.contains("failed to deserialize message") {
468 tracing::warn!(
469 "⚠️ 客户端发送了无效数据包 - 房间: {}, 错误: {}",
470 room_id,
471 error_msg
472 );
473 tracing::debug!(
474 "💡 这通常是由网络中断或客户端异常关闭导致的,属于正常现象"
475 );
476 } else if error_msg.contains("unexpected end of buffer") {
477 tracing::warn!(
478 "⚠️ 数据包不完整 - 房间: {}, 可能是网络传输中断",
479 room_id
480 );
481 } else if error_msg.contains("connection closed")
482 || error_msg.contains("broken pipe")
483 {
484 tracing::info!(
485 "🔌 客户端连接意外断开 - 房间: {} ({})",
486 room_id,
487 error_msg
488 );
489 } else {
490 tracing::error!(
491 "❌ 客户端连接异常 - 房间: {}, 错误: {}",
492 room_id,
493 error_msg
494 );
495 }
496 },
497 }
498 }
499
500 pub fn sync_service(&self) -> &Arc<SyncService> {
502 &self.sync_service
503 }
504
505 async fn room_check_handler(
507 room_id: String,
508 server: CollaborationServer,
509 ) -> Result<impl Reply, Rejection> {
510 tracing::debug!("🔍 检查房间是否存在: {}", room_id);
511
512 let exists = server.yrs_manager.room_exists(&room_id);
513
514 if exists {
515 let room_info = server.sync_service.get_room_info(&room_id).await;
516
517 let response = json!({
518 "exists": true,
519 "room_id": room_id,
520 "status": "available",
521 "info": room_info
522 });
523
524 tracing::debug!("✅ 房间 {} 存在", room_id);
525 Ok(warp::reply::with_status(
526 warp::reply::json(&response),
527 warp::http::StatusCode::OK,
528 ))
529 } else {
530 let response = json!({
531 "exists": false,
532 "room_id": room_id,
533 "status": "not_found",
534 "message": format!("房间 '{}' 不存在", room_id)
535 });
536
537 tracing::debug!("❌ 房间 {} 不存在", room_id);
538 Ok(warp::reply::with_status(
539 warp::reply::json(&response),
540 warp::http::StatusCode::NOT_FOUND,
541 ))
542 }
543 }
544
545 async fn health_check_handler(
547 server: CollaborationServer
548 ) -> Result<impl Reply, Rejection> {
549 let room_stats = server.sync_service.get_rooms_stats().await;
550 let active_rooms = server.sync_service.get_active_rooms();
551
552 let response = json!({
553 "status": "healthy",
554 "timestamp": std::time::SystemTime::now()
555 .duration_since(std::time::UNIX_EPOCH)
556 .unwrap_or_default()
557 .as_secs(),
558 "service": "ModuForge Collaboration Server",
559 "version": env!("CARGO_PKG_VERSION"),
560 "statistics": {
561 "active_rooms": active_rooms.len(),
562 "total_rooms": room_stats.len(),
563 "rooms": active_rooms
564 }
565 });
566
567 Ok(warp::reply::with_status(
568 warp::reply::json(&response),
569 warp::http::StatusCode::OK,
570 ))
571 }
572
573 async fn room_status_handler(
575 room_id: String,
576 server: CollaborationServer,
577 ) -> Result<impl Reply, Rejection> {
578 tracing::debug!("📊 获取房间状态: {}", room_id);
579
580 if let Some(room_info) =
581 server.sync_service.get_room_info(&room_id).await
582 {
583 let response = json!({
584 "room_id": room_id,
585 "status": room_info.status,
586 "node_count": room_info.node_count,
587 "client_count": room_info.client_count,
588 "last_activity": room_info.last_activity
589 .duration_since(std::time::UNIX_EPOCH)
590 .unwrap_or_default()
591 .as_secs(),
592 "available": true
593 });
594
595 Ok(warp::reply::with_status(
596 warp::reply::json(&response),
597 warp::http::StatusCode::OK,
598 ))
599 } else {
600 let response = json!({
601 "room_id": room_id,
602 "status": "not_found",
603 "available": false,
604 "message": format!("房间 '{}' 不存在", room_id)
605 });
606
607 Ok(warp::reply::with_status(
608 warp::reply::json(&response),
609 warp::http::StatusCode::NOT_FOUND,
610 ))
611 }
612 }
613}