feat: config history — auto-snapshot on update, list/pin/restore endpoints

This commit is contained in:
2026-03-17 14:39:09 +01:00
parent ad3a73f061
commit 20e80ac28e
3 changed files with 151 additions and 1 deletions

View File

@@ -1,6 +1,8 @@
use std::sync::Arc;
use crate::entities::Channel;
use uuid::Uuid;
use crate::entities::{Channel, ChannelConfigSnapshot, ScheduleConfig};
use crate::errors::{DomainError, DomainResult};
use crate::repositories::ChannelRepository;
use crate::value_objects::{ChannelId, UserId};
@@ -42,10 +44,73 @@ impl ChannelService {
}
pub async fn update(&self, channel: Channel) -> DomainResult<Channel> {
// Auto-snapshot the config being replaced
self.channel_repo
.save_config_snapshot(channel.id, &channel.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?;