use crate::error::ApiError; use actix_web::Result; use room::ws_context::WsUserContext; use service::AppService; use std::sync::Arc; use uuid::Uuid; pub struct WsRequestHandler { service: Arc, user_id: Uuid, } impl WsRequestHandler { pub fn new(service: Arc, user_id: Uuid) -> Self { Self { service, user_id } } pub async fn handle(&self, request: WsRequest) -> WsResponse { let request_id = request.request_id; let action_str = request.action.to_string(); match self.handle_action(request).await { Ok(data) => WsResponse::success(request_id, &action_str, data), Err(err) => WsResponse::from_api_error(request_id, &action_str, err), } } async fn handle_action(&self, request: WsRequest) -> Result { let params = request.params(); let ctx = WsUserContext::new(self.user_id); match request.action { WsAction::RoomList => { let project_name = params.project_name.clone().ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "project_name required".into(), )) })?; let rooms = self .service .room .room_list(project_name, params.only_public, &ctx) .await .map_err(ApiError::from)?; Ok(WsResponseData::room_list(rooms)) } WsAction::RoomGet => { let room_id = params.room_id.ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "room_id required".into(), )) })?; let room = self .service .room .room_get(room_id, &ctx) .await .map_err(ApiError::from)?; Ok(WsResponseData::room(room)) } WsAction::RoomCreate => { let project_name = params.project_name.clone().ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "project_name required".into(), )) })?; let room = self .service .room .room_create( project_name, room::RoomCreateRequest { room_name: params.room_name.clone().unwrap_or_default(), public: params.room_public.unwrap_or(false), category: params.room_category, }, &ctx, ) .await .map_err(ApiError::from)?; Ok(WsResponseData::room(room)) } WsAction::RoomUpdate => { let room_id = params.room_id.ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "room_id required".into(), )) })?; let room = self .service .room .room_update( room_id, room::RoomUpdateRequest { room_name: params.room_name.clone(), public: params.room_public, category: params.room_category, }, &ctx, ) .await .map_err(ApiError::from)?; Ok(WsResponseData::room(room)) } WsAction::RoomDelete => { let room_id = params.room_id.ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "room_id required".into(), )) })?; self.service .room .room_delete(room_id, &ctx) .await .map_err(ApiError::from)?; Ok(WsResponseData::bool(true)) } WsAction::CategoryList => { let project_name = params.project_name.clone().ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "project_name required".into(), )) })?; let categories = self .service .room .room_category_list(project_name, &ctx) .await .map_err(ApiError::from)?; Ok(WsResponseData::category_list(categories)) } WsAction::CategoryCreate => { let project_name = params.project_name.clone().ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "project_name required".into(), )) })?; let category = self .service .room .room_category_create( project_name, room::RoomCategoryCreateRequest { name: params.name.clone().unwrap_or_default(), position: params.position, }, &ctx, ) .await .map_err(ApiError::from)?; Ok(WsResponseData::category(category)) } WsAction::CategoryUpdate => { let category_id = params.category_id.ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "category_id required".into(), )) })?; let category = self .service .room .room_category_update( category_id, room::RoomCategoryUpdateRequest { name: params.name.clone(), position: params.position, }, &ctx, ) .await .map_err(ApiError::from)?; Ok(WsResponseData::category(category)) } WsAction::CategoryDelete => { let category_id = params.category_id.ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "category_id required".into(), )) })?; self.service .room .room_category_delete(category_id, &ctx) .await .map_err(ApiError::from)?; Ok(WsResponseData::bool(true)) } WsAction::MessageList => { let room_id = params.room_id.ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "room_id required".into(), )) })?; let messages = self .service .room .room_message_list( room_id, params.before_seq, params.after_seq, params.limit, &ctx, ) .await .map_err(ApiError::from)?; Ok(WsResponseData::message_list(messages)) } WsAction::MessageCreate => { let room_id = params.room_id.ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "room_id required".into(), )) })?; let message = self .service .room .room_message_create( room_id, room::RoomMessageCreateRequest { content: params.content.clone().unwrap_or_default(), content_type: params.content_type.clone(), thread: params.thread_id, in_reply_to: params.in_reply_to, }, &ctx, ) .await .map_err(ApiError::from)?; Ok(WsResponseData::message(message)) } WsAction::MessageUpdate => { let message_id = params.message_id.ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "message_id required".into(), )) })?; let message = self .service .room .room_message_update( message_id, room::RoomMessageUpdateRequest { content: params.content.clone().unwrap_or_default(), }, &ctx, ) .await .map_err(ApiError::from)?; Ok(WsResponseData::message(message)) } WsAction::MessageRevoke => { let message_id = params.message_id.ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "message_id required".into(), )) })?; let message = self .service .room .room_message_revoke(message_id, &ctx) .await .map_err(ApiError::from)?; Ok(WsResponseData::message(message)) } WsAction::MessageGet => { let message_id = params.message_id.ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "message_id required".into(), )) })?; let message = self .service .room .room_message_get(message_id, &ctx) .await .map_err(ApiError::from)?; Ok(WsResponseData::message(message)) } WsAction::MemberList => { let room_id = params.room_id.ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "room_id required".into(), )) })?; let members = self .service .room .room_member_list(room_id, &ctx) .await .map_err(ApiError::from)?; Ok(WsResponseData::member_list(members)) } WsAction::MemberAdd => { let room_id = params.room_id.ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "room_id required".into(), )) })?; let user_id = params.user_id.ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "user_id required".into(), )) })?; let member = self .service .room .room_member_add( room_id, room::RoomMemberAddRequest { user_id, role: params.role.clone(), }, &ctx, ) .await .map_err(ApiError::from)?; Ok(WsResponseData::member(member)) } WsAction::MemberRemove => { let room_id = params.room_id.ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "room_id required".into(), )) })?; let user_id = params.user_id.ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "user_id required".into(), )) })?; self.service .room .room_member_remove(room_id, user_id, &ctx) .await .map_err(ApiError::from)?; Ok(WsResponseData::bool(true)) } WsAction::MemberLeave => { let room_id = params.room_id.ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "room_id required".into(), )) })?; self.service .room .room_member_leave(room_id, &ctx) .await .map_err(ApiError::from)?; Ok(WsResponseData::bool(true)) } WsAction::MemberSetReadSeq => { let room_id = params.room_id.ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "room_id required".into(), )) })?; let last_read_seq = params.last_read_seq.ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "last_read_seq required".into(), )) })?; let member = self .service .room .room_member_set_read_seq( room_id, room::RoomMemberReadSeqRequest { last_read_seq }, &ctx, ) .await .map_err(ApiError::from)?; Ok(WsResponseData::member(member)) } WsAction::MemberUpdateRole => { let room_id = params.room_id.ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "room_id required".into(), )) })?; let user_id = params.user_id.ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "user_id required".into(), )) })?; let role = params.role.clone().ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest("role required".into())) })?; let member = self .service .room .room_member_update_role( room_id, room::RoomMemberRoleUpdateRequest { user_id, role }, &ctx, ) .await .map_err(ApiError::from)?; Ok(WsResponseData::member(member)) } WsAction::PinList => { let room_id = params.room_id.ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "room_id required".into(), )) })?; let pins = self .service .room .room_pin_list(room_id, &ctx) .await .map_err(ApiError::from)?; Ok(WsResponseData::pin_list(pins)) } WsAction::PinAdd => { let message_id = params.message_id.ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "message_id required".into(), )) })?; let pin = self .service .room .room_pin_add(message_id, &ctx) .await .map_err(ApiError::from)?; Ok(WsResponseData::pin(pin)) } WsAction::PinRemove => { let message_id = params.message_id.ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "message_id required".into(), )) })?; self.service .room .room_pin_remove(message_id, &ctx) .await .map_err(ApiError::from)?; Ok(WsResponseData::bool(true)) } WsAction::ThreadList => { let room_id = params.room_id.ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "room_id required".into(), )) })?; let threads = self .service .room .room_thread_list(room_id, &ctx) .await .map_err(ApiError::from)?; Ok(WsResponseData::thread_list(threads)) } WsAction::ThreadCreate => { let room_id = params.room_id.ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "room_id required".into(), )) })?; let parent_seq = params.parent_seq.ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "parent_seq required".into(), )) })?; let thread = self .service .room .room_thread_create(room_id, room::RoomThreadCreateRequest { parent_seq }, &ctx) .await .map_err(ApiError::from)?; Ok(WsResponseData::thread(thread)) } WsAction::ThreadMessages => { let thread_id = params.thread_id.ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "thread_id required".into(), )) })?; let messages = self .service .room .room_thread_messages( thread_id, params.before_seq, params.after_seq, params.limit, &ctx, ) .await .map_err(ApiError::from)?; Ok(WsResponseData::message_list(messages)) } WsAction::ReactionAdd => { let message_id = params.message_id.ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "message_id required".into(), )) })?; let emoji = params.emoji.clone().ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "emoji required".into(), )) })?; let reactions = self .service .room .message_reaction_add(message_id, emoji, &ctx) .await .map_err(ApiError::from)?; Ok(WsResponseData::reaction_list(reactions)) } WsAction::ReactionRemove => { let message_id = params.message_id.ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "message_id required".into(), )) })?; let emoji = params.emoji.clone().ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "emoji required".into(), )) })?; let reactions = self .service .room .message_reaction_remove(message_id, emoji, &ctx) .await .map_err(ApiError::from)?; Ok(WsResponseData::reaction_list(reactions)) } WsAction::ReactionGet => { let message_id = params.message_id.ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "message_id required".into(), )) })?; let reactions = self .service .room .message_reactions_get(message_id, &ctx) .await .map_err(ApiError::from)?; Ok(WsResponseData::reaction_list(reactions)) } WsAction::ReactionListBatch => { let room_id = params.room_id.ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "room_id required".into(), )) })?; let message_ids = params.message_ids.clone().ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "message_ids required".into(), )) })?; let results = self .service .room .message_reactions_batch(room_id, message_ids, &ctx) .await .map_err(ApiError::from)?; Ok(WsResponseData::reaction_list_batch(results)) } WsAction::MessageSearch => { let room_id = params.room_id.ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "room_id required".into(), )) })?; let query = params.query.clone().ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "query required".into(), )) })?; let result = self .service .room .message_search(room_id, &query, params.limit, params.offset, &ctx) .await .map_err(ApiError::from)?; Ok(WsResponseData::search_result(result)) } WsAction::MessageEditHistory => { let message_id = params.message_id.ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "message_id required".into(), )) })?; let history = self .service .room .get_message_edit_history(message_id, &ctx) .await .map_err(ApiError::from)?; Ok(WsResponseData::edit_history(history)) } WsAction::AiList => { let room_id = params.room_id.ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "room_id required".into(), )) })?; let configs = self .service .room .room_ai_list(room_id, &ctx) .await .map_err(ApiError::from)?; Ok(WsResponseData::ai_list(configs)) } WsAction::AiUpsert => { let room_id = params.room_id.ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "room_id required".into(), )) })?; let model = params.model.ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "model required".into(), )) })?; let config = self .service .room .room_ai_upsert( room_id, room::RoomAiUpsertRequest { model, version: params.model_version, history_limit: params.history_limit, system_prompt: params.system_prompt.clone(), temperature: params.temperature, max_tokens: params.max_tokens, use_exact: params.use_exact, think: params.think, stream: params.stream, min_score: params.min_score, }, &ctx, ) .await .map_err(ApiError::from)?; Ok(WsResponseData::ai_config(config)) } WsAction::AiDelete => { let room_id = params.room_id.ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "room_id required".into(), )) })?; let model_id = params.model_id.ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "model_id required".into(), )) })?; self.service .room .room_ai_delete(room_id, model_id, &ctx) .await .map_err(ApiError::from)?; Ok(WsResponseData::bool(true)) } WsAction::NotificationList => { let notifications = self .service .room .notification_list(params.only_unread, params.archived, params.limit, &ctx) .await .map_err(ApiError::from)?; Ok(WsResponseData::notification_list(notifications)) } WsAction::NotificationMarkRead => { let notification_id = params.notification_id.ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "notification_id required".into(), )) })?; self.service .room .notification_mark_read(notification_id, &ctx) .await .map_err(ApiError::from)?; Ok(WsResponseData::bool(true)) } WsAction::NotificationMarkAllRead => { let count = self .service .room .notification_mark_all_read(&ctx) .await .map_err(ApiError::from)?; Ok(WsResponseData::u64(count)) } WsAction::NotificationArchive => { let notification_id = params.notification_id.ok_or_else(|| { ApiError::from(service::error::AppError::BadRequest( "notification_id required".into(), )) })?; self.service .room .notification_archive(notification_id, &ctx) .await .map_err(ApiError::from)?; Ok(WsResponseData::bool(true)) } WsAction::MentionList => { let mentions = self .service .room .get_mention_notifications(params.limit, &ctx) .await .map_err(ApiError::from)?; Ok(WsResponseData::mention_list(mentions)) } WsAction::MentionReadAll => { self.service .room .mark_mention_notifications_read(&ctx) .await .map_err(ApiError::from)?; Ok(WsResponseData::bool(true)) } WsAction::SubscribeRoom => Ok(WsResponseData::subscribed(params.room_id, None)), WsAction::UnsubscribeRoom => Ok(WsResponseData::bool(true)), WsAction::SubscribeProject => Ok(WsResponseData::subscribed(None, None)), WsAction::UnsubscribeProject => Ok(WsResponseData::bool(true)), } } } use super::ws_types::{WsAction, WsRequest, WsResponse, WsResponseData};