125 lines
4.0 KiB
Rust
125 lines
4.0 KiB
Rust
use std::sync::Arc;
|
|
|
|
use uuid::Uuid;
|
|
|
|
use crate::entities::{Channel, ChannelConfigSnapshot, ScheduleConfig};
|
|
use crate::errors::{DomainError, DomainResult};
|
|
use crate::repositories::ChannelRepository;
|
|
use crate::value_objects::{ChannelId, UserId};
|
|
|
|
/// Service for managing channels (CRUD + ownership enforcement).
|
|
pub struct ChannelService {
|
|
channel_repo: Arc<dyn ChannelRepository>,
|
|
}
|
|
|
|
impl ChannelService {
|
|
pub fn new(channel_repo: Arc<dyn ChannelRepository>) -> Self {
|
|
Self { channel_repo }
|
|
}
|
|
|
|
pub async fn create(
|
|
&self,
|
|
owner_id: UserId,
|
|
name: &str,
|
|
timezone: &str,
|
|
) -> DomainResult<Channel> {
|
|
let channel = Channel::new(owner_id, name, timezone);
|
|
self.channel_repo.save(&channel).await?;
|
|
Ok(channel)
|
|
}
|
|
|
|
pub async fn find_by_id(&self, id: ChannelId) -> DomainResult<Channel> {
|
|
self.channel_repo
|
|
.find_by_id(id)
|
|
.await?
|
|
.ok_or(DomainError::ChannelNotFound(id))
|
|
}
|
|
|
|
pub async fn find_all(&self) -> DomainResult<Vec<Channel>> {
|
|
self.channel_repo.find_all().await
|
|
}
|
|
|
|
pub async fn find_by_owner(&self, owner_id: UserId) -> DomainResult<Vec<Channel>> {
|
|
self.channel_repo.find_by_owner(owner_id).await
|
|
}
|
|
|
|
pub async fn update(&self, channel: Channel) -> DomainResult<Channel> {
|
|
// Auto-snapshot the existing config before overwriting
|
|
if let Some(existing) = self.channel_repo.find_by_id(channel.id).await? {
|
|
self.channel_repo
|
|
.save_config_snapshot(channel.id, &existing.schedule_config, None)
|
|
.await?;
|
|
}
|
|
self.channel_repo.save(&channel).await?;
|
|
Ok(channel)
|
|
}
|
|
|
|
pub async fn list_config_snapshots(
|
|
&self,
|
|
channel_id: ChannelId,
|
|
) -> DomainResult<Vec<ChannelConfigSnapshot>> {
|
|
self.channel_repo.list_config_snapshots(channel_id).await
|
|
}
|
|
|
|
pub async fn get_config_snapshot(
|
|
&self,
|
|
channel_id: ChannelId,
|
|
snapshot_id: Uuid,
|
|
) -> DomainResult<Option<ChannelConfigSnapshot>> {
|
|
self.channel_repo.get_config_snapshot(channel_id, snapshot_id).await
|
|
}
|
|
|
|
pub async fn patch_config_snapshot_label(
|
|
&self,
|
|
channel_id: ChannelId,
|
|
snapshot_id: Uuid,
|
|
label: Option<String>,
|
|
) -> DomainResult<Option<ChannelConfigSnapshot>> {
|
|
self.channel_repo.patch_config_snapshot_label(channel_id, snapshot_id, label).await
|
|
}
|
|
|
|
/// Restore a snapshot: auto-snapshot current config, then apply the snapshot's config.
|
|
pub async fn restore_config_snapshot(
|
|
&self,
|
|
channel_id: ChannelId,
|
|
snapshot_id: Uuid,
|
|
) -> DomainResult<Channel> {
|
|
let snapshot = self
|
|
.channel_repo
|
|
.get_config_snapshot(channel_id, snapshot_id)
|
|
.await?
|
|
.ok_or(DomainError::ChannelNotFound(channel_id))?;
|
|
let mut channel = self
|
|
.channel_repo
|
|
.find_by_id(channel_id)
|
|
.await?
|
|
.ok_or(DomainError::ChannelNotFound(channel_id))?;
|
|
// Snapshot current config before overwriting
|
|
self.channel_repo
|
|
.save_config_snapshot(channel_id, &channel.schedule_config, None)
|
|
.await?;
|
|
channel.schedule_config = snapshot.config;
|
|
channel.updated_at = chrono::Utc::now();
|
|
self.channel_repo.save(&channel).await?;
|
|
Ok(channel)
|
|
}
|
|
|
|
pub async fn save_config_snapshot(
|
|
&self,
|
|
channel_id: ChannelId,
|
|
config: &ScheduleConfig,
|
|
label: Option<String>,
|
|
) -> DomainResult<ChannelConfigSnapshot> {
|
|
self.channel_repo.save_config_snapshot(channel_id, config, label).await
|
|
}
|
|
|
|
/// Delete a channel, enforcing that `requester_id` is the owner.
|
|
pub async fn delete(&self, id: ChannelId, requester_id: UserId) -> DomainResult<()> {
|
|
let channel = self.find_by_id(id).await?;
|
|
if channel.owner_id != requester_id {
|
|
return Err(DomainError::forbidden("You don't own this channel"));
|
|
}
|
|
self.channel_repo.delete(id).await
|
|
}
|
|
}
|