gitdataai/lib/channel/http/handler/mod.rs
zhenyi 779e4eae2f feat(channel): add article feed and composer with room type support
- Add ArticleFeed component for article-based channels
- Implement ArticleComposer with draft persistence
- Add Newspaper icon for article room type
- Update ChannelPage to conditionally render article feed vs message view
- Add article-related API endpoints and models
- Reset thread view when switching rooms
- Add room type check in channel sidebar
- Update CSS to hide scrollbars globally
- Add gRPC message size limit configuration
- Fix git diff tree handling
2026-05-31 03:09:49 +08:00

434 lines
14 KiB
Rust

use uuid::Uuid;
use crate::{ChannelBus, ChannelResult};
pub(crate) use super::out_event::WsOutEvent;
use super::types::{WS_PROTOCOL_VERSION, WsInMessage};
pub(crate) const MAX_TEXT_LEN: usize = 64 * 1024;
pub(crate) const MAX_MESSAGES_PER_REQUEST: u64 = 100;
pub(crate) const MAX_ROOM_NAME_LEN: usize = 100;
pub(crate) const MAX_CATEGORY_NAME_LEN: usize = 50;
mod helpers;
mod article;
mod ban;
mod category;
mod conversation;
mod draft;
mod forward;
mod invite;
mod message;
mod message_read;
mod notification;
mod pin;
mod presence;
mod reaction;
mod room;
mod search;
mod star;
mod subscription;
mod thread;
mod user;
mod voice;
pub struct WsHandler;
impl WsHandler {
pub async fn handle(
bus: &ChannelBus,
user_id: Uuid,
msg: WsInMessage,
) -> ChannelResult<Option<WsOutEvent>> {
match msg {
WsInMessage::Ping => Ok(Some(WsOutEvent::Pong {
protocol_version: WS_PROTOCOL_VERSION,
})),
WsInMessage::Subscribe { room } => {
Self::subscribe(bus, user_id, room).await
}
WsInMessage::Unsubscribe { room } => {
Self::unsubscribe(bus, user_id, room).await
}
WsInMessage::TypingStart { room } => {
Self::typing(bus, room, user_id, "start").await
}
WsInMessage::TypingStop { room } => {
Self::typing(bus, room, user_id, "stop").await
}
WsInMessage::ReadReceipt {
room,
last_read_seq,
} => Self::read_receipt(bus, user_id, room, last_read_seq).await,
WsInMessage::MessageList {
room,
before_seq,
after_seq,
limit,
} => {
Self::message_list(
bus, user_id, room, before_seq, after_seq, limit,
)
.await
}
WsInMessage::MessageAround {
room,
seq,
limit,
thread,
} => {
Self::message_around(bus, user_id, room, seq, limit, thread)
.await
}
WsInMessage::MessageCreate {
room,
content,
content_type,
thread,
in_reply_to,
} => {
Self::message_create(
bus,
user_id,
room,
content,
content_type,
thread,
in_reply_to,
)
.await
}
WsInMessage::MessageUpdate { message, content } => {
Self::message_update(bus, user_id, message, content).await
}
WsInMessage::MessageRevoke { message } => {
Self::message_revoke(bus, user_id, message).await
}
WsInMessage::RoomGet { room } => {
Self::room_get(bus, user_id, room).await
}
WsInMessage::RoomCreate {
workspace,
room_name,
public,
category,
ai_enabled,
channel_type,
} => {
Self::room_create(
bus, user_id, workspace, room_name, public, category,
ai_enabled, channel_type,
)
.await
}
WsInMessage::RoomUpdate {
room,
room_name,
public,
category,
ai_enabled,
} => {
Self::room_update(
bus, user_id, room, room_name, public, category, ai_enabled,
)
.await
}
WsInMessage::RoomDelete { room } => {
Self::room_delete(bus, user_id, room).await
}
WsInMessage::CategoryCreate {
workspace,
name,
position,
} => {
Self::category_create(bus, user_id, workspace, name, position)
.await
}
WsInMessage::CategoryUpdate { id, name, position } => {
Self::category_update(bus, user_id, id, name, position).await
}
WsInMessage::CategoryDelete { id } => {
Self::category_delete(bus, user_id, id).await
}
WsInMessage::AccessGrant { room, user } => {
Self::access_grant(bus, user_id, room, user).await
}
WsInMessage::AccessRevoke { room, user } => {
Self::access_revoke(bus, user_id, room, user).await
}
WsInMessage::StateSetReadSeq {
room,
last_read_seq,
} => Self::read_receipt(bus, user_id, room, last_read_seq).await,
WsInMessage::MissedMessages {
room,
after_seq,
limit,
} => {
Self::missed_messages(bus, user_id, room, after_seq, limit)
.await
}
WsInMessage::CsrfToken => Self::csrf_token(bus, user_id).await,
WsInMessage::StateUpdateDnd {
room,
do_not_disturb,
dnd_start_hour,
dnd_end_hour,
} => {
Self::dnd_update(
bus,
user_id,
room,
do_not_disturb,
dnd_start_hour,
dnd_end_hour,
)
.await
}
WsInMessage::ReactionAdd {
room,
message,
emoji,
} => Self::reaction_add(bus, user_id, room, message, emoji).await,
WsInMessage::ReactionRemove {
room,
message,
emoji,
} => {
Self::reaction_remove(bus, user_id, room, message, emoji).await
}
WsInMessage::ThreadCreate { room, parent } => {
Self::thread_create(bus, user_id, room, parent).await
}
WsInMessage::ThreadList { room } => {
Self::thread_list(bus, user_id, room).await
}
WsInMessage::ThreadResolve { thread_id } => {
Self::thread_resolve(bus, user_id, thread_id).await
}
WsInMessage::ThreadArchive { thread_id } => {
Self::thread_archive(bus, user_id, thread_id).await
}
WsInMessage::PinAdd { room, message } => {
Self::pin_add(bus, user_id, room, message).await
}
WsInMessage::PinRemove { room, message } => {
Self::pin_remove(bus, user_id, room, message).await
}
WsInMessage::DraftSave { room, content } => {
Self::draft_save(bus, user_id, room, content).await
}
WsInMessage::DraftClear { room } => {
Self::draft_clear(bus, user_id, room).await
}
WsInMessage::NotificationMarkRead { id } => {
Self::notification_mark_read(bus, user_id, id).await
}
WsInMessage::NotificationMarkAllRead { workspace_id } => {
Self::notification_mark_all_read(bus, user_id, workspace_id)
.await
}
WsInMessage::NotificationArchive { id } => {
Self::notification_archive(bus, user_id, id).await
}
WsInMessage::PresenceUpdate { status } => {
Self::presence_update(bus, user_id, status).await
}
WsInMessage::CustomStatusUpdate {
emoji,
text,
expires_at,
} => {
Self::custom_status_update(
bus, user_id, emoji, text, expires_at,
)
.await
}
WsInMessage::InviteCreate {
workspace,
room,
max_uses,
expires_at,
} => {
Self::invite_create(
bus, user_id, workspace, room, max_uses, expires_at,
)
.await
}
WsInMessage::InviteAccept { code } => {
Self::invite_accept(bus, user_id, code).await
}
WsInMessage::InviteRevoke { id } => {
Self::invite_revoke(bus, user_id, id).await
}
WsInMessage::BanCreate {
workspace,
user,
reason,
expires_at,
} => {
Self::ban_create(
bus, user_id, workspace, user, reason, expires_at,
)
.await
}
WsInMessage::BanRemove { workspace, user } => {
Self::ban_remove(bus, user_id, workspace, user).await
}
WsInMessage::VoiceJoin { room } => {
Self::voice_join(bus, user_id, room).await
}
WsInMessage::VoiceLeave { room } => {
Self::voice_leave(bus, user_id, room).await
}
WsInMessage::VoiceMute { room, muted } => {
Self::voice_mute(bus, user_id, room, muted).await
}
WsInMessage::VoiceDeaf { room, deafened } => {
Self::voice_deaf(bus, user_id, room, deafened).await
}
WsInMessage::ScreenShare { room, start } => {
Self::screen_share(bus, user_id, room, start).await
}
WsInMessage::UserSummary { username } => {
Self::user_summary(bus, username).await
}
WsInMessage::Search {
q,
room,
limit,
offset,
..
} => Self::search(bus, user_id, q, room, limit, offset).await,
WsInMessage::ConversationPin { room, pin } => {
Self::conversation_pin(bus, user_id, room, pin).await
}
WsInMessage::ConversationMute { room, mute } => {
Self::conversation_mute(bus, user_id, room, mute).await
}
WsInMessage::ConversationNotifyLevel { room, notify_level } => {
Self::conversation_notify_level(
bus,
user_id,
room,
notify_level,
)
.await
}
WsInMessage::ConversationList => {
Self::conversation_list(bus, user_id).await
}
WsInMessage::MessageMarkRead { room, message_ids } => {
Self::message_mark_read(bus, user_id, room, message_ids).await
}
WsInMessage::MessageGetReaders { message_id } => {
Self::message_get_readers(bus, user_id, message_id).await
}
WsInMessage::MessageStar {
room,
message,
star,
} => Self::message_star(bus, user_id, room, message, star).await,
WsInMessage::StarredList { room, limit } => {
Self::starred_list(bus, user_id, room, limit).await
}
WsInMessage::MessageForward {
source_message_id,
target_room,
} => {
Self::message_forward(
bus,
user_id,
source_message_id,
target_room,
)
.await
}
WsInMessage::ArticleCreate {
channel,
title,
cover_url,
content,
content_type,
summary,
tags,
status,
} => {
Self::article_create(
bus, user_id, channel, title, cover_url, content,
content_type, summary, tags, status,
)
.await
}
WsInMessage::ArticleUpdate {
article_id,
title,
cover_url,
content,
content_type,
summary,
tags,
is_pinned,
status,
} => {
Self::article_update(
bus, user_id, article_id, title, cover_url, content,
content_type, summary, tags, is_pinned, status,
)
.await
}
WsInMessage::ArticleDelete { article_id } => {
Self::article_delete(bus, user_id, article_id).await
}
WsInMessage::ArticleList {
channel,
before,
limit,
} => {
Self::article_list(bus, user_id, channel, before, limit).await
}
WsInMessage::ArticleGet { article_id } => {
Self::article_get(bus, user_id, article_id).await
}
WsInMessage::ArticleLike { article_id, like } => {
Self::article_like(bus, user_id, article_id, like).await
}
WsInMessage::ArticleCommentCreate {
article_id,
content,
parent,
} => {
Self::article_comment_create(
bus, user_id, article_id, content, parent,
)
.await
}
WsInMessage::ArticleCommentDelete { comment_id } => {
Self::article_comment_delete(bus, user_id, comment_id).await
}
WsInMessage::ArticleCommentList {
article_id,
before,
limit,
} => {
Self::article_comment_list(
bus, user_id, article_id, before, limit,
)
.await
}
WsInMessage::ArticleLikedUsers {
article_id,
before,
limit,
} => {
Self::article_liked_users(
bus, user_id, article_id, before, limit,
)
.await
}
}
}
}