Skip to content
Clone

refactor(chat): refresh storagelayer auth, mapper, mock and repository

zhenyi committed
parent cd576b3 commit 1e4b9b5 Browse files

6 files changed

+84 −17

File tree 6
Modified chat/storagelayer/mapper/attachment.rs View chat/storagelayer/mapper/attachment.rs
@@ -1,7 +1,10 @@
11use std::sync::Arc;
22
33use async_trait::async_trait;
4use sea_orm::{ColumnTrait, EntityTrait, QueryFilter, QueryOrder, QuerySelect, Set};
4use sea_orm::{
5 ActiveModelTrait, ColumnTrait, EntityTrait, IntoActiveModel, PaginatorTrait, QueryFilter,
6 QueryOrder, QuerySelect, Set,
7};
58use time::OffsetDateTime;
69use uuid::Uuid;
710
@@ -13,6 +16,7 @@
1316use crate::repository::attachment::AttachmentRepository;
1417
1518use db::attachment::attachments as att_entity;
19use db::chat::chat_messages as message_entity;
1620
1721pub struct SeaAttachmentRepository {
1822 db: Arc<db::conn::Db>,
@@ -60,6 +64,10 @@
6064 let Some(att) = att_entity::Entity::find_by_id(id).one(&db).await? else {
6165 return Ok(None);
6266 };
67 if att.owner_id.is_nil() {
68 auth::require_authenticated(ctx).await?;
69 return Ok(Some(att.into()));
70 }
6371 if let Some(chat_id) =
6472 auth::chat_id_for_attachment_owner(&self.db, att.owner_type.into(), att.owner_id)
6573 .await?
@@ -105,6 +113,35 @@
105113 }
106114 Ok(())
107115 }
116
117 async fn attach_to_message(
118 &self,
119 ctx: &ChatStorageCtx,
120 id: Uuid,
121 message_id: Uuid,
122 ) -> ChatStorageResult<Attachment> {
123 auth::require_uploader_or_admin(&self.db, ctx, id).await?;
124 let db = self.db.writer();
125 let Some(att) = att_entity::Entity::find_by_id(id).one(&db).await? else {
126 return Err(ChatStorageError::AttachmentNotFound(id));
127 };
128 if !att.owner_id.is_nil() {
129 return Err(ChatStorageError::Conflict(format!(
130 "attachment {id} is already attached to a message"
131 )));
132 }
133 let message_exists = message_entity::Entity::find_by_id(message_id)
134 .count(&db)
135 .await?
136 > 0;
137 if !message_exists {
138 return Err(ChatStorageError::MessageNotFound(message_id));
139 }
140 let mut active = att.into_active_model();
141 active.owner_id = Set(message_id);
142 let updated = active.update(&db).await?;
143 Ok(updated.into())
144 }
108145}
109146impl std::fmt::Debug for SeaAttachmentRepository {
110147 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Modified chat/storagelayer/mock/in_memory.rs View chat/storagelayer/mock/in_memory.rs
@@ -548,6 +548,39 @@
548548 }
549549 Ok(())
550550 }
551
552 async fn attach_to_message(
553 &self,
554 ctx: &ChatStorageCtx,
555 id: Uuid,
556 message_id: Uuid,
557 ) -> ChatStorageResult<Attachment> {
558 require_uploader_or_admin(&self.state, ctx, id)?;
559 let mut map = self.state.attachments.write().unwrap();
560 let Some(att) = map.get(&id).cloned() else {
561 return Err(ChatStorageError::AttachmentNotFound(id));
562 };
563 if !att.owner_id.is_nil() {
564 return Err(ChatStorageError::Conflict(format!(
565 "attachment {id} is already attached to a message"
566 )));
567 }
568 if !self
569 .state
570 .messages
571 .read()
572 .unwrap()
573 .contains_key(&message_id)
574 {
575 return Err(ChatStorageError::MessageNotFound(message_id));
576 }
577 let attached = Attachment {
578 owner_id: message_id,
579 ..att
580 };
581 map.insert(id, attached.clone());
582 Ok(attached)
583 }
551584}
552585
553586fn chat_id_for_attachment_owner_in_state(
Modified chat/storagelayer/models/conversation.rs View chat/storagelayer/models/conversation.rs
@@ -24,11 +24,8 @@
2424
2525#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2626pub struct NewConversation {
27 /// Space the chat lives in. Creating a chat atomically mints its own Object
28 /// channel (never a dangling object channel), mirroring repo/project/issue.
29 pub space_id: Uuid,
30 /// Optional Directory channel to host the chat's object channel under.
31 pub parent_dir: Option<Uuid>,
27 pub space_id: Uuid,
28 pub parent_dir: Option<Uuid>,
3229 pub kind: ConversationKind,
3330 pub counterpart_user_id: Option<Uuid>,
3431 pub title: Option<String>,
Modified chat/storagelayer/repository/attachment.rs View chat/storagelayer/repository/attachment.rs
@@ -31,6 +31,13 @@
3131 ) -> ChatStorageResult<Vec<Attachment>>;
3232
3333 async fn delete(&self, ctx: &ChatStorageCtx, id: Uuid) -> ChatStorageResult<()>;
34
35 async fn attach_to_message(
36 &self,
37 ctx: &ChatStorageCtx,
38 id: Uuid,
39 message_id: Uuid,
40 ) -> ChatStorageResult<Attachment>;
3441}
3542
3643pub type DynAttachmentRepository = Arc<dyn AttachmentRepository>;
Modified chat/storagelayer/repository/read_receipt.rs View chat/storagelayer/repository/read_receipt.rs
@@ -29,8 +29,7 @@
2929 thread_id: Option<Uuid>,
3030 ) -> ChatStorageResult<Option<ReadReceipt>>;
3131
32 /// Toggle the caller's per-chat mute flag (persisted on chat_reads.muted).
33 async fn set_muted(
32 async fn set_muted(
3433 &self,
3534 ctx: &ChatStorageCtx,
3635 chat_id: Uuid,
Modified chat/storagelayer/auth.rs View chat/storagelayer/auth.rs
@@ -11,7 +11,6 @@
1111use db::chat::chats as chat_entity;
1212use db::space::{space_member_roles, space_members, space_role_permissions, spaces};
1313
14/// Permission bit that elevates a space role to admin (mirrors gitauth::permission).
1514pub const SPACE_ADMIN_PERMISSION: &str = "manage_members";
1615
1716pub async fn require_authenticated(ctx: &ChatStorageCtx) -> ChatStorageResult<Uuid> {
@@ -28,7 +27,6 @@
2827 Ok(user_id)
2928}
3029
31/// Resolve the space that owns a chat via chat -> channel -> space.
3230async fn space_id_for_chat<C: ConnectionTrait>(
3331 db: &C,
3432 chat_id: Uuid,
@@ -71,10 +69,6 @@
7169 Ok(())
7270}
7371
74/// Grants chat access iff the actor is an active member of the chat's space.
75///
76/// Channel/chat-level membership no longer exists: access to a chat inherits from
77/// space membership (space_members.status == Active).
7872pub async fn require_member(
7973 db: &db::conn::Db,
8074 ctx: &ChatStorageCtx,
@@ -127,8 +121,6 @@
127121 Ok(())
128122}
129123
130/// Require the caller to be a space administrator (space owner, or a space role
131/// carrying the admin permission) for the chat's space.
132124pub async fn require_admin(
133125 db: &db::conn::Db,
134126 ctx: &ChatStorageCtx,
@@ -204,7 +196,6 @@
204196 }
205197}
206198
207/// Resolve the owning chat for an attachment and grant space access to it.
208199pub async fn require_chat_member_for_attachment_owner(
209200 db: &db::conn::Db,
210201 ctx: &ChatStorageCtx,
@@ -212,6 +203,9 @@
212203 owner_id: Uuid,
213204) -> ChatStorageResult<Uuid> {
214205 let user_id = require_authenticated(ctx).await?;
206 if owner_id.is_nil() {
207 return Ok(user_id);
208 }
215209 if let Some(chat_id) = chat_id_for_attachment_owner(db, owner, owner_id).await? {
216210 require_member(db, ctx, chat_id).await?;
217211 }