use std::sync::Arc; use async_trait::async_trait; use libertas_core::{authz, error::{CoreError, CoreResult}, models::{FaceRegion, Media, Person, PersonPermission}, repositories::{FaceRegionRepository, MediaRepository, PersonRepository, PersonShareRepository}, services::PersonService}; use uuid::Uuid; pub struct PersonServiceImpl { person_repo: Arc, face_repo: Arc, media_repo: Arc, person_share_repo: Arc, } impl PersonServiceImpl { pub fn new( person_repo: Arc, face_repo: Arc, media_repo: Arc, person_share_repo: Arc, ) -> Self { Self { person_repo, face_repo, media_repo, person_share_repo, } } async fn get_and_authorize_person_owner( &self, person_id: Uuid, user_id: Uuid, ) -> CoreResult { let person = self .person_repo .find_by_id(person_id) .await? .ok_or(CoreError::NotFound("Person".to_string(), person_id))?; if person.owner_id != user_id { return Err(CoreError::Auth( "User must be the owner to perform this action".to_string(), )); } Ok(person) } async fn get_and_authorize_person_access( &self, person_id: Uuid, user_id: Uuid, ) -> CoreResult { let person = self .person_repo .find_by_id(person_id) .await? .ok_or(CoreError::NotFound("Person".to_string(), person_id))?; let share_permission = self.person_share_repo.get_user_permission(person_id, user_id).await?; if !authz::can_access_person(user_id, &person, share_permission) { return Err(CoreError::Auth( "User does not have permission to access this person".to_string(), )); } Ok(person) } async fn get_and_authorize_person_usage( &self, person_id: Uuid, user_id: Uuid, ) -> CoreResult { let person = self .person_repo .find_by_id(person_id) .await? .ok_or(CoreError::NotFound("Person".to_string(), person_id))?; let share_permission = self.person_share_repo.get_user_permission(person_id, user_id).await?; if !authz::can_edit_person(user_id, &person, share_permission) { return Err(CoreError::Auth( "User does not have permission to use this person".to_string(), )); } Ok(person) } async fn authorize_media_access( &self, media_id: Uuid, user_id: Uuid, ) -> CoreResult { 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( "User does not have permission to access this media".to_string(), )); } Ok(media) } } #[async_trait] impl PersonService for PersonServiceImpl { async fn create_person( &self, name: &str, owner_id: Uuid, ) -> CoreResult { let person = Person { id: Uuid::new_v4(), owner_id, name: name.to_string(), thumbnail_media_id: None, }; self.person_repo.create(person.clone()).await?; Ok(person) } async fn get_person(&self, person_id: Uuid, user_id: Uuid) -> CoreResult { self.get_and_authorize_person_access(person_id, user_id).await } async fn list_people(&self, user_id: Uuid) -> CoreResult> { let mut owned_people = self.person_repo.list_by_user(user_id).await?; let shared_people_with_perms = self .person_share_repo .list_people_shared_with_user(user_id) .await?; let shared_people = shared_people_with_perms .into_iter() .map(|(person, _permission)| person) .collect::>(); owned_people.extend(shared_people); Ok(owned_people) } async fn update_person( &self, person_id: Uuid, name: &str, user_id: Uuid, ) -> CoreResult { let mut person = self.get_and_authorize_person_owner(person_id, user_id).await?; person.name = name.to_string(); self.person_repo.update(person.clone()).await?; Ok(person) } async fn delete_person(&self, person_id: Uuid, user_id: Uuid) -> CoreResult<()> { self.get_and_authorize_person_owner(person_id, user_id).await?; self.person_repo.delete(person_id).await } async fn assign_face_to_person( &self, face_region_id: Uuid, person_id: Uuid, user_id: Uuid, ) -> CoreResult { self.get_and_authorize_person_usage(person_id, user_id).await?; let mut face = self .face_repo .find_by_id(face_region_id) .await? .ok_or(CoreError::NotFound("FaceRegion".to_string(), face_region_id))?; self.authorize_media_access(face.media_id, user_id).await?; self.face_repo .update_person_id(face_region_id, person_id) .await?; face.person_id = Some(person_id); Ok(face) } async fn list_faces_for_media( &self, media_id: Uuid, user_id: Uuid, ) -> CoreResult> { self.authorize_media_access(media_id, user_id).await?; self.face_repo.find_by_media_id(media_id).await } async fn share_person( &self, person_id: Uuid, target_user_id: Uuid, permission: PersonPermission, owner_id: Uuid, ) -> CoreResult<()> { self.get_and_authorize_person_owner(person_id, owner_id).await?; self.person_share_repo .create_or_update_share(person_id, target_user_id, permission) .await } async fn unshare_person( &self, person_id: Uuid, target_user_id: Uuid, owner_id: Uuid, ) -> CoreResult<()> { self.get_and_authorize_person_owner(person_id, owner_id).await?; self.person_share_repo .remove_share(person_id, target_user_id) .await } }