use axum::{ extract::{Path, State}, http::StatusCode, response::{IntoResponse, Json}, routing::{get, post}, Router, }; use log::{error, info}; use serde::{Deserialize, Serialize}; use serde_json::Value; use std::sync::Arc; use uuid::Uuid; use crate::shared::state::AppState; pub mod service; use service::{DefaultTranscriptionService, MeetingService}; // ===== Router Configuration ===== /// Configure meet API routes pub fn configure() -> Router> { Router::new() .route("/api/voice/start", post(voice_start)) .route("/api/voice/stop", post(voice_stop)) .route("/api/meet/create", post(create_meeting)) .route("/api/meet/rooms", get(list_rooms)) .route("/api/meet/rooms/:room_id", get(get_room)) .route("/api/meet/rooms/:room_id/join", post(join_room)) .route( "/api/meet/rooms/:room_id/transcription/start", post(start_transcription), ) .route("/api/meet/token", post(get_meeting_token)) .route("/api/meet/invite", post(send_meeting_invites)) .route("/ws/meet", get(meeting_websocket)) } // ===== Request/Response Structures ===== #[derive(Debug, Deserialize)] pub struct CreateMeetingRequest { pub name: String, pub created_by: String, pub settings: Option, } #[derive(Debug, Deserialize)] pub struct JoinRoomRequest { pub participant_name: String, pub participant_id: Option, } #[derive(Debug, Deserialize)] pub struct GetTokenRequest { pub room_id: String, pub user_id: String, } #[derive(Debug, Deserialize)] pub struct SendInvitesRequest { pub room_id: String, pub emails: Vec, } // ===== Voice/Meet Handlers ===== pub async fn voice_start( State(data): State>, Json(info): Json, ) -> impl IntoResponse { let session_id = info .get("session_id") .and_then(|s| s.as_str()) .unwrap_or(""); let user_id = info .get("user_id") .and_then(|u| u.as_str()) .unwrap_or("user"); info!( "Voice session start request - session: {}, user: {}", session_id, user_id ); match data .voice_adapter .start_voice_session(session_id, user_id) .await { Ok(token) => { info!( "Voice session started successfully for session {}", session_id ); ( StatusCode::OK, Json(serde_json::json!({"token": token, "status": "started"})), ) } Err(e) => { error!( "Failed to start voice session for session {}: {}", session_id, e ); ( StatusCode::INTERNAL_SERVER_ERROR, Json(serde_json::json!({"error": e.to_string()})), ) } } } pub async fn voice_stop( State(data): State>, Json(info): Json, ) -> impl IntoResponse { let session_id = info .get("session_id") .and_then(|s| s.as_str()) .unwrap_or(""); match data.voice_adapter.stop_voice_session(session_id).await { Ok(()) => { info!( "Voice session stopped successfully for session {}", session_id ); ( StatusCode::OK, Json(serde_json::json!({"status": "stopped"})), ) } Err(e) => { error!( "Failed to stop voice session for session {}: {}", session_id, e ); ( StatusCode::INTERNAL_SERVER_ERROR, Json(serde_json::json!({"error": e.to_string()})), ) } } } /// Create a new meeting room pub async fn create_meeting( State(state): State>, Json(payload): Json, ) -> impl IntoResponse { let transcription_service = Arc::new(DefaultTranscriptionService); let meeting_service = MeetingService::new(state.clone(), transcription_service); match meeting_service .create_room(payload.name, payload.created_by, payload.settings) .await { Ok(room) => { info!("Created meeting room: {}", room.id); (StatusCode::OK, Json(serde_json::json!(room))) } Err(e) => { error!("Failed to create meeting room: {}", e); ( StatusCode::INTERNAL_SERVER_ERROR, Json(serde_json::json!({"error": e.to_string()})), ) } } } /// List all active meeting rooms pub async fn list_rooms(State(state): State>) -> impl IntoResponse { let transcription_service = Arc::new(DefaultTranscriptionService); let meeting_service = MeetingService::new(state.clone(), transcription_service); let rooms = meeting_service.rooms.read().await; let room_list: Vec<_> = rooms.values().cloned().collect(); (StatusCode::OK, Json(serde_json::json!(room_list))) } /// Get a specific meeting room pub async fn get_room( State(state): State>, Path(room_id): Path, ) -> impl IntoResponse { let transcription_service = Arc::new(DefaultTranscriptionService); let meeting_service = MeetingService::new(state.clone(), transcription_service); let rooms = meeting_service.rooms.read().await; match rooms.get(&room_id) { Some(room) => (StatusCode::OK, Json(serde_json::json!(room))), None => ( StatusCode::NOT_FOUND, Json(serde_json::json!({"error": "Room not found"})), ), } } /// Join a meeting room pub async fn join_room( State(state): State>, Path(room_id): Path, Json(payload): Json, ) -> impl IntoResponse { let transcription_service = Arc::new(DefaultTranscriptionService); let meeting_service = MeetingService::new(state.clone(), transcription_service); match meeting_service .join_room(&room_id, payload.participant_name, payload.participant_id) .await { Ok(participant) => { info!("Participant {} joined room {}", participant.id, room_id); (StatusCode::OK, Json(serde_json::json!(participant))) } Err(e) => { error!("Failed to join room {}: {}", room_id, e); ( StatusCode::INTERNAL_SERVER_ERROR, Json(serde_json::json!({"error": e.to_string()})), ) } } } /// Start transcription for a meeting pub async fn start_transcription( State(state): State>, Path(room_id): Path, ) -> impl IntoResponse { let transcription_service = Arc::new(DefaultTranscriptionService); let meeting_service = MeetingService::new(state.clone(), transcription_service); match meeting_service.start_transcription(&room_id).await { Ok(_) => { info!("Started transcription for room {}", room_id); ( StatusCode::OK, Json(serde_json::json!({"status": "transcription_started"})), ) } Err(e) => { error!("Failed to start transcription for room {}: {}", room_id, e); ( StatusCode::INTERNAL_SERVER_ERROR, Json(serde_json::json!({"error": e.to_string()})), ) } } } /// Get meeting token for WebRTC pub async fn get_meeting_token( State(_state): State>, Json(payload): Json, ) -> impl IntoResponse { // Generate a simple token (in production, use JWT or proper token service) let token = format!( "meet_token_{}_{}_{}", payload.room_id, payload.user_id, uuid::Uuid::new_v4() ); ( StatusCode::OK, Json(serde_json::json!({ "token": token, "room_id": payload.room_id, "user_id": payload.user_id })), ) } /// Send meeting invites pub async fn send_meeting_invites( State(_state): State>, Json(payload): Json, ) -> impl IntoResponse { info!("Sending meeting invites for room {}", payload.room_id); // In production, integrate with email service ( StatusCode::OK, Json(serde_json::json!({ "status": "invites_sent", "recipients": payload.emails })), ) } /// WebSocket handler for real-time meeting communication pub async fn meeting_websocket( ws: axum::extract::ws::WebSocketUpgrade, State(state): State>, ) -> impl IntoResponse { ws.on_upgrade(|socket| handle_meeting_socket(socket, state)) } async fn handle_meeting_socket(_socket: axum::extract::ws::WebSocket, _state: Arc) { info!("Meeting WebSocket connection established"); // Handle WebSocket messages for real-time meeting communication // This would integrate with WebRTC signaling }