gitdataai/libs/api/room/ws_handler.rs
2026-04-15 09:08:09 +08:00

730 lines
29 KiB
Rust

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<AppService>,
user_id: Uuid,
}
impl WsRequestHandler {
pub fn new(service: Arc<AppService>, 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<WsResponseData, ApiError> {
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};