use std::sync::Arc; use async_trait::async_trait; use chrono::Utc; use libertas_core::{ authz, error::{CoreError, CoreResult}, models::Album, repositories::{AlbumRepository, AlbumShareRepository, MediaRepository}, schema::{AddMediaToAlbumData, CreateAlbumData, ShareAlbumData, UpdateAlbumData}, services::AlbumService, }; use uuid::Uuid; pub struct AlbumServiceImpl { album_repo: Arc, media_repo: Arc, album_share_repo: Arc, } impl AlbumServiceImpl { pub fn new( album_repo: Arc, media_repo: Arc, album_share_repo: Arc, ) -> Self { Self { album_repo, media_repo, album_share_repo, } } } #[async_trait] impl AlbumService for AlbumServiceImpl { async fn create_album(&self, data: CreateAlbumData<'_>) -> CoreResult<()> { if data.name.is_empty() { return Err(CoreError::Validation( "Album name cannot be empty".to_string(), )); } let now = Utc::now(); let album = Album { id: Uuid::new_v4(), owner_id: data.owner_id, name: data.name.to_string(), description: data.description.map(String::from), is_public: data.is_public, created_at: now, updated_at: now, }; self.album_repo.create(album).await } async fn get_album_details(&self, album_id: Uuid, user_id: Uuid) -> CoreResult { let album = self .album_repo .find_by_id(album_id) .await? .ok_or(CoreError::NotFound("Album".to_string(), album_id))?; let share_permission = self .album_share_repo .get_user_permission(album_id, user_id) .await?; if !authz::can_view_album(user_id, &album, share_permission) { return Err(CoreError::Auth("Access denied to album".to_string())); } Ok(album) } async fn add_media_to_album(&self, data: AddMediaToAlbumData, user_id: Uuid) -> CoreResult<()> { let album = self .album_repo .find_by_id(data.album_id) .await? .ok_or(CoreError::NotFound("Album".to_string(), data.album_id))?; let share_permission = self .album_share_repo .get_user_permission(data.album_id, user_id) .await?; if !authz::can_contribute_to_album(user_id, &album, share_permission) { return Err(CoreError::Auth( "User does not have permission to add media to this album".to_string(), )); } for media_id in &data.media_ids { let media = self .media_repo .find_by_id(*media_id) .await? .ok_or(CoreError::NotFound("Media".to_string(), *media_id))?; if !authz::is_owner(user_id, &media) { return Err(CoreError::Auth(format!( "Access denied to media item {}", media_id ))); } } self.album_repo .add_media_to_album(data.album_id, &data.media_ids) .await } async fn list_user_albums(&self, user_id: Uuid) -> CoreResult> { self.album_repo.list_by_user(user_id).await } async fn share_album(&self, data: ShareAlbumData, owner_id: Uuid) -> CoreResult<()> { let album = self .album_repo .find_by_id(data.album_id) .await? .ok_or(CoreError::NotFound("Album".to_string(), data.album_id))?; if !authz::is_owner(owner_id, &album) { return Err(CoreError::Auth( "Only the album owner can share the album".to_string(), )); } if data.target_user_id == owner_id { return Err(CoreError::Validation( "Cannot share album with oneself".to_string(), )); } self.album_share_repo .create_or_update_share(data.album_id, data.target_user_id, data.permission) .await } async fn update_album( &self, album_id: Uuid, user_id: Uuid, data: UpdateAlbumData<'_>, ) -> CoreResult { let mut album = self .album_repo .find_by_id(album_id) .await? .ok_or(CoreError::NotFound("Album".to_string(), album_id))?; let share_permission = self .album_share_repo .get_user_permission(album_id, user_id) .await?; if !authz::can_contribute_to_album(user_id, &album, share_permission) { return Err(CoreError::Auth( "User does not have permission to update this album".to_string(), )); } if let Some(name) = data.name { if name.is_empty() { return Err(CoreError::Validation( "Album name cannot be empty".to_string(), )); } album.name = name.to_string(); } if let Some(description) = data.description { album.description = description.map(|s| s.to_string()); } if let Some(is_public) = data.is_public { if !authz::is_owner(user_id, &album) && is_public { return Err(CoreError::Auth( "Only the owner can make an album public".to_string(), )); } album.is_public = is_public; } self.album_repo.update(album.clone()).await?; Ok(album) } async fn delete_album(&self, album_id: Uuid, user_id: Uuid) -> CoreResult<()> { let album = self .album_repo .find_by_id(album_id) .await? .ok_or(CoreError::NotFound("Album".to_string(), album_id))?; if !authz::is_owner(user_id, &album) { return Err(CoreError::Auth( "Only the album owner can delete the album".to_string(), )); } self.album_repo.delete(album_id).await } }