use db::channel::channels as channel_entity; use db::conn::sea_orm::*; use db::space::space_members as member_entity; use db::space::spaces as space_entity; use time::OffsetDateTime; use uuid::Uuid; use crate::error::{SpaceError, SpaceResult}; use crate::features::SpaceService; use super::types::{ChannelPage, ChannelView}; async fn member_space( svc: &SpaceService, caller: Uuid, space_id: Uuid, ) -> SpaceResult { let db = svc.db.reader(); let space = space_entity::Entity::find_by_id(space_id) .one(&db) .await? .ok_or_else(|| SpaceError::NotFound(format!("space {space_id}")))?; let member = member_entity::Entity::find_by_id((space_id, caller)) .one(&db) .await?; let active = member .map(|m| m.status == member_entity::MemberStatus::Active) .unwrap_or(false); if !active { return Err(SpaceError::Forbidden( "caller is not an active member of this space".to_string(), )); } Ok(space) } pub async fn get_channel( svc: &SpaceService, caller: Uuid, space_id: Uuid, channel_id: Uuid, ) -> SpaceResult { member_space(svc, caller, space_id).await?; let db = svc.db.reader(); let channel = channel_entity::Entity::find_by_id(channel_id) .one(&db) .await? .ok_or_else(|| SpaceError::NotFound(format!("channel {channel_id}")))?; if channel.space_id != Some(space_id) { return Err(SpaceError::NotFound(format!("channel {channel_id}"))); } Ok(ChannelView::from(channel)) } pub async fn list_channels( svc: &SpaceService, caller: Uuid, space_id: Uuid, limit: u64, offset: u64, ) -> SpaceResult { member_space(svc, caller, space_id).await?; let db = svc.db.reader(); let limit = std::cmp::max(std::cmp::min(limit, svc.config.max_pagination), 1); let items = channel_entity::Entity::find() .filter(channel_entity::Column::SpaceId.eq(space_id)) .order_by_asc(channel_entity::Column::Position) .limit(Some(limit)) .offset(Some(offset)) .all(&db) .await? .into_iter() .map(ChannelView::from) .collect(); let total = channel_entity::Entity::find() .filter(channel_entity::Column::SpaceId.eq(space_id)) .count(&db) .await?; Ok(ChannelPage { items, total: total as i64 }) } pub async fn ensure_channel_for_resource( svc: &SpaceService, space_id: Uuid, name: &str, created_by: Uuid, ) -> SpaceResult { let db = svc.db.writer(); let now = OffsetDateTime::now_utc(); let active: channel_entity::ActiveModel = channel_entity::ActiveModel::builder() .set_id(Uuid::new_v4()) .set_space_id(Some(space_id)) .set_name(name.to_string()) .set_parent_id(None) .set_color(None) .set_position(0) .set_icon(None) .set_topic(None) .set_purpose(None) .set_is_archived(false) .set_archived_at(None) .set_archived_by(None) .set_created_at(now) .set_updated_at(now) .set_created_by(created_by) .into(); let saved = active.insert(&db).await?; Ok(ChannelView::from(saved)) }