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 pub async fn offline_room(
113 &self,
114 room_id: &str,
115 save_data: bool,
116 ) -> crate::Result<bool> {
117 tracing::info!("🔄 开始下线房间: {}", room_id);
118
119 let room_status = self.sync_service.get_room_status(room_id).await;
121 match room_status {
122 RoomStatus::NotExists => {
123 tracing::warn!("❌ 尝试下线不存在的房间: {}", room_id);
124 return Ok(false);
125 },
126 RoomStatus::Offline => {
127 tracing::info!("ℹ️ 房间 {} 已经下线", room_id);
128 return Ok(true);
129 },
130 _ => {},
131 }
132
133 if let Some(room_info) = self.sync_service.get_room_info(room_id).await
135 {
136 tracing::info!(
137 "📊 房间信息 - 节点数: {}, 客户端数: {}",
138 room_info.node_count,
139 room_info.client_count
140 );
141 }
142
143 match self.sync_service.offline_room(room_id, save_data).await {
145 Ok(snapshot) => {
146 if let Some(_snapshot) = snapshot {
147 tracing::info!("💾 房间 {} 数据已保存", room_id);
148 }
149 tracing::info!("✅ 房间 {} 成功下线", room_id);
150 Ok(true)
151 },
152 Err(e) => {
153 tracing::error!("❌ 房间 {} 下线失败: {}", room_id, e);
154 Err(e)
155 },
156 }
157 }
158
159 pub async fn force_offline_room(
161 &self,
162 room_id: &str,
163 ) -> crate::Result<bool> {
164 tracing::warn!("⚠️ 强制下线房间: {}", room_id);
165
166 match self.sync_service.force_offline_room(room_id).await {
167 Ok(success) => {
168 if success {
169 tracing::info!("✅ 房间 {} 强制下线成功", room_id);
170 } else {
171 tracing::error!("❌ 房间 {} 强制下线失败", room_id);
172 }
173 Ok(success)
174 },
175 Err(e) => {
176 tracing::error!(
177 "❌ 强制下线房间 {} 时发生错误: {}",
178 room_id,
179 e
180 );
181 Err(e)
182 },
183 }
184 }
185
186 pub async fn offline_rooms(
188 &self,
189 room_ids: &[String],
190 save_data: bool,
191 ) -> crate::Result<Vec<(String, bool)>> {
192 tracing::info!("🔄 批量下线 {} 个房间", room_ids.len());
193
194 let mut results = Vec::new();
195
196 for room_id in room_ids {
197 match self.offline_room(room_id, save_data).await {
198 Ok(success) => results.push((room_id.clone(), success)),
199 Err(e) => {
200 tracing::error!("❌ 下线房间 {} 失败: {}", room_id, e);
201 results.push((room_id.clone(), false));
202 },
203 }
204 }
205
206 let successful_count =
207 results.iter().filter(|(_, success)| *success).count();
208 tracing::info!(
209 "📊 批量下线完成: {}/{} 个房间成功下线",
210 successful_count,
211 room_ids.len()
212 );
213
214 Ok(results)
215 }
216
217 pub fn get_active_rooms(&self) -> Vec<String> {
219 self.sync_service.get_active_rooms()
220 }
221
222 pub async fn get_rooms_stats(&self) -> Vec<RoomInfo> {
224 self.sync_service.get_rooms_stats().await
225 }
226
227 pub async fn offline_rooms_by_condition<F>(
230 &self,
231 condition: F,
232 save_data: bool,
233 ) -> crate::Result<Vec<String>>
234 where
235 F: Fn(&RoomInfo) -> bool,
236 {
237 let room_stats = self.get_rooms_stats().await;
238 let rooms_to_offline: Vec<String> = room_stats
239 .into_iter()
240 .filter(|info| condition(info))
241 .map(|info| info.room_id)
242 .collect();
243
244 if rooms_to_offline.is_empty() {
245 tracing::info!("🔍 没有房间满足下线条件");
246 return Ok(vec![]);
247 }
248
249 tracing::info!("🎯 找到 {} 个房间满足下线条件", rooms_to_offline.len());
250
251 let results = self.offline_rooms(&rooms_to_offline, save_data).await?;
252 let successful_rooms: Vec<String> = results
253 .into_iter()
254 .filter_map(
255 |(room_id, success)| if success { Some(room_id) } else { None },
256 )
257 .collect();
258
259 Ok(successful_rooms)
260 }
261
262 pub async fn offline_empty_rooms(
264 &self,
265 save_data: bool,
266 ) -> crate::Result<Vec<String>> {
267 tracing::info!("🔍 搜索并下线空房间");
268
269 self.offline_rooms_by_condition(
270 |room_info| room_info.client_count == 0,
271 save_data,
272 )
273 .await
274 }
275
276 pub async fn offline_inactive_rooms(
278 &self,
279 inactive_duration: std::time::Duration,
280 save_data: bool,
281 ) -> crate::Result<Vec<String>> {
282 let now = std::time::SystemTime::now();
283 tracing::info!(
284 "🔍 搜索并下线超过 {:?} 未活动的房间",
285 inactive_duration
286 );
287
288 self.offline_rooms_by_condition(
289 |room_info| {
290 now.duration_since(room_info.last_activity).unwrap_or_default()
291 > inactive_duration
292 },
293 save_data,
294 )
295 .await
296 }
297
298 pub async fn shutdown(
300 &self,
301 save_all_data: bool,
302 ) -> crate::Result<()> {
303 tracing::info!("🔴 开始服务器关闭流程");
304
305 let all_rooms = self.get_active_rooms();
306 if all_rooms.is_empty() {
307 tracing::info!("ℹ️ 没有活跃房间需要关闭");
308 return Ok(());
309 }
310
311 tracing::info!("📊 准备关闭 {} 个房间", all_rooms.len());
312
313 let results = self.offline_rooms(&all_rooms, save_all_data).await?;
315 let successful_count =
316 results.iter().filter(|(_, success)| *success).count();
317
318 tracing::info!(
319 "✅ 服务器关闭完成: {}/{} 个房间成功下线",
320 successful_count,
321 all_rooms.len()
322 );
323
324 if successful_count != all_rooms.len() {
325 tracing::warn!("⚠️ 部分房间下线失败,可能需要手动清理");
326 }
327
328 Ok(())
329 }
330
331 pub async fn start(self) {
333 let server = self.clone(); let ws_route = warp::path("collaboration")
337 .and(warp::path::param::<String>()) .and(warp::ws())
339 .and(warp::addr::remote()) .and(warp::any().map(move || server.clone()))
341 .and_then(Self::ws_handler);
342
343 let server_for_http = self.clone();
345 let room_check_route = warp::path("collaboration")
346 .and(warp::path("room-check"))
347 .and(warp::path::param::<String>()) .and(warp::get())
349 .and(warp::any().map(move || server_for_http.clone()))
350 .and_then(Self::room_check_handler);
351
352 let server_for_health = self.clone();
354 let health_route = warp::path("health")
355 .and(warp::get())
356 .and(warp::any().map(move || server_for_health.clone()))
357 .and_then(Self::health_check_handler);
358
359 let server_for_status = self.clone();
361 let room_status_route = warp::path("collaboration")
362 .and(warp::path("rooms"))
363 .and(warp::path::param::<String>()) .and(warp::path("status"))
365 .and(warp::get())
366 .and(warp::any().map(move || server_for_status.clone()))
367 .and_then(Self::room_status_handler);
368
369 let routes = ws_route
371 .or(room_check_route)
372 .or(health_route)
373 .or(room_status_route)
374 .recover(Self::handle_rejection) .with(
376 warp::cors()
377 .allow_any_origin()
378 .allow_headers(vec!["content-type"])
379 .allow_methods(vec![
380 "GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS",
381 ]),
382 );
383
384 let addr = ([0, 0, 0, 0], self.port);
385 tracing::info!(
386 "🌐 协作服务器启动于 http://{}:{}",
387 addr.0.iter().map(|&o| o.to_string()).collect::<Vec<_>>().join("."),
388 addr.1
389 );
390 tracing::info!(
391 "📡 WebSocket: ws://{}:{}/collaboration/{{room_id}}",
392 addr.0.iter().map(|&o| o.to_string()).collect::<Vec<_>>().join("."),
393 addr.1
394 );
395 tracing::info!(
396 "🔍 房间检查: http://{}:{}/collaboration/room-check/{{room_id}}",
397 addr.0.iter().map(|&o| o.to_string()).collect::<Vec<_>>().join("."),
398 addr.1
399 );
400 tracing::info!(
401 "💚 健康检查: http://{}:{}/health",
402 addr.0.iter().map(|&o| o.to_string()).collect::<Vec<_>>().join("."),
403 addr.1
404 );
405 tracing::info!(
406 "📊 房间状态: http://{}:{}/collaboration/rooms/{{room_id}}/status",
407 addr.0.iter().map(|&o| o.to_string()).collect::<Vec<_>>().join("."),
408 addr.1
409 );
410
411 warp::serve(routes).run(addr).await;
412 }
413
414 async fn ws_handler(
416 room_id: String,
417 ws: Ws,
418 remote_addr: Option<std::net::SocketAddr>,
419 server: CollaborationServer,
420 ) -> Result<impl Reply, Rejection> {
421 let yrs_manager = server.yrs_manager.clone();
422 let awareness_ref = yrs_manager.get_or_create_awareness(&room_id);
424 Ok(ws.on_upgrade(move |socket| async move {
425 tracing::info!("✅ 客户端成功连接到现有房间: {}", room_id);
426 let client_addr = remote_addr
427 .map(|addr| addr.to_string())
428 .unwrap_or_else(|| "unknown".to_string());
429 let bcast = Arc::new(BroadcastGroup::new(awareness_ref, 128).await);
431 Self::peer(socket, bcast, room_id.clone(), client_addr).await;
432 }))
433 }
434
435 async fn peer(
437 ws: WebSocket,
438 bcast: Arc<BroadcastGroup>,
439 room_id: String,
440 client_addr: String,
441 ) {
442 let (sink, stream) = ws.split();
443 let sink = Arc::new(Mutex::new(WarpSink::from(sink)));
444 let stream = WarpStream::from(stream);
445 tracing::info!(
447 "🔗 新客户端连接到房间: {} (地址: {})",
448 room_id,
449 client_addr
450 );
451
452 let sub = bcast.subscribe(sink, stream);
453
454 match sub.completed().await {
455 Ok(_) => {
456 tracing::info!(
457 "✅ 客户端正常断开连接 - 房间: {} (地址: {})",
458 room_id,
459 client_addr
460 );
461 },
462 Err(e) => {
463 let error_msg = format!("{e}");
465
466 if error_msg.contains("failed to deserialize message") {
467 tracing::warn!(
468 "⚠️ 客户端发送了无效数据包 - 房间: {}, 错误: {}",
469 room_id,
470 error_msg
471 );
472 tracing::debug!(
473 "💡 这通常是由网络中断或客户端异常关闭导致的,属于正常现象"
474 );
475 } else if error_msg.contains("unexpected end of buffer") {
476 tracing::warn!(
477 "⚠️ 数据包不完整 - 房间: {}, 可能是网络传输中断",
478 room_id
479 );
480 } else if error_msg.contains("connection closed")
481 || error_msg.contains("broken pipe")
482 {
483 tracing::info!(
484 "🔌 客户端连接意外断开 - 房间: {} ({})",
485 room_id,
486 error_msg
487 );
488 } else {
489 tracing::error!(
490 "❌ 客户端连接异常 - 房间: {}, 错误: {}",
491 room_id,
492 error_msg
493 );
494 }
495 },
496 }
497 }
498
499 pub fn sync_service(&self) -> &Arc<SyncService> {
501 &self.sync_service
502 }
503
504 async fn room_check_handler(
506 room_id: String,
507 server: CollaborationServer,
508 ) -> Result<impl Reply, Rejection> {
509 tracing::debug!("🔍 检查房间是否存在: {}", room_id);
510
511 let exists = server.yrs_manager.room_exists(&room_id);
512
513 if exists {
514 let room_info = server.sync_service.get_room_info(&room_id).await;
515
516 let response = json!({
517 "exists": true,
518 "room_id": room_id,
519 "status": "available",
520 "info": room_info
521 });
522
523 tracing::debug!("✅ 房间 {} 存在", room_id);
524 Ok(warp::reply::with_status(
525 warp::reply::json(&response),
526 warp::http::StatusCode::OK,
527 ))
528 } else {
529 let response = json!({
530 "exists": false,
531 "room_id": room_id,
532 "status": "not_found",
533 "message": format!("房间 '{}' 不存在", room_id)
534 });
535
536 tracing::debug!("❌ 房间 {} 不存在", room_id);
537 Ok(warp::reply::with_status(
538 warp::reply::json(&response),
539 warp::http::StatusCode::NOT_FOUND,
540 ))
541 }
542 }
543
544 async fn health_check_handler(
546 server: CollaborationServer
547 ) -> Result<impl Reply, Rejection> {
548 let room_stats = server.sync_service.get_rooms_stats().await;
549 let active_rooms = server.sync_service.get_active_rooms();
550
551 let response = json!({
552 "status": "healthy",
553 "timestamp": std::time::SystemTime::now()
554 .duration_since(std::time::UNIX_EPOCH)
555 .unwrap_or_default()
556 .as_secs(),
557 "service": "ModuForge Collaboration Server",
558 "version": env!("CARGO_PKG_VERSION"),
559 "statistics": {
560 "active_rooms": active_rooms.len(),
561 "total_rooms": room_stats.len(),
562 "rooms": active_rooms
563 }
564 });
565
566 Ok(warp::reply::with_status(
567 warp::reply::json(&response),
568 warp::http::StatusCode::OK,
569 ))
570 }
571
572 async fn room_status_handler(
574 room_id: String,
575 server: CollaborationServer,
576 ) -> Result<impl Reply, Rejection> {
577 tracing::debug!("📊 获取房间状态: {}", room_id);
578
579 if let Some(room_info) =
580 server.sync_service.get_room_info(&room_id).await
581 {
582 let response = json!({
583 "room_id": room_id,
584 "status": room_info.status,
585 "node_count": room_info.node_count,
586 "client_count": room_info.client_count,
587 "last_activity": room_info.last_activity
588 .duration_since(std::time::UNIX_EPOCH)
589 .unwrap_or_default()
590 .as_secs(),
591 "available": true
592 });
593
594 Ok(warp::reply::with_status(
595 warp::reply::json(&response),
596 warp::http::StatusCode::OK,
597 ))
598 } else {
599 let response = json!({
600 "room_id": room_id,
601 "status": "not_found",
602 "available": false,
603 "message": format!("房间 '{}' 不存在", room_id)
604 });
605
606 Ok(warp::reply::with_status(
607 warp::reply::json(&response),
608 warp::http::StatusCode::NOT_FOUND,
609 ))
610 }
611 }
612}