refactor(chat): refresh storagelayer mappers, repositories, models and chats entity
zhenyi committed
Modified chat/storagelayer/mapper/attachment.rs chat/storagelayer/mapper/attachment.rs +26 −9 View chat/storagelayer/mapper/attachment.rs
@@ -5,6 +5,7 @@
55use time::OffsetDateTime;
66use uuid::Uuid;
77
8use crate::auth;
89use crate::error::{ChatStorageError, ChatStorageResult};
910use crate::models::attachment::{Attachment, AttachmentOwner, NewAttachment};
1011use crate::Page;
@@ -28,9 +29,10 @@
2829impl AttachmentRepository for SeaAttachmentRepository {
2930 async fn create_metadata(
3031 &self,
31 _ctx: &ChatStorageCtx,
32 ctx: &ChatStorageCtx,
3233 input: NewAttachment,
3334 ) -> ChatStorageResult<Attachment> {
35 auth::require_chat_member_for_attachment_owner(&self.db, ctx, input.owner, input.owner_id).await?;
3436 let db = self.db.writer();
3537 let active = att_entity::ActiveModel {
3638 id: Set(Uuid::new_v4()),
@@ -51,23 +53,37 @@
5153
5254 async fn find_by_id(
5355 &self,
54 _ctx: &ChatStorageCtx,
56 ctx: &ChatStorageCtx,
5557 id: Uuid,
5658 ) -> ChatStorageResult<Option<Attachment>> {
5759 let db = self.db.reader();
58 Ok(att_entity::Entity::find_by_id(id)
59 .one(&db)
60 .await?
61 .map(Into::into))
60 let Some(att) = att_entity::Entity::find_by_id(id).one(&db).await? else {
61 return Ok(None);
62 };
63 if let Some(chat_id) =
64 auth::chat_id_for_attachment_owner(&self.db, att.owner_type.into(), att.owner_id).await?
65 {
66 auth::require_member(&self.db, ctx, chat_id).await?;
67 } else {
68 auth::require_authenticated(ctx).await?;
69 }
70 Ok(Some(att.into()))
6271 }
6372
6473 async fn list_for_owner(
6574 &self,
66 _ctx: &ChatStorageCtx,
75 ctx: &ChatStorageCtx,
6776 owner: AttachmentOwner,
6877 owner_id: Uuid,
6978 page: Page,
7079 ) -> ChatStorageResult<Vec<Attachment>> {
80 if let Some(chat_id) =
81 auth::chat_id_for_attachment_owner(&self.db, owner, owner_id).await?
82 {
83 auth::require_member(&self.db, ctx, chat_id).await?;
84 } else {
85 auth::require_authenticated(ctx).await?;
86 }
7187 let db = self.db.reader();
7288 let owner_type: att_entity::OwnerType = owner.into();
7389 let models = att_entity::Entity::find()
@@ -80,7 +96,8 @@
8096 Ok(models.into_iter().map(Into::into).collect())
8197 }
8298
83 async fn delete(&self, _ctx: &ChatStorageCtx, id: Uuid) -> ChatStorageResult<()> {
99 async fn delete(&self, ctx: &ChatStorageCtx, id: Uuid) -> ChatStorageResult<()> {
100 auth::require_uploader_or_admin(&self.db, ctx, id).await?;
84101 let db = self.db.writer();
85102 let res = att_entity::Entity::delete_by_id(id).exec(&db).await?;
86103 if res.rows_affected == 0 {
@@ -93,4 +110,4 @@
93110 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94111 f.debug_struct("SeaAttachmentRepository").finish_non_exhaustive()
95112 }
96}
113}
No newline at end of file
Modified chat/storagelayer/mapper/conversation.rs chat/storagelayer/mapper/conversation.rs +23 −9 View chat/storagelayer/mapper/conversation.rs
@@ -5,6 +5,7 @@
55use time::OffsetDateTime;
66use uuid::Uuid;
77
8use crate::auth;
89use crate::error::{ChatStorageError, ChatStorageResult};
910use crate::models::conversation::{Conversation, ConversationKind, ConversationMember, NewConversation};
1011use crate::Page;
@@ -26,7 +27,8 @@
2627
2728#[async_trait]
2829impl ConversationRepository for SeaConversationRepository {
29 async fn create(&self, _ctx: &ChatStorageCtx, input: NewConversation) -> ChatStorageResult<Conversation> {
30 async fn create(&self, ctx: &ChatStorageCtx, input: NewConversation) -> ChatStorageResult<Conversation> {
31 let creator = auth::require_authenticated(ctx).await?;
3032 let db = self.db.writer();
3133 let id = Uuid::new_v4();
3234 let now = OffsetDateTime::now_utc();
@@ -37,12 +39,17 @@
3739 kind: Set(input.kind.into()),
3840 counterpart_user_id: Set(input.counterpart_user_id),
3941 title: Set(input.title.clone()),
42 creator_user_id: Set(creator),
4043 created_at: Set(now),
4144 updated_at: Set(None),
4245 };
4346 chat_entity::Entity::insert(chat).exec(&db).await?;
4447
45 for user_id in input.initial_members {
48 let mut initial: Vec<Uuid> = input.initial_members.clone();
49 if !initial.contains(&creator) {
50 initial.push(creator);
51 }
52 for user_id in initial {
4653 let member = member_entity::ActiveModel {
4754 chat_id: Set(id),
4855 user_id: Set(user_id),
@@ -59,17 +66,20 @@
5966 kind: input.kind,
6067 counterpart_user_id: input.counterpart_user_id,
6168 title: input.title,
69 creator_user_id: creator,
6270 created_at: now,
6371 updated_at: None,
6472 })
6573 }
6674
67 async fn find_by_id(&self, _ctx: &ChatStorageCtx, id: Uuid) -> ChatStorageResult<Option<Conversation>> {
75 async fn find_by_id(&self, ctx: &ChatStorageCtx, id: Uuid) -> ChatStorageResult<Option<Conversation>> {
76 auth::require_member(&self.db, ctx, id).await?;
6877 let db = self.db.reader();
6978 Ok(chat_entity::Entity::find_by_id(id).one(&db).await?.map(Into::into))
7079 }
7180
72 async fn list_by_channel(&self, _ctx: &ChatStorageCtx, channel_id: Uuid, page: Page) -> ChatStorageResult<Vec<Conversation>> {
81 async fn list_by_channel(&self, ctx: &ChatStorageCtx, channel_id: Uuid, page: Page) -> ChatStorageResult<Vec<Conversation>> {
82 auth::require_authenticated(ctx).await?;
7383 let db = self.db.reader();
7484 let limit = page.limit as u64;
7585 let rows = chat_entity::Entity::find()
@@ -81,7 +91,8 @@
8191 Ok(rows.into_iter().map(Into::into).collect())
8292 }
8393
84 async fn update_title(&self, _ctx: &ChatStorageCtx, id: Uuid, title: Option<String>) -> ChatStorageResult<()> {
94 async fn update_title(&self, ctx: &ChatStorageCtx, id: Uuid, title: Option<String>) -> ChatStorageResult<()> {
95 auth::require_admin(&self.db, ctx, id).await?;
8596 let db = self.db.writer();
8697 let Some(chat) = chat_entity::Entity::find_by_id(id).one(&db).await? else {
8798 return Err(ChatStorageError::ConversationNotFound(id));
@@ -93,7 +104,8 @@
93104 Ok(())
94105 }
95106
96 async fn update_kind(&self, _ctx: &ChatStorageCtx, id: Uuid, kind: ConversationKind) -> ChatStorageResult<()> {
107 async fn update_kind(&self, ctx: &ChatStorageCtx, id: Uuid, kind: ConversationKind) -> ChatStorageResult<()> {
108 auth::require_admin(&self.db, ctx, id).await?;
97109 let db = self.db.writer();
98110 let Some(chat) = chat_entity::Entity::find_by_id(id).one(&db).await? else {
99111 return Err(ChatStorageError::ConversationNotFound(id));
@@ -105,7 +117,8 @@
105117 Ok(())
106118 }
107119
108 async fn delete(&self, _ctx: &ChatStorageCtx, id: Uuid) -> ChatStorageResult<()> {
120 async fn delete(&self, ctx: &ChatStorageCtx, id: Uuid) -> ChatStorageResult<()> {
121 auth::require_admin(&self.db, ctx, id).await?;
109122 let db = self.db.writer();
110123 let result = chat_entity::Entity::delete_by_id(id).exec(&db).await?;
111124 if result.rows_affected == 0 {
@@ -123,7 +136,8 @@
123136 Ok(chat_entity::Entity::find_by_id(id).count(&db).await? > 0)
124137 }
125138
126 async fn list_members(&self, _ctx: &ChatStorageCtx, chat_id: Uuid, page: Page) -> ChatStorageResult<Vec<ConversationMember>> {
139 async fn list_members(&self, ctx: &ChatStorageCtx, chat_id: Uuid, page: Page) -> ChatStorageResult<Vec<ConversationMember>> {
140 auth::require_member(&self.db, ctx, chat_id).await?;
127141 let db = self.db.reader();
128142 let rows = member_entity::Entity::find()
129143 .filter(member_entity::COLUMN.chat_id.eq(chat_id))
@@ -138,4 +152,4 @@
138152 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
139153 f.debug_struct("SeaConversationRepository").finish_non_exhaustive()
140154 }
141}
155}
No newline at end of file
Modified chat/storagelayer/mapper/member.rs chat/storagelayer/mapper/member.rs +13 −7 View chat/storagelayer/mapper/member.rs
@@ -28,10 +28,11 @@
2828impl MemberRepository for SeaMemberRepository {
2929 async fn add_member(
3030 &self,
31 _ctx: &ChatStorageCtx,
31 ctx: &ChatStorageCtx,
3232 chat_id: Uuid,
3333 user_id: Uuid,
3434 ) -> ChatStorageResult<ConversationMember> {
35 crate::auth::require_admin(&self.db, ctx, chat_id).await?;
3536 let db = self.db.writer();
3637 let now = OffsetDateTime::now_utc();
3738 let active = member_entity::ActiveModel {
@@ -63,10 +64,14 @@
6364
6465 async fn remove_member(
6566 &self,
66 _ctx: &ChatStorageCtx,
67 ctx: &ChatStorageCtx,
6768 chat_id: Uuid,
6869 user_id: Uuid,
6970 ) -> ChatStorageResult<()> {
71 let caller = crate::auth::require_authenticated(ctx).await?;
72 if caller != user_id {
73 crate::auth::require_admin(&self.db, ctx, chat_id).await?;
74 }
7075 let db = self.db.writer();
7176 let res = member_entity::Entity::delete_many()
7277 .filter(member_entity::Entity::COLUMN.chat_id.eq(chat_id))
@@ -81,10 +86,11 @@
8186
8287 async fn list_members(
8388 &self,
84 _ctx: &ChatStorageCtx,
89 ctx: &ChatStorageCtx,
8590 chat_id: Uuid,
8691 page: Page,
8792 ) -> ChatStorageResult<Vec<ConversationMember>> {
93 crate::auth::require_member(&self.db, ctx, chat_id).await?;
8894 let db = self.db.reader();
8995 let models = member_entity::Entity::find()
9096 .filter(member_entity::Entity::COLUMN.chat_id.eq(chat_id))
@@ -97,11 +103,11 @@
97103
98104 async fn mark_read(
99105 &self,
100 _ctx: &ChatStorageCtx,
106 ctx: &ChatStorageCtx,
101107 chat_id: Uuid,
102 user_id: Uuid,
103108 at: OffsetDateTime,
104109 ) -> ChatStorageResult<()> {
110 let user_id = crate::auth::require_self(ctx, crate::auth::require_member(&self.db, ctx, chat_id).await?)?;
105111 let db = self.db.writer();
106112 let res = member_entity::Entity::update_many()
107113 .filter(member_entity::Entity::COLUMN.chat_id.eq(chat_id))
@@ -120,11 +126,11 @@
120126
121127 async fn set_muted(
122128 &self,
123 _ctx: &ChatStorageCtx,
129 ctx: &ChatStorageCtx,
124130 chat_id: Uuid,
125 user_id: Uuid,
126131 muted: bool,
127132 ) -> ChatStorageResult<()> {
133 let user_id = crate::auth::require_self(ctx, crate::auth::require_member(&self.db, ctx, chat_id).await?)?;
128134 let db = self.db.writer();
129135 let res = member_entity::Entity::update_many()
130136 .filter(member_entity::Entity::COLUMN.chat_id.eq(chat_id))
Modified chat/storagelayer/mapper/mention.rs chat/storagelayer/mapper/mention.rs +9 −3 View chat/storagelayer/mapper/mention.rs
@@ -5,6 +5,7 @@
55use time::OffsetDateTime;
66use uuid::Uuid;
77
8use crate::auth;
89use crate::error::ChatStorageResult;
910use crate::models::mention::{Mention, NewMention};
1011use crate::repository::mention::MentionRepository;
@@ -26,9 +27,11 @@
2627impl MentionRepository for SeaMentionRepository {
2728 async fn add_mention(
2829 &self,
29 _ctx: &ChatStorageCtx,
30 ctx: &ChatStorageCtx,
3031 input: NewMention,
3132 ) -> ChatStorageResult<Mention> {
33 let chat_id = auth::chat_id_for_message(&self.db, input.message_id).await?;
34 auth::require_member(&self.db, ctx, chat_id).await?;
3235 let db = self.db.writer();
3336 let active = mention_entity::ActiveModel {
3437 id: Set(Uuid::new_v4()),
@@ -48,9 +51,11 @@
4851
4952 async fn list_for_message(
5053 &self,
51 _ctx: &ChatStorageCtx,
54 ctx: &ChatStorageCtx,
5255 message_id: Uuid,
5356 ) -> ChatStorageResult<Vec<Mention>> {
57 let chat_id = auth::chat_id_for_message(&self.db, message_id).await?;
58 auth::require_member(&self.db, ctx, chat_id).await?;
5459 let db = self.db.reader();
5560 let models = mention_entity::Entity::find()
5661 .filter(mention_entity::Entity::COLUMN.message_id.eq(message_id))
@@ -62,10 +67,11 @@
6267
6368 async fn list_for_user(
6469 &self,
65 _ctx: &ChatStorageCtx,
70 ctx: &ChatStorageCtx,
6671 user_id: Uuid,
6772 limit: u32,
6873 ) -> ChatStorageResult<Vec<Mention>> {
74 auth::require_self(ctx, user_id)?;
6975 let db = self.db.reader();
7076 let models = mention_entity::Entity::find()
7177 .filter(
Modified chat/storagelayer/mapper/message.rs chat/storagelayer/mapper/message.rs +19 −9 View chat/storagelayer/mapper/message.rs
@@ -36,9 +36,11 @@
3636impl MessageRepository for SeaMessageRepository {
3737 async fn create(
3838 &self,
39 _ctx: &ChatStorageCtx,
39 ctx: &ChatStorageCtx,
4040 input: NewMessage,
4141 ) -> ChatStorageResult<Message> {
42 crate::auth::require_member(&self.db, ctx, input.chat_id).await?;
43 crate::auth::require_self(ctx, input.sender_id)?;
4244 let db = self.db.writer();
4345 let id = Uuid::new_v4();
4446 let now = OffsetDateTime::now_utc();
@@ -141,7 +143,9 @@
141143 Ok(created)
142144 }
143145
144 async fn find_by_id(&self, _ctx: &ChatStorageCtx, id: Uuid) -> ChatStorageResult<Option<Message>> {
146 async fn find_by_id(&self, ctx: &ChatStorageCtx, id: Uuid) -> ChatStorageResult<Option<Message>> {
147 let chat_id = crate::auth::chat_id_for_message(&self.db, id).await?;
148 crate::auth::require_member(&self.db, ctx, chat_id).await?;
145149 let db = self.db.reader();
146150 let model = message_entity::Entity::find_by_id(id).one(&db).await?;
147151 Ok(model.map(Into::into))
@@ -149,10 +153,11 @@
149153
150154 async fn list_by_conversation(
151155 &self,
152 _ctx: &ChatStorageCtx,
156 ctx: &ChatStorageCtx,
153157 chat_id: Uuid,
154158 query: MessageQuery,
155159 ) -> ChatStorageResult<MessagePage> {
160 crate::auth::require_member(&self.db, ctx, chat_id).await?;
156161 let db = self.db.reader();
157162 let mut select =
158163 message_entity::Entity::find().filter(message_entity::COLUMN.chat_id.eq(chat_id));
@@ -219,11 +224,12 @@
219224
220225 async fn list_thread(
221226 &self,
222 _ctx: &ChatStorageCtx,
227 ctx: &ChatStorageCtx,
223228 chat_id: Uuid,
224229 root_message_id: Uuid,
225230 page: Page,
226231 ) -> ChatStorageResult<MessagePage> {
232 crate::auth::require_member(&self.db, ctx, chat_id).await?;
227233 let db = self.db.reader();
228234 let models = message_entity::Entity::find()
229235 .filter(message_entity::COLUMN.chat_id.eq(chat_id))
@@ -239,11 +245,11 @@
239245
240246 async fn edit(
241247 &self,
242 _ctx: &ChatStorageCtx,
248 ctx: &ChatStorageCtx,
243249 id: Uuid,
244 _editor: Uuid,
245250 new_body: String,
246251 ) -> ChatStorageResult<Message> {
252 crate::auth::require_sender_or_admin(&self.db, ctx, id).await?;
247253 let db = self.db.writer();
248254 let model = message_entity::Entity::find_by_id(id)
249255 .one(&db)
@@ -258,7 +264,8 @@
258264 Ok(updated.into())
259265 }
260266
261 async fn soft_delete(&self, _ctx: &ChatStorageCtx, id: Uuid, by: Uuid) -> ChatStorageResult<()> {
267 async fn soft_delete(&self, ctx: &ChatStorageCtx, id: Uuid) -> ChatStorageResult<()> {
268 crate::auth::require_sender_or_admin(&self.db, ctx, id).await?;
262269 let db = self.db.writer();
263270 let model = message_entity::Entity::find_by_id(id)
264271 .one(&db)
@@ -267,13 +274,16 @@
267274 let flags = MessageFlags::from_bits(model.flags);
268275 let mut active = model.into_active_model();
269276 active.deleted_at = Set(Some(OffsetDateTime::now_utc()));
270 active.deleted_by = Set(Some(by));
277 let actor = ctx.require_user()?;
278 active.deleted_by = Set(Some(actor));
271279 active.flags = Set(flags.with(MessageFlags::DELETED).bits());
272280 active.update(&db).await?;
273281 Ok(())
274282 }
275283
276 async fn restore(&self, _ctx: &ChatStorageCtx, id: Uuid) -> ChatStorageResult<()> {
284 async fn restore(&self, ctx: &ChatStorageCtx, id: Uuid) -> ChatStorageResult<()> {
285 let chat_id = crate::auth::chat_id_for_message(&self.db, id).await?;
286 crate::auth::require_admin(&self.db, ctx, chat_id).await?;
277287 let db = self.db.writer();
278288 let model = message_entity::Entity::find_by_id(id)
279289 .one(&db)
Modified chat/storagelayer/mapper/pinned.rs chat/storagelayer/mapper/pinned.rs +9 −4 View chat/storagelayer/mapper/pinned.rs
@@ -5,6 +5,7 @@
55use time::OffsetDateTime;
66use uuid::Uuid;
77
8use crate::auth;
89use crate::error::{ChatStorageError, ChatStorageResult};
910use crate::models::pinned::{NewPinnedMessage, PinnedMessage};
1011use crate::repository::pinned::PinnedMessageRepository;
@@ -26,9 +27,11 @@
2627impl PinnedMessageRepository for SeaPinnedMessageRepository {
2728 async fn pin(
2829 &self,
29 _ctx: &ChatStorageCtx,
30 ctx: &ChatStorageCtx,
3031 input: NewPinnedMessage,
3132 ) -> ChatStorageResult<PinnedMessage> {
33 auth::require_admin(&self.db, ctx, input.chat_id).await?;
34 auth::require_self(ctx, input.pinned_by)?;
3235 let db = self.db.writer();
3336 let existing = pinned_entity::Entity::find()
3437 .filter(pinned_entity::Entity::COLUMN.chat_id.eq(input.chat_id))
@@ -53,10 +56,11 @@
5356
5457 async fn unpin(
5558 &self,
56 _ctx: &ChatStorageCtx,
59 ctx: &ChatStorageCtx,
5760 chat_id: Uuid,
5861 message_id: Uuid,
5962 ) -> ChatStorageResult<()> {
63 auth::require_admin(&self.db, ctx, chat_id).await?;
6064 let db = self.db.writer();
6165 let found = pinned_entity::Entity::find()
6266 .filter(pinned_entity::Entity::COLUMN.chat_id.eq(chat_id))
@@ -75,9 +79,10 @@
7579
7680 async fn list_for_chat(
7781 &self,
78 _ctx: &ChatStorageCtx,
82 ctx: &ChatStorageCtx,
7983 chat_id: Uuid,
8084 ) -> ChatStorageResult<Vec<PinnedMessage>> {
85 auth::require_member(&self.db, ctx, chat_id).await?;
8186 let db = self.db.reader();
8287 let models = pinned_entity::Entity::find()
8388 .filter(pinned_entity::Entity::COLUMN.chat_id.eq(chat_id))
@@ -91,4 +96,4 @@
9196 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
9297 f.debug_struct("SeaPinnedMessageRepository").finish_non_exhaustive()
9398 }
94}
99}
No newline at end of file
Modified chat/storagelayer/mapper/reaction.rs chat/storagelayer/mapper/reaction.rs +12 −5 View chat/storagelayer/mapper/reaction.rs
@@ -5,6 +5,7 @@
55use time::OffsetDateTime;
66use uuid::Uuid;
77
8use crate::auth;
89use crate::error::{ChatStorageError, ChatStorageResult};
910use crate::models::reaction::{NewReaction, Reaction};
1011use crate::repository::reaction::ReactionRepository;
@@ -26,9 +27,12 @@
2627impl ReactionRepository for SeaReactionRepository {
2728 async fn add_reaction(
2829 &self,
29 _ctx: &ChatStorageCtx,
30 ctx: &ChatStorageCtx,
3031 input: NewReaction,
3132 ) -> ChatStorageResult<Reaction> {
33 let chat_id = auth::chat_id_for_message(&self.db, input.message_id).await?;
34 auth::require_member(&self.db, ctx, chat_id).await?;
35 auth::require_self(ctx, input.user_id)?;
3236 let db = self.db.writer();
3337 let existing = reaction_entity::Entity::find()
3438 .filter(reaction_entity::Entity::COLUMN.message_id.eq(input.message_id))
@@ -54,11 +58,12 @@
5458
5559 async fn remove_reaction(
5660 &self,
57 _ctx: &ChatStorageCtx,
61 ctx: &ChatStorageCtx,
5862 message_id: Uuid,
59 user_id: Uuid,
6063 emoji: &str,
6164 ) -> ChatStorageResult<()> {
65 let chat_id = auth::chat_id_for_message(&self.db, message_id).await?;
66 let user_id = auth::require_member(&self.db, ctx, chat_id).await?;
6267 let db = self.db.writer();
6368 let res = reaction_entity::Entity::delete_many()
6469 .filter(reaction_entity::Entity::COLUMN.message_id.eq(message_id))
@@ -74,9 +79,11 @@
7479
7580 async fn list_reactions(
7681 &self,
77 _ctx: &ChatStorageCtx,
82 ctx: &ChatStorageCtx,
7883 message_id: Uuid,
7984 ) -> ChatStorageResult<Vec<Reaction>> {
85 let chat_id = auth::chat_id_for_message(&self.db, message_id).await?;
86 auth::require_member(&self.db, ctx, chat_id).await?;
8087 let db = self.db.reader();
8188 let models = reaction_entity::Entity::find()
8289 .filter(reaction_entity::Entity::COLUMN.message_id.eq(message_id))
@@ -90,4 +97,4 @@
9097 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
9198 f.debug_struct("SeaReactionRepository").finish_non_exhaustive()
9299 }
93}
100}
No newline at end of file
Modified chat/storagelayer/mapper/read_receipt.rs chat/storagelayer/mapper/read_receipt.rs +8 −3 View chat/storagelayer/mapper/read_receipt.rs
@@ -5,6 +5,7 @@
55use time::OffsetDateTime;
66use uuid::Uuid;
77
8use crate::auth;
89use crate::error::ChatStorageResult;
910use crate::models::read_receipt::{NewReadReceipt, ReadReceipt};
1011use crate::repository::read_receipt::ReadReceiptRepository;
@@ -26,9 +27,11 @@
2627impl ReadReceiptRepository for SeaReadReceiptRepository {
2728 async fn upsert(
2829 &self,
29 _ctx: &ChatStorageCtx,
30 ctx: &ChatStorageCtx,
3031 input: NewReadReceipt,
3132 ) -> ChatStorageResult<ReadReceipt> {
33 auth::require_member(&self.db, ctx, input.chat_id).await?;
34 auth::require_self(ctx, input.user_id)?;
3235 let db = self.db.writer();
3336 let now = OffsetDateTime::now_utc();
3437 let existing = read_entity::Entity::find()
@@ -63,9 +66,10 @@
6366
6467 async fn list_for_chat(
6568 &self,
66 _ctx: &ChatStorageCtx,
69 ctx: &ChatStorageCtx,
6770 chat_id: Uuid,
6871 ) -> ChatStorageResult<Vec<ReadReceipt>> {
72 auth::require_member(&self.db, ctx, chat_id).await?;
6973 let db = self.db.reader();
7074 let models = read_entity::Entity::find()
7175 .filter(read_entity::Entity::COLUMN.chat_id.eq(chat_id))
@@ -77,11 +81,12 @@
7781
7882 async fn find(
7983 &self,
80 _ctx: &ChatStorageCtx,
84 ctx: &ChatStorageCtx,
8185 chat_id: Uuid,
8286 user_id: Uuid,
8387 thread_id: Option<Uuid>,
8488 ) -> ChatStorageResult<Option<ReadReceipt>> {
89 auth::require_member(&self.db, ctx, chat_id).await?;
8590 let db = self.db.reader();
8691 let model = read_entity::Entity::find()
8792 .filter(read_entity::Entity::COLUMN.chat_id.eq(chat_id))
Modified chat/storagelayer/mapper/thread.rs chat/storagelayer/mapper/thread.rs +5 −2 View chat/storagelayer/mapper/thread.rs
@@ -5,6 +5,7 @@
55use time::OffsetDateTime;
66use uuid::Uuid;
77
8use crate::auth;
89use crate::error::{ChatStorageError, ChatStorageResult};
910use crate::models::thread::Thread;
1011use crate::repository::thread::ThreadRepository;
@@ -26,12 +27,13 @@
2627impl ThreadRepository for SeaThreadRepository {
2728 async fn increment_reply(
2829 &self,
29 _ctx: &ChatStorageCtx,
30 ctx: &ChatStorageCtx,
3031 chat_id: Uuid,
3132 root_message_id: Uuid,
3233 reply_by: Uuid,
3334 at: OffsetDateTime,
3435 ) -> ChatStorageResult<Thread> {
36 auth::require_member(&self.db, ctx, chat_id).await?;
3537 let db = self.db.writer();
3638 let existing = thread_entity::Entity::find_by_id((chat_id, root_message_id))
3739 .one(&db)
@@ -82,9 +84,10 @@
8284
8385 async fn list_for_chat(
8486 &self,
85 _ctx: &ChatStorageCtx,
87 ctx: &ChatStorageCtx,
8688 chat_id: Uuid,
8789 ) -> ChatStorageResult<Vec<Thread>> {
90 auth::require_member(&self.db, ctx, chat_id).await?;
8891 let db = self.db.reader();
8992 let models = thread_entity::Entity::find()
9093 .filter(thread_entity::Entity::COLUMN.chat_id.eq(chat_id))
Modified chat/storagelayer/mock/in_memory.rs chat/storagelayer/mock/in_memory.rs +591 −144 View chat/storagelayer/mock/in_memory.rs
@@ -1,21 +1,23 @@
11use std::collections::HashMap;
22use std::sync::Arc;
33use std::sync::RwLock;
4use std::time::Duration;
45
56use async_trait::async_trait;
7use bytes::Bytes;
68use time::OffsetDateTime;
9use url::Url;
710use uuid::Uuid;
811
12use crate::blob::media::AttachmentPath;
913use crate::blob::ChatMediaStore;
1014use crate::error::{ChatStorageError, ChatStorageResult};
11use crate::blob::media::AttachmentPath;
12use bytes::Bytes;
13use std::time::Duration;
14use url::Url;
1515use crate::models::attachment::{Attachment, AttachmentOwner, NewAttachment};
1616use crate::models::conversation::{Conversation, ConversationKind, ConversationMember, NewConversation};
1717use crate::models::mention::{Mention, NewMention};
18use crate::models::message::{Message, MessageFlags, MessagePage, MessageQuery, NewMessage, Page};
18use crate::models::message::{
19 ListDirection, Message, MessageFlags, MessagePage, MessageQuery, NewMessage, Page,
20};
1921use crate::models::pinned::{NewPinnedMessage, PinnedMessage};
2022use crate::models::reaction::{NewReaction, Reaction};
2123use crate::models::read_receipt::{NewReadReceipt, ReadReceipt};
@@ -30,11 +32,11 @@
3032use crate::repository::read_receipt::{DynReadReceiptRepository, ReadReceiptRepository};
3133use crate::repository::thread::{DynThreadRepository, ThreadRepository};
3234use crate::repository::{ChatStorage, ChatStorageCtx, DynChatStorage};
33
3435
3536#[derive(Debug, Default)]
3637pub struct InMemoryState {
3738 pub conversations: RwLock<HashMap<Uuid, Conversation>>,
39 pub creators: RwLock<HashMap<Uuid, Uuid>>,
3840 pub messages: RwLock<HashMap<Uuid, Message>>,
3941 pub attachments: RwLock<HashMap<Uuid, Attachment>>,
4042 pub members: RwLock<HashMap<(Uuid, Uuid), ConversationMember>>,
@@ -51,6 +53,110 @@
5153 ctx.require_user()
5254}
5355
56fn is_member(state: &SharedState, chat_id: Uuid, user_id: Uuid) -> bool {
57 state.members.read().unwrap().contains_key(&(chat_id, user_id))
58}
59
60fn is_admin(state: &SharedState, chat_id: Uuid, user_id: Uuid) -> bool {
61 state
62 .creators
63 .read()
64 .unwrap()
65 .get(&chat_id)
66 .copied()
67 .map(|c| c == user_id)
68 .unwrap_or(false)
69}
70
71fn chat_id_for_message(state: &SharedState, message_id: Uuid) -> Option<Uuid> {
72 state
73 .messages
74 .read()
75 .unwrap()
76 .get(&message_id)
77 .map(|m| m.chat_id)
78}
79
80fn chat_id_for_attachment(state: &SharedState, att: &Attachment) -> Option<Uuid> {
81 if att.owner == AttachmentOwner::ChatMessage {
82 chat_id_for_message(state, att.owner_id)
83 } else {
84 None
85 }
86}
87
88fn require_member(state: &SharedState, ctx: &ChatStorageCtx, chat_id: Uuid) -> ChatStorageResult<Uuid> {
89 let user_id = require_user(ctx)?;
90 if !is_member(state, chat_id, user_id) {
91 return Err(ChatStorageError::PermissionDenied(format!(
92 "user {user_id} is not a member of chat {chat_id}"
93 )));
94 }
95 Ok(user_id)
96}
97
98fn require_admin(state: &SharedState, ctx: &ChatStorageCtx, chat_id: Uuid) -> ChatStorageResult<Uuid> {
99 let user_id = require_user(ctx)?;
100 if !is_admin(state, chat_id, user_id) {
101 return Err(ChatStorageError::PermissionDenied(format!(
102 "user {user_id} is not admin of chat {chat_id}"
103 )));
104 }
105 Ok(user_id)
106}
107
108fn require_sender_or_admin(
109 state: &SharedState,
110 ctx: &ChatStorageCtx,
111 message_id: Uuid,
112) -> ChatStorageResult<Uuid> {
113 let user_id = require_user(ctx)?;
114 let msg = state
115 .messages
116 .read()
117 .unwrap()
118 .get(&message_id)
119 .cloned()
120 .ok_or(ChatStorageError::MessageNotFound(message_id))?;
121 if msg.sender_id == user_id {
122 return Ok(user_id);
123 }
124 if is_admin(state, msg.chat_id, user_id) {
125 return Ok(user_id);
126 }
127 Err(ChatStorageError::PermissionDenied(format!(
128 "user {user_id} is neither sender nor admin of message {message_id}"
129 )))
130}
131
132fn require_uploader_or_admin(
133 state: &SharedState,
134 ctx: &ChatStorageCtx,
135 attachment_id: Uuid,
136) -> ChatStorageResult<Uuid> {
137 let user_id = require_user(ctx)?;
138 let att = state
139 .attachments
140 .read()
141 .unwrap()
142 .get(&attachment_id)
143 .cloned()
144 .ok_or(ChatStorageError::AttachmentNotFound(attachment_id))?;
145 if att.uploaded_by == Some(user_id) {
146 return Ok(user_id);
147 }
148 let chat_id = chat_id_for_attachment(state, &att).ok_or_else(|| {
149 ChatStorageError::PermissionDenied(format!(
150 "user {user_id} cannot delete attachment {attachment_id}: owner not found"
151 ))
152 })?;
153 if is_admin(state, chat_id, user_id) {
154 return Ok(user_id);
155 }
156 Err(ChatStorageError::PermissionDenied(format!(
157 "user {user_id} cannot delete attachment {attachment_id}"
158 )))
159}
54160
55161#[derive(Debug)]
56162pub struct InMemoryConversationRepository {
@@ -65,8 +171,12 @@
65171
66172#[async_trait]
67173impl ConversationRepository for InMemoryConversationRepository {
68 async fn create(&self, ctx: &ChatStorageCtx, input: NewConversation) -> ChatStorageResult<Conversation> {
69 require_user(ctx)?;
174 async fn create(
175 &self,
176 ctx: &ChatStorageCtx,
177 input: NewConversation,
178 ) -> ChatStorageResult<Conversation> {
179 let creator = require_user(ctx)?;
70180 let now = OffsetDateTime::now_utc();
71181 let conv = Conversation {
72182 id: Uuid::new_v4(),
@@ -74,66 +184,115 @@
74184 kind: input.kind,
75185 counterpart_user_id: input.counterpart_user_id,
76186 title: input.title,
187 creator_user_id: creator,
77188 created_at: now,
78189 updated_at: Some(now),
79190 };
80 self.state.conversations.write().unwrap().insert(conv.id, conv.clone());
191 self.state
192 .conversations
193 .write()
194 .unwrap()
195 .insert(conv.id, conv.clone());
196 self.state
197 .creators
198 .write()
199 .unwrap()
200 .insert(conv.id, creator);
81201
82202 let joined_at = now;
83203 let mut members = self.state.members.write().unwrap();
84 for uid in &input.initial_members {
204 let mut initial = input.initial_members.clone();
205 if !initial.contains(&creator) {
206 initial.push(creator);
207 }
208 for uid in initial {
85209 let m = ConversationMember {
86210 chat_id: conv.id,
87 user_id: *uid,
211 user_id: uid,
88212 joined_at,
89213 last_read_at: None,
90214 muted: false,
91215 };
92 members.insert((conv.id, *uid), m);
216 members.insert((conv.id, uid), m);
93217 }
94218 Ok(conv)
95219 }
96220
97 async fn find_by_id(&self, _ctx: &ChatStorageCtx, id: Uuid) -> ChatStorageResult<Option<Conversation>> {
221 async fn find_by_id(
222 &self,
223 ctx: &ChatStorageCtx,
224 id: Uuid,
225 ) -> ChatStorageResult<Option<Conversation>> {
226 require_member(&self.state, ctx, id)?;
98227 Ok(self.state.conversations.read().unwrap().get(&id).cloned())
99228 }
100229
101 async fn list_by_channel(&self, _ctx: &ChatStorageCtx, channel_id: Uuid, page: Page) -> ChatStorageResult<Vec<Conversation>> {
230 async fn list_by_channel(
231 &self,
232 ctx: &ChatStorageCtx,
233 channel_id: Uuid,
234 page: Page,
235 ) -> ChatStorageResult<Vec<Conversation>> {
236 require_user(ctx)?;
102237 let map = self.state.conversations.read().unwrap();
103238 let mut out: Vec<Conversation> = map
104239 .values()
105240 .filter(|c| c.channel_id == channel_id)
106241 .cloned()
107242 .collect();
108 out.sort_by_key(|c| c.created_at);
243 out.sort_by_key(|c| std::cmp::Reverse(c.created_at));
109244 let limit = page.limit as usize;
110245 Ok(out.into_iter().take(limit).collect())
111246 }
112247
113 async fn update_title(&self, ctx: &ChatStorageCtx, id: Uuid, title: Option<String>) -> ChatStorageResult<()> {
114 require_user(ctx)?;
248 async fn update_title(
249 &self,
250 ctx: &ChatStorageCtx,
251 id: Uuid,
252 title: Option<String>,
253 ) -> ChatStorageResult<()> {
254 require_admin(&self.state, ctx, id)?;
115255 let mut map = self.state.conversations.write().unwrap();
116 let conv = map.get_mut(&id).ok_or(ChatStorageError::ConversationNotFound(id))?;
256 let conv = map
257 .get_mut(&id)
258 .ok_or(ChatStorageError::ConversationNotFound(id))?;
117259 conv.title = title;
118260 conv.updated_at = Some(OffsetDateTime::now_utc());
119261 Ok(())
120262 }
121263
122 async fn update_kind(&self, ctx: &ChatStorageCtx, id: Uuid, kind: ConversationKind) -> ChatStorageResult<()> {
123 require_user(ctx)?;
264 async fn update_kind(
265 &self,
266 ctx: &ChatStorageCtx,
267 id: Uuid,
268 kind: ConversationKind,
269 ) -> ChatStorageResult<()> {
270 require_admin(&self.state, ctx, id)?;
124271 let mut map = self.state.conversations.write().unwrap();
125 let conv = map.get_mut(&id).ok_or(ChatStorageError::ConversationNotFound(id))?;
272 let conv = map
273 .get_mut(&id)
274 .ok_or(ChatStorageError::ConversationNotFound(id))?;
126275 conv.kind = kind;
127276 conv.updated_at = Some(OffsetDateTime::now_utc());
128277 Ok(())
129278 }
130279
131280 async fn delete(&self, ctx: &ChatStorageCtx, id: Uuid) -> ChatStorageResult<()> {
132 require_user(ctx)?;
281 require_admin(&self.state, ctx, id)?;
133282 let removed = self.state.conversations.write().unwrap().remove(&id).is_some();
134283 if !removed {
135284 return Err(ChatStorageError::ConversationNotFound(id));
136285 }
286 self.state.creators.write().unwrap().remove(&id);
287 let mut members = self.state.members.write().unwrap();
288 let keys: Vec<(Uuid, Uuid)> = members
289 .keys()
290 .filter(|(c, _)| *c == id)
291 .copied()
292 .collect();
293 for k in keys {
294 members.remove(&k);
295 }
137296 Ok(())
138297 }
139298
@@ -141,7 +300,13 @@
141300 Ok(self.state.conversations.read().unwrap().contains_key(&id))
142301 }
143302
144 async fn list_members(&self, _ctx: &ChatStorageCtx, chat_id: Uuid, page: Page) -> ChatStorageResult<Vec<ConversationMember>> {
303 async fn list_members(
304 &self,
305 ctx: &ChatStorageCtx,
306 chat_id: Uuid,
307 page: Page,
308 ) -> ChatStorageResult<Vec<ConversationMember>> {
309 require_member(&self.state, ctx, chat_id)?;
145310 let map = self.state.members.read().unwrap();
146311 let mut out: Vec<ConversationMember> = map
147312 .values()
@@ -153,7 +318,6 @@
153318 Ok(out.into_iter().take(limit).collect())
154319 }
155320}
156
157321
158322#[derive(Debug)]
159323pub struct InMemoryMessageRepository {
@@ -169,12 +333,19 @@
169333#[async_trait]
170334impl MessageRepository for InMemoryMessageRepository {
171335 async fn create(&self, ctx: &ChatStorageCtx, input: NewMessage) -> ChatStorageResult<Message> {
172 let sender_id = ctx.user_id.unwrap_or(input.sender_id);
336 let caller = require_user(ctx)?;
337 require_member(&self.state, ctx, input.chat_id)?;
338 if caller != input.sender_id {
339 return Err(ChatStorageError::PermissionDenied(format!(
340 "user {caller} cannot send as user {}",
341 input.sender_id
342 )));
343 }
173344 let now = OffsetDateTime::now_utc();
174345 let msg = Message {
175346 id: Uuid::new_v4(),
176347 chat_id: input.chat_id,
177 sender_id,
348 sender_id: input.sender_id,
178349 body: input.body,
179350 reply_to_message_id: input.reply_to_message_id,
180351 thread_id: input.thread_id,
@@ -185,19 +356,34 @@
185356 edited_at: None,
186357 created_at: now,
187358 };
188 self.state.messages.write().unwrap().insert(msg.id, msg.clone());
359 self.state
360 .messages
361 .write()
362 .unwrap()
363 .insert(msg.id, msg.clone());
189364 Ok(msg)
190365 }
191366
192 async fn find_by_id(&self, _ctx: &ChatStorageCtx, id: Uuid) -> ChatStorageResult<Option<Message>> {
193 let map = self.state.messages.read().unwrap();
194 let msg = map.get(&id).cloned();
195 let include_deleted = true;
196 let _ = include_deleted;
197 Ok(msg)
367 async fn find_by_id(
368 &self,
369 ctx: &ChatStorageCtx,
370 id: Uuid,
371 ) -> ChatStorageResult<Option<Message>> {
372 let chat_id = match chat_id_for_message(&self.state, id) {
373 Some(c) => c,
374 None => return Ok(None),
375 };
376 require_member(&self.state, ctx, chat_id)?;
377 Ok(self.state.messages.read().unwrap().get(&id).cloned())
198378 }
199379
200 async fn list_by_conversation(&self, _ctx: &ChatStorageCtx, chat_id: Uuid, query: MessageQuery) -> ChatStorageResult<MessagePage> {
380 async fn list_by_conversation(
381 &self,
382 ctx: &ChatStorageCtx,
383 chat_id: Uuid,
384 query: MessageQuery,
385 ) -> ChatStorageResult<MessagePage> {
386 require_member(&self.state, ctx, chat_id)?;
201387 let map = self.state.messages.read().unwrap();
202388 let mut items: Vec<Message> = map
203389 .values()
@@ -205,7 +391,6 @@
205391 .filter(|m| query.include_deleted || m.deleted_at.is_none())
206392 .cloned()
207393 .collect();
208 use crate::models::message::ListDirection;
209394 match query.direction {
210395 ListDirection::Forward => items.sort_by_key(|m| m.created_at),
211396 ListDirection::Backward => items.sort_by_key(|m| std::cmp::Reverse(m.created_at)),
@@ -216,7 +401,14 @@
216401 Ok(MessagePage { items, next_cursor })
217402 }
218403
219 async fn list_thread(&self, _ctx: &ChatStorageCtx, chat_id: Uuid, root_message_id: Uuid, page: Page) -> ChatStorageResult<MessagePage> {
404 async fn list_thread(
405 &self,
406 ctx: &ChatStorageCtx,
407 chat_id: Uuid,
408 root_message_id: Uuid,
409 page: Page,
410 ) -> ChatStorageResult<MessagePage> {
411 require_member(&self.state, ctx, chat_id)?;
220412 let map = self.state.messages.read().unwrap();
221413 let mut items: Vec<Message> = map
222414 .values()
@@ -230,37 +422,55 @@
230422 Ok(MessagePage { items, next_cursor })
231423 }
232424
233 async fn edit(&self, ctx: &ChatStorageCtx, id: Uuid, editor: Uuid, new_body: String) -> ChatStorageResult<Message> {
234 require_user(ctx)?;
425 async fn edit(
426 &self,
427 ctx: &ChatStorageCtx,
428 id: Uuid,
429 new_body: String,
430 ) -> ChatStorageResult<Message> {
431 require_sender_or_admin(&self.state, ctx, id)?;
235432 let now = OffsetDateTime::now_utc();
236433 let mut map = self.state.messages.write().unwrap();
237 let msg = map.get_mut(&id).ok_or(ChatStorageError::MessageNotFound(id))?;
434 let msg = map
435 .get_mut(&id)
436 .ok_or(ChatStorageError::MessageNotFound(id))?;
238437 msg.body = new_body;
239438 msg.edited_at = Some(now);
240 msg.flags = MessageFlags::from_bits_truncate(msg.flags).with(MessageFlags::EDITED).bits();
439 msg.flags = MessageFlags::from_bits_truncate(msg.flags)
440 .with(MessageFlags::EDITED)
441 .bits();
241442 msg.deleted_by = None;
242 let _ = editor;
243443 Ok(msg.clone())
244444 }
245445
246 async fn soft_delete(&self, ctx: &ChatStorageCtx, id: Uuid, by: Uuid) -> ChatStorageResult<()> {
247 require_user(ctx)?;
446 async fn soft_delete(&self, ctx: &ChatStorageCtx, id: Uuid) -> ChatStorageResult<()> {
447 let actor = require_sender_or_admin(&self.state, ctx, id)?;
248448 let now = OffsetDateTime::now_utc();
249449 let mut map = self.state.messages.write().unwrap();
250 let msg = map.get_mut(&id).ok_or(ChatStorageError::MessageNotFound(id))?;
450 let msg = map
451 .get_mut(&id)
452 .ok_or(ChatStorageError::MessageNotFound(id))?;
251453 msg.deleted_at = Some(now);
252 msg.deleted_by = Some(by);
253 msg.flags = MessageFlags::from_bits_truncate(msg.flags).with(MessageFlags::DELETED).bits();
454 msg.deleted_by = Some(actor);
455 msg.flags = MessageFlags::from_bits_truncate(msg.flags)
456 .with(MessageFlags::DELETED)
457 .bits();
254458 Ok(())
255459 }
256460
257461 async fn restore(&self, ctx: &ChatStorageCtx, id: Uuid) -> ChatStorageResult<()> {
258 require_user(ctx)?;
462 let chat_id = chat_id_for_message(&self.state, id)
463 .ok_or(ChatStorageError::MessageNotFound(id))?;
464 require_admin(&self.state, ctx, chat_id)?;
259465 let mut map = self.state.messages.write().unwrap();
260 let msg = map.get_mut(&id).ok_or(ChatStorageError::MessageNotFound(id))?;
466 let msg = map
467 .get_mut(&id)
468 .ok_or(ChatStorageError::MessageNotFound(id))?;
261469 msg.deleted_at = None;
262470 msg.deleted_by = None;
263 msg.flags = MessageFlags::from_bits_truncate(msg.flags).without(MessageFlags::DELETED).bits();
471 msg.flags = MessageFlags::from_bits_truncate(msg.flags)
472 .without(MessageFlags::DELETED)
473 .bits();
264474 Ok(())
265475 }
266476
@@ -268,7 +478,6 @@
268478 Ok(self.state.messages.read().unwrap().contains_key(&id))
269479 }
270480}
271
272481
273482#[derive(Debug)]
274483pub struct InMemoryAttachmentRepository {
@@ -283,8 +492,20 @@
283492
284493#[async_trait]
285494impl AttachmentRepository for InMemoryAttachmentRepository {
286 async fn create_metadata(&self, ctx: &ChatStorageCtx, input: NewAttachment) -> ChatStorageResult<Attachment> {
495 async fn create_metadata(
496 &self,
497 ctx: &ChatStorageCtx,
498 input: NewAttachment,
499 ) -> ChatStorageResult<Attachment> {
287500 require_user(ctx)?;
501 if let Some(chat_id) =
502 chat_id_for_attachment_owner_in_state(&self.state, input.owner, input.owner_id)
503 && !is_member(&self.state, chat_id, require_user(ctx)?)
504 {
505 return Err(ChatStorageError::PermissionDenied(format!(
506 "user not a member of chat {chat_id}"
507 )));
508 }
288509 let att = Attachment {
289510 id: Uuid::new_v4(),
290511 owner: input.owner,
@@ -296,15 +517,43 @@
296517 uploaded_by: input.uploaded_by,
297518 created_at: OffsetDateTime::now_utc(),
298519 };
299 self.state.attachments.write().unwrap().insert(att.id, att.clone());
520 self.state
521 .attachments
522 .write()
523 .unwrap()
524 .insert(att.id, att.clone());
300525 Ok(att)
301526 }
302527
303 async fn find_by_id(&self, _ctx: &ChatStorageCtx, id: Uuid) -> ChatStorageResult<Option<Attachment>> {
304 Ok(self.state.attachments.read().unwrap().get(&id).cloned())
528 async fn find_by_id(
529 &self,
530 ctx: &ChatStorageCtx,
531 id: Uuid,
532 ) -> ChatStorageResult<Option<Attachment>> {
533 let att = self.state.attachments.read().unwrap().get(&id).cloned();
534 let Some(att) = att else {
535 return Ok(None);
536 };
537 if let Some(chat_id) = chat_id_for_attachment(&self.state, &att) {
538 require_member(&self.state, ctx, chat_id)?;
539 } else {
540 require_user(ctx)?;
541 }
542 Ok(Some(att))
305543 }
306544
307 async fn list_for_owner(&self, _ctx: &ChatStorageCtx, owner: AttachmentOwner, owner_id: Uuid, page: Page) -> ChatStorageResult<Vec<Attachment>> {
545 async fn list_for_owner(
546 &self,
547 ctx: &ChatStorageCtx,
548 owner: AttachmentOwner,
549 owner_id: Uuid,
550 page: Page,
551 ) -> ChatStorageResult<Vec<Attachment>> {
552 if let Some(chat_id) = chat_id_for_attachment_owner_in_state(&self.state, owner, owner_id) {
553 require_member(&self.state, ctx, chat_id)?;
554 } else {
555 require_user(ctx)?;
556 }
308557 let map = self.state.attachments.read().unwrap();
309558 let mut items: Vec<Attachment> = map
310559 .values()
@@ -317,7 +566,7 @@
317566 }
318567
319568 async fn delete(&self, ctx: &ChatStorageCtx, id: Uuid) -> ChatStorageResult<()> {
320 require_user(ctx)?;
569 require_uploader_or_admin(&self.state, ctx, id)?;
321570 let removed = self.state.attachments.write().unwrap().remove(&id).is_some();
322571 if !removed {
323572 return Err(ChatStorageError::AttachmentNotFound(id));
@@ -326,6 +575,16 @@
326575 }
327576}
328577
578fn chat_id_for_attachment_owner_in_state(
579 state: &SharedState,
580 owner: AttachmentOwner,
581 owner_id: Uuid,
582) -> Option<Uuid> {
583 match owner {
584 AttachmentOwner::ChatMessage => chat_id_for_message(state, owner_id),
585 _ => None,
586 }
587}
329588
330589#[derive(Debug)]
331590pub struct InMemoryMemberRepository {
@@ -340,8 +599,13 @@
340599
341600#[async_trait]
342601impl MemberRepository for InMemoryMemberRepository {
343 async fn add_member(&self, ctx: &ChatStorageCtx, chat_id: Uuid, user_id: Uuid) -> ChatStorageResult<ConversationMember> {
344 require_user(ctx)?;
602 async fn add_member(
603 &self,
604 ctx: &ChatStorageCtx,
605 chat_id: Uuid,
606 user_id: Uuid,
607 ) -> ChatStorageResult<ConversationMember> {
608 require_admin(&self.state, ctx, chat_id)?;
345609 let m = ConversationMember {
346610 chat_id,
347611 user_id,
@@ -349,58 +613,99 @@
349613 last_read_at: None,
350614 muted: false,
351615 };
352 self.state.members.write().unwrap().insert((chat_id, user_id), m.clone());
616 self.state
617 .members
618 .write()
619 .unwrap()
620 .insert((chat_id, user_id), m.clone());
353621 Ok(m)
354622 }
355623
356 async fn remove_member(&self, ctx: &ChatStorageCtx, chat_id: Uuid, user_id: Uuid) -> ChatStorageResult<()> {
357 require_user(ctx)?;
358 let removed = self.state.members.write().unwrap().remove(&(chat_id, user_id)).is_some();
624 async fn remove_member(
625 &self,
626 ctx: &ChatStorageCtx,
627 chat_id: Uuid,
628 user_id: Uuid,
629 ) -> ChatStorageResult<()> {
630 let caller = require_user(ctx)?;
631 if caller != user_id {
632 require_admin(&self.state, ctx, chat_id)?;
633 }
634 let removed = self
635 .state
636 .members
637 .write()
638 .unwrap()
639 .remove(&(chat_id, user_id))
640 .is_some();
359641 if !removed {
360642 return Err(ChatStorageError::MemberNotFound(chat_id, user_id));
361643 }
362644 Ok(())
363645 }
364646
365 async fn list_members(&self, _ctx: &ChatStorageCtx, chat_id: Uuid, page: Page) -> ChatStorageResult<Vec<ConversationMember>> {
366 self.conversation_repo_list_members(chat_id, page)
647 async fn list_members(
648 &self,
649 ctx: &ChatStorageCtx,
650 chat_id: Uuid,
651 page: Page,
652 ) -> ChatStorageResult<Vec<ConversationMember>> {
653 require_member(&self.state, ctx, chat_id)?;
654 let map = self.state.members.read().unwrap();
655 let mut out: Vec<ConversationMember> = map
656 .values()
657 .filter(|m| m.chat_id == chat_id)
658 .cloned()
659 .collect();
660 out.sort_by_key(|m| m.joined_at);
661 let limit = page.limit as usize;
662 Ok(out.into_iter().take(limit).collect())
367663 }
368664
369 async fn mark_read(&self, ctx: &ChatStorageCtx, chat_id: Uuid, user_id: Uuid, at: OffsetDateTime) -> ChatStorageResult<()> {
370 require_user(ctx)?;
665 async fn mark_read(
666 &self,
667 ctx: &ChatStorageCtx,
668 chat_id: Uuid,
669 at: OffsetDateTime,
670 ) -> ChatStorageResult<()> {
671 let user_id = require_member(&self.state, ctx, chat_id)?;
371672 let mut map = self.state.members.write().unwrap();
372 let m = map.get_mut(&(chat_id, user_id)).ok_or(ChatStorageError::MemberNotFound(chat_id, user_id))?;
673 let m = map
674 .get_mut(&(chat_id, user_id))
675 .ok_or(ChatStorageError::MemberNotFound(chat_id, user_id))?;
373676 m.last_read_at = Some(at);
374677 Ok(())
375678 }
376679
377 async fn set_muted(&self, ctx: &ChatStorageCtx, chat_id: Uuid, user_id: Uuid, muted: bool) -> ChatStorageResult<()> {
378 require_user(ctx)?;
680 async fn set_muted(
681 &self,
682 ctx: &ChatStorageCtx,
683 chat_id: Uuid,
684 muted: bool,
685 ) -> ChatStorageResult<()> {
686 let user_id = require_member(&self.state, ctx, chat_id)?;
379687 let mut map = self.state.members.write().unwrap();
380 let m = map.get_mut(&(chat_id, user_id)).ok_or(ChatStorageError::MemberNotFound(chat_id, user_id))?;
688 let m = map
689 .get_mut(&(chat_id, user_id))
690 .ok_or(ChatStorageError::MemberNotFound(chat_id, user_id))?;
381691 m.muted = muted;
382692 Ok(())
383693 }
384694
385 async fn is_member(&self, _ctx: &ChatStorageCtx, chat_id: Uuid, user_id: Uuid) -> ChatStorageResult<bool> {
386 Ok(self.state.members.read().unwrap().contains_key(&(chat_id, user_id)))
387 }
388}
389
390impl InMemoryMemberRepository {
391 fn conversation_repo_list_members(&self, chat_id: Uuid, page: Page) -> ChatStorageResult<Vec<ConversationMember>> {
392 let map = self.state.members.read().unwrap();
393 let mut out: Vec<ConversationMember> = map
394 .values()
395 .filter(|m| m.chat_id == chat_id)
396 .cloned()
397 .collect();
398 out.sort_by_key(|m| m.joined_at);
399 let limit = page.limit as usize;
400 Ok(out.into_iter().take(limit).collect())
695 async fn is_member(
696 &self,
697 _ctx: &ChatStorageCtx,
698 chat_id: Uuid,
699 user_id: Uuid,
700 ) -> ChatStorageResult<bool> {
701 Ok(self
702 .state
703 .members
704 .read()
705 .unwrap()
706 .contains_key(&(chat_id, user_id)))
401707 }
402708}
403
404709
405710#[derive(Debug)]
406711pub struct InMemoryReactionRepository {
@@ -415,8 +720,21 @@
415720
416721#[async_trait]
417722impl ReactionRepository for InMemoryReactionRepository {
418 async fn add_reaction(&self, ctx: &ChatStorageCtx, input: NewReaction) -> ChatStorageResult<Reaction> {
419 require_user(ctx)?;
723 async fn add_reaction(
724 &self,
725 ctx: &ChatStorageCtx,
726 input: NewReaction,
727 ) -> ChatStorageResult<Reaction> {
728 let caller = require_user(ctx)?;
729 if caller != input.user_id {
730 return Err(ChatStorageError::PermissionDenied(format!(
731 "user {caller} cannot react as user {}",
732 input.user_id
733 )));
734 }
735 let chat_id = chat_id_for_message(&self.state, input.message_id)
736 .ok_or(ChatStorageError::MessageNotFound(input.message_id))?;
737 require_member(&self.state, ctx, chat_id)?;
420738 let r = Reaction {
421739 id: Uuid::new_v4(),
422740 message_id: input.message_id,
@@ -432,8 +750,20 @@
432750 Ok(r)
433751 }
434752
435 async fn remove_reaction(&self, ctx: &ChatStorageCtx, message_id: Uuid, user_id: Uuid, emoji: &str) -> ChatStorageResult<()> {
436 require_user(ctx)?;
753 async fn remove_reaction(
754 &self,
755 ctx: &ChatStorageCtx,
756 message_id: Uuid,
757 emoji: &str,
758 ) -> ChatStorageResult<()> {
759 let user_id = require_user(ctx)?;
760 let chat_id = chat_id_for_message(&self.state, message_id)
761 .ok_or(ChatStorageError::MessageNotFound(message_id))?;
762 if !is_member(&self.state, chat_id, user_id) {
763 return Err(ChatStorageError::PermissionDenied(format!(
764 "user {user_id} is not a member of chat {chat_id}"
765 )));
766 }
437767 let removed = self
438768 .state
439769 .reactions
@@ -447,7 +777,14 @@
447777 Ok(())
448778 }
449779
450 async fn list_reactions(&self, _ctx: &ChatStorageCtx, message_id: Uuid) -> ChatStorageResult<Vec<Reaction>> {
780 async fn list_reactions(
781 &self,
782 ctx: &ChatStorageCtx,
783 message_id: Uuid,
784 ) -> ChatStorageResult<Vec<Reaction>> {
785 let chat_id = chat_id_for_message(&self.state, message_id)
786 .ok_or(ChatStorageError::MessageNotFound(message_id))?;
787 require_member(&self.state, ctx, chat_id)?;
451788 let map = self.state.reactions.read().unwrap();
452789 Ok(map
453790 .values()
@@ -456,7 +793,6 @@
456793 .collect())
457794 }
458795}
459
460796
461797#[derive(Debug)]
462798pub struct InMemoryMentionRepository {
@@ -471,8 +807,14 @@
471807
472808#[async_trait]
473809impl MentionRepository for InMemoryMentionRepository {
474 async fn add_mention(&self, ctx: &ChatStorageCtx, input: NewMention) -> ChatStorageResult<Mention> {
475 require_user(ctx)?;
810 async fn add_mention(
811 &self,
812 ctx: &ChatStorageCtx,
813 input: NewMention,
814 ) -> ChatStorageResult<Mention> {
815 let chat_id = chat_id_for_message(&self.state, input.message_id)
816 .ok_or(ChatStorageError::MessageNotFound(input.message_id))?;
817 require_member(&self.state, ctx, chat_id)?;
476818 let m = Mention {
477819 id: Uuid::new_v4(),
478820 message_id: input.message_id,
@@ -487,19 +829,44 @@
487829 Ok(m)
488830 }
489831
490 async fn list_for_message(&self, _ctx: &ChatStorageCtx, message_id: Uuid) -> ChatStorageResult<Vec<Mention>> {
832 async fn list_for_message(
833 &self,
834 ctx: &ChatStorageCtx,
835 message_id: Uuid,
836 ) -> ChatStorageResult<Vec<Mention>> {
837 let chat_id = chat_id_for_message(&self.state, message_id)
838 .ok_or(ChatStorageError::MessageNotFound(message_id))?;
839 require_member(&self.state, ctx, chat_id)?;
491840 let map = self.state.mentions.read().unwrap();
492 Ok(map.values().filter(|m| m.message_id == message_id).cloned().collect())
841 Ok(map
842 .values()
843 .filter(|m| m.message_id == message_id)
844 .cloned()
845 .collect())
493846 }
494847
495 async fn list_for_user(&self, _ctx: &ChatStorageCtx, user_id: Uuid, limit: u32) -> ChatStorageResult<Vec<Mention>> {
848 async fn list_for_user(
849 &self,
850 ctx: &ChatStorageCtx,
851 user_id: Uuid,
852 limit: u32,
853 ) -> ChatStorageResult<Vec<Mention>> {
854 let caller = require_user(ctx)?;
855 if caller != user_id {
856 return Err(ChatStorageError::PermissionDenied(format!(
857 "user {caller} cannot list mentions for user {user_id}"
858 )));
859 }
496860 let map = self.state.mentions.read().unwrap();
497 let mut items: Vec<Mention> = map.values().filter(|m| m.user_id == Some(user_id)).cloned().collect();
861 let mut items: Vec<Mention> = map
862 .values()
863 .filter(|m| m.user_id == Some(user_id) || m.is_everyone || m.is_here)
864 .cloned()
865 .collect();
498866 items.sort_by_key(|m| std::cmp::Reverse(m.created_at));
499867 Ok(items.into_iter().take(limit as usize).collect())
500868 }
501869}
502
503870
504871#[derive(Debug)]
505872pub struct InMemoryReadReceiptRepository {
@@ -514,14 +881,26 @@
514881
515882#[async_trait]
516883impl ReadReceiptRepository for InMemoryReadReceiptRepository {
517 async fn upsert(&self, ctx: &ChatStorageCtx, input: NewReadReceipt) -> ChatStorageResult<ReadReceipt> {
518 require_user(ctx)?;
884 async fn upsert(
885 &self,
886 ctx: &ChatStorageCtx,
887 input: NewReadReceipt,
888 ) -> ChatStorageResult<ReadReceipt> {
889 let caller = require_user(ctx)?;
890 if caller != input.user_id {
891 return Err(ChatStorageError::PermissionDenied(format!(
892 "user {caller} cannot mark read for user {}",
893 input.user_id
894 )));
895 }
896 require_member(&self.state, ctx, input.chat_id)?;
519897 let mut map = self.state.read_receipts.write().unwrap();
520898 let now = OffsetDateTime::now_utc();
521 if let Some(existing) = map
522 .values_mut()
523 .find(|r| r.chat_id == input.chat_id && r.user_id == input.user_id && r.thread_id == input.thread_id)
524 {
899 if let Some(existing) = map.values_mut().find(|r| {
900 r.chat_id == input.chat_id
901 && r.user_id == input.user_id
902 && r.thread_id == input.thread_id
903 }) {
525904 existing.last_read_message_id = input.last_read_message_id;
526905 existing.updated_at = now;
527906 return Ok(existing.clone());
@@ -538,20 +917,37 @@
538917 Ok(r)
539918 }
540919
541 async fn list_for_chat(&self, _ctx: &ChatStorageCtx, chat_id: Uuid) -> ChatStorageResult<Vec<ReadReceipt>> {
920 async fn list_for_chat(
921 &self,
922 ctx: &ChatStorageCtx,
923 chat_id: Uuid,
924 ) -> ChatStorageResult<Vec<ReadReceipt>> {
925 require_member(&self.state, ctx, chat_id)?;
542926 let map = self.state.read_receipts.read().unwrap();
543 Ok(map.values().filter(|r| r.chat_id == chat_id).cloned().collect())
927 Ok(map
928 .values()
929 .filter(|r| r.chat_id == chat_id)
930 .cloned()
931 .collect())
544932 }
545933
546 async fn find(&self, _ctx: &ChatStorageCtx, chat_id: Uuid, user_id: Uuid, thread_id: Option<Uuid>) -> ChatStorageResult<Option<ReadReceipt>> {
934 async fn find(
935 &self,
936 ctx: &ChatStorageCtx,
937 chat_id: Uuid,
938 _user_id: Uuid,
939 thread_id: Option<Uuid>,
940 ) -> ChatStorageResult<Option<ReadReceipt>> {
941 require_member(&self.state, ctx, chat_id)?;
547942 let map = self.state.read_receipts.read().unwrap();
548 Ok(map
943 let caller = require_user(ctx)?;
944 let r = map
549945 .values()
550 .find(|r| r.chat_id == chat_id && r.user_id == user_id && r.thread_id == thread_id)
551 .cloned())
946 .find(|r| r.chat_id == chat_id && r.user_id == caller && r.thread_id == thread_id)
947 .cloned();
948 Ok(r)
552949 }
553950}
554
555951
556952#[derive(Debug)]
557953pub struct InMemoryPinnedMessageRepository {
@@ -566,8 +962,19 @@
566962
567963#[async_trait]
568964impl PinnedMessageRepository for InMemoryPinnedMessageRepository {
569 async fn pin(&self, ctx: &ChatStorageCtx, input: NewPinnedMessage) -> ChatStorageResult<PinnedMessage> {
570 require_user(ctx)?;
965 async fn pin(
966 &self,
967 ctx: &ChatStorageCtx,
968 input: NewPinnedMessage,
969 ) -> ChatStorageResult<PinnedMessage> {
970 let caller = require_user(ctx)?;
971 if caller != input.pinned_by {
972 return Err(ChatStorageError::PermissionDenied(format!(
973 "user {caller} cannot pin as user {}",
974 input.pinned_by
975 )));
976 }
977 require_admin(&self.state, ctx, input.chat_id)?;
571978 let p = PinnedMessage {
572979 id: Uuid::new_v4(),
573980 chat_id: input.chat_id,
@@ -575,25 +982,44 @@
575982 pinned_by: input.pinned_by,
576983 created_at: OffsetDateTime::now_utc(),
577984 };
578 self.state.pinned.write().unwrap().insert((p.chat_id, p.message_id), p.clone());
985 self.state
986 .pinned
987 .write()
988 .unwrap()
989 .insert((p.chat_id, p.message_id), p.clone());
579990 Ok(p)
580991 }
581992
582 async fn unpin(&self, ctx: &ChatStorageCtx, chat_id: Uuid, message_id: Uuid) -> ChatStorageResult<()> {
583 require_user(ctx)?;
584 let removed = self.state.pinned.write().unwrap().remove(&(chat_id, message_id)).is_some();
993 async fn unpin(
994 &self,
995 ctx: &ChatStorageCtx,
996 chat_id: Uuid,
997 message_id: Uuid,
998 ) -> ChatStorageResult<()> {
999 require_admin(&self.state, ctx, chat_id)?;
1000 let removed = self
1001 .state
1002 .pinned
1003 .write()
1004 .unwrap()
1005 .remove(&(chat_id, message_id))
1006 .is_some();
5851007 if !removed {
5861008 return Err(ChatStorageError::PinnedMessageNotFound(message_id));
5871009 }
5881010 Ok(())
5891011 }
5901012
591 async fn list_for_chat(&self, _ctx: &ChatStorageCtx, chat_id: Uuid) -> ChatStorageResult<Vec<PinnedMessage>> {
1013 async fn list_for_chat(
1014 &self,
1015 ctx: &ChatStorageCtx,
1016 chat_id: Uuid,
1017 ) -> ChatStorageResult<Vec<PinnedMessage>> {
1018 require_member(&self.state, ctx, chat_id)?;
5921019 let map = self.state.pinned.read().unwrap();
5931020 Ok(map.values().filter(|p| p.chat_id == chat_id).cloned().collect())
5941021 }
5951022}
596
5971023
5981024#[derive(Debug)]
5991025pub struct InMemoryThreadRepository {
@@ -608,8 +1034,15 @@
6081034
6091035#[async_trait]
6101036impl ThreadRepository for InMemoryThreadRepository {
611 async fn increment_reply(&self, ctx: &ChatStorageCtx, chat_id: Uuid, root_message_id: Uuid, reply_by: Uuid, at: OffsetDateTime) -> ChatStorageResult<Thread> {
612 require_user(ctx)?;
1037 async fn increment_reply(
1038 &self,
1039 ctx: &ChatStorageCtx,
1040 chat_id: Uuid,
1041 root_message_id: Uuid,
1042 reply_by: Uuid,
1043 at: OffsetDateTime,
1044 ) -> ChatStorageResult<Thread> {
1045 require_member(&self.state, ctx, chat_id)?;
6131046 let mut map = self.state.threads.write().unwrap();
6141047 let entry = map.entry((chat_id, root_message_id)).or_insert(Thread {
6151048 chat_id,
@@ -624,17 +1057,31 @@
6241057 Ok(entry.clone())
6251058 }
6261059
627 async fn find(&self, _ctx: &ChatStorageCtx, chat_id: Uuid, root_message_id: Uuid) -> ChatStorageResult<Option<Thread>> {
628 let map = self.state.threads.read().unwrap();
629 Ok(map.get(&(chat_id, root_message_id)).cloned())
1060 async fn find(
1061 &self,
1062 _ctx: &ChatStorageCtx,
1063 chat_id: Uuid,
1064 root_message_id: Uuid,
1065 ) -> ChatStorageResult<Option<Thread>> {
1066 Ok(self
1067 .state
1068 .threads
1069 .read()
1070 .unwrap()
1071 .get(&(chat_id, root_message_id))
1072 .cloned())
6301073 }
6311074
632 async fn list_for_chat(&self, _ctx: &ChatStorageCtx, chat_id: Uuid) -> ChatStorageResult<Vec<Thread>> {
1075 async fn list_for_chat(
1076 &self,
1077 ctx: &ChatStorageCtx,
1078 chat_id: Uuid,
1079 ) -> ChatStorageResult<Vec<Thread>> {
1080 require_member(&self.state, ctx, chat_id)?;
6331081 let map = self.state.threads.read().unwrap();
6341082 Ok(map.values().filter(|t| t.chat_id == chat_id).cloned().collect())
6351083 }
6361084}
637
6381085
6391086pub struct InMemoryChatStorage {
6401087 state: SharedState,
@@ -667,6 +1114,12 @@
6671114#[derive(Debug)]
6681115pub struct NullChatMedia;
6691116
1117fn unsupported<T>() -> ChatStorageResult<T> {
1118 Err(ChatStorageError::Blob(storage::StorageError::Unsupported(
1119 "null backend".to_string(),
1120 )))
1121}
1122
6701123#[async_trait]
6711124impl ChatMediaStore for NullChatMedia {
6721125 async fn put_attachment_bytes(
@@ -676,19 +1129,13 @@
6761129 _filename: &str,
6771130 _bytes: Bytes,
6781131 ) -> ChatStorageResult<AttachmentPath> {
679 Err(ChatStorageError::Blob(storage::StorageError::Unsupported(
680 "null backend".to_string(),
681 )))
1132 unsupported()
6821133 }
6831134 async fn get_attachment_bytes(&self, _path: &AttachmentPath) -> ChatStorageResult<Bytes> {
684 Err(ChatStorageError::Blob(storage::StorageError::Unsupported(
685 "null backend".to_string(),
686 )))
1135 unsupported()
6871136 }
6881137 async fn delete_attachment(&self, _path: &AttachmentPath) -> ChatStorageResult<()> {
689 Err(ChatStorageError::Blob(storage::StorageError::Unsupported(
690 "null backend".to_string(),
691 )))
1138 unsupported()
6921139 }
6931140 async fn presign_upload(
6941141 &self,
@@ -697,14 +1144,14 @@
6971144 _filename: &str,
6981145 _ttl: Duration,
6991146 ) -> ChatStorageResult<Url> {
700 Err(ChatStorageError::Blob(storage::StorageError::Unsupported(
701 "null backend".to_string(),
702 )))
1147 unsupported()
7031148 }
704 async fn presign_download(&self, _path: &AttachmentPath, _ttl: Duration) -> ChatStorageResult<Url> {
705 Err(ChatStorageError::Blob(storage::StorageError::Unsupported(
706 "null backend".to_string(),
707 )))
1149 async fn presign_download(
1150 &self,
1151 _path: &AttachmentPath,
1152 _ttl: Duration,
1153 ) -> ChatStorageResult<Url> {
1154 unsupported()
7081155 }
7091156}
7101157
Modified chat/storagelayer/models/conversation.rs chat/storagelayer/models/conversation.rs +1 −0 View chat/storagelayer/models/conversation.rs
@@ -17,6 +17,7 @@
1717 pub kind: ConversationKind,
1818 pub counterpart_user_id: Option<Uuid>,
1919 pub title: Option<String>,
20 pub creator_user_id: Uuid,
2021 pub created_at: OffsetDateTime,
2122 pub updated_at: Option<OffsetDateTime>,
2223}
Modified chat/storagelayer/repository/member.rs chat/storagelayer/repository/member.rs +0 −2 View chat/storagelayer/repository/member.rs
@@ -36,7 +36,6 @@
3636 &self,
3737 ctx: &ChatStorageCtx,
3838 chat_id: Uuid,
39 user_id: Uuid,
4039 at: OffsetDateTime,
4140 ) -> ChatStorageResult<()>;
4241
@@ -44,7 +43,6 @@
4443 &self,
4544 ctx: &ChatStorageCtx,
4645 chat_id: Uuid,
47 user_id: Uuid,
4846 muted: bool,
4947 ) -> ChatStorageResult<()>;
5048
Modified chat/storagelayer/repository/message.rs chat/storagelayer/repository/message.rs +1 −7 View chat/storagelayer/repository/message.rs
@@ -40,16 +40,10 @@
4040 &self,
4141 ctx: &ChatStorageCtx,
4242 id: Uuid,
43 editor: Uuid,
4443 new_body: String,
4544 ) -> ChatStorageResult<Message>;
4645
47 async fn soft_delete(
48 &self,
49 ctx: &ChatStorageCtx,
50 id: Uuid,
51 by: Uuid,
52 ) -> ChatStorageResult<()>;
46 async fn soft_delete(&self, ctx: &ChatStorageCtx, id: Uuid) -> ChatStorageResult<()>;
5347
5448 async fn restore(&self, ctx: &ChatStorageCtx, id: Uuid) -> ChatStorageResult<()>;
5549
Modified chat/storagelayer/repository/reaction.rs chat/storagelayer/repository/reaction.rs +0 −1 View chat/storagelayer/repository/reaction.rs
@@ -19,7 +19,6 @@
1919 &self,
2020 ctx: &ChatStorageCtx,
2121 message_id: Uuid,
22 user_id: Uuid,
2322 emoji: &str,
2423 ) -> ChatStorageResult<()>;
2524
Modified chat/storagelayer/lib.rs chat/storagelayer/lib.rs +1 −0 View chat/storagelayer/lib.rs
@@ -2,6 +2,7 @@
22#![warn(missing_debug_implementations)]
33#![warn(unreachable_pub)]
44
5pub mod auth;
56pub mod blob;
67pub mod error;
78pub mod mapper;
Modified chat/storagelayer/mapping.rs chat/storagelayer/mapping.rs +2 −0 View chat/storagelayer/mapping.rs
@@ -77,6 +77,7 @@
7777 kind: m.kind.into(),
7878 counterpart_user_id: m.counterpart_user_id,
7979 title: m.title,
80 creator_user_id: m.creator_user_id,
8081 created_at: m.created_at,
8182 updated_at: m.updated_at,
8283 }
@@ -92,6 +93,7 @@
9293 kind: Set(c.kind.into()),
9394 counterpart_user_id: Set(c.counterpart_user_id),
9495 title: Set(c.title),
96 creator_user_id: Set(c.creator_user_id),
9597 created_at: Set(c.created_at),
9698 updated_at: Set(c.updated_at),
9799 })
Modified lib/db/chat/chats.rs lib/db/chat/chats.rs +1 −0 View lib/db/chat/chats.rs
@@ -23,6 +23,7 @@
2323 pub kind: ChatKind,
2424 pub counterpart_user_id: Option<Uuid>,
2525 pub title: Option<String>,
26 pub creator_user_id: Uuid,
2627 pub created_at: OffsetDateTime,
2728 pub updated_at: Option<OffsetDateTime>,
2829 #[sea_orm(belongs_to, from = "channel_id", to = "id")]