Skip to content
Clone
main
154 lines 5.0 KB Rust
use models::chat::user_drafts;
use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, Set};
use serde::{Deserialize, Serialize};
use time::OffsetDateTime;
use utoipa::ToSchema;
use uuid::Uuid;
use crate::errors::ChatError;
use crate::services::service::ChatService;
use crate::share::lookup;
#[derive(Debug, Clone, Serialize, ToSchema)]
pub struct DraftDto {
pub room_id: Uuid,
pub thread_id: Option<Uuid>,
pub content: String,
pub updated_at: OffsetDateTime,
}
#[derive(Debug, Clone, Deserialize, ToSchema)]
pub struct UpsertDraftCmd {
#[serde(default)]
pub thread_id: Option<Uuid>,
pub content: String,
}
impl ChatService {
pub async fn upsert_draft(
&self,
caller: i64,
room_id: Uuid,
cmd: UpsertDraftCmd,
) -> Result<DraftDto, ChatError> {
let _ctx = lookup::require_room_readable(self, room_id, caller).await?;
if cmd.content.chars().count() > self.settings.chat.max_message_len {
return Err(ChatError::BadRequest("draft too long".to_string()));
}
let now = OffsetDateTime::now_utc();
let mut existing: Option<user_drafts::Model> = None;
let candidates = user_drafts::Entity::find()
.filter(user_drafts::Column::UserId.eq(caller))
.filter(user_drafts::Column::RoomId.eq(room_id))
.all(self.database.write())
.await?;
for row in candidates {
if row.thread_id == cmd.thread_id {
existing = Some(row);
break;
}
}
match existing {
Some(row) => {
if cmd.content.is_empty() {
user_drafts::Entity::delete_by_id(row.id)
.exec(self.database.write())
.await?;
return Ok(DraftDto {
room_id,
thread_id: cmd.thread_id,
content: String::new(),
updated_at: now,
});
}
let mut am: user_drafts::ActiveModel = row.into();
am.content = Set(cmd.content.clone());
am.updated_at = Set(now);
am.update(self.database.write()).await?;
}
None => {
if !cmd.content.is_empty() {
user_drafts::ActiveModel {
id: Set(Uuid::now_v7()),
user_id: Set(caller),
room_id: Set(room_id),
thread_id: Set(cmd.thread_id),
content: Set(cmd.content.clone()),
created_at: Set(now),
updated_at: Set(now),
}
.insert(self.database.write())
.await?;
}
}
}
Ok(DraftDto {
room_id,
thread_id: cmd.thread_id,
content: cmd.content,
updated_at: now,
})
}
pub async fn get_draft(
&self,
caller: i64,
room_id: Uuid,
thread_id: Option<Uuid>,
) -> Result<Option<DraftDto>, ChatError> {
let _ctx = lookup::require_room_readable(self, room_id, caller).await?;
let rows = user_drafts::Entity::find()
.filter(user_drafts::Column::UserId.eq(caller))
.filter(user_drafts::Column::RoomId.eq(room_id))
.all(self.database.read())
.await?;
Ok(rows
.into_iter()
.find(|r| r.thread_id == thread_id)
.map(|r| DraftDto {
room_id,
thread_id: r.thread_id,
content: r.content,
updated_at: r.updated_at,
}))
}
pub async fn delete_draft(
&self,
caller: i64,
room_id: Uuid,
thread_id: Option<Uuid>,
) -> Result<(), ChatError> {
let _ctx = lookup::require_room_readable(self, room_id, caller).await?;
let mut q = user_drafts::Entity::delete_many()
.filter(user_drafts::Column::UserId.eq(caller))
.filter(user_drafts::Column::RoomId.eq(room_id));
if let Some(tid) = thread_id {
q = q.filter(user_drafts::Column::ThreadId.eq(tid));
}
let deleted = q.exec(self.database.write()).await?;
if deleted.rows_affected == 0 {
return Err(ChatError::NotFound);
}
Ok(())
}
pub async fn list_my_drafts(&self, caller: i64) -> Result<Vec<DraftDto>, ChatError> {
let rows = user_drafts::Entity::find()
.filter(user_drafts::Column::UserId.eq(caller))
.all(self.database.read())
.await?;
Ok(rows
.into_iter()
.map(|r| DraftDto {
room_id: r.room_id,
thread_id: r.thread_id,
content: r.content,
updated_at: r.updated_at,
})
.collect())
}
}