feat: Implement person and tag management services
- Added `Person` and `Tag` models to the core library. - Created `PersonService` and `TagService` traits with implementations for managing persons and tags. - Introduced repositories for `Person`, `Tag`, `FaceRegion`, and `PersonShare` with PostgreSQL support. - Updated authorization logic to include permissions for accessing and editing persons. - Enhanced the schema to support new models and relationships. - Implemented database migrations for new tables related to persons and tags. - Added request and response structures for API interactions with persons and tags.
This commit is contained in:
@@ -1,21 +1,29 @@
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::models::{Album, AlbumPermission, Media, Role, User};
|
||||
use crate::models::{Album, AlbumPermission, Media, Person, PersonPermission, Role, User};
|
||||
|
||||
pub trait Ownable {
|
||||
fn owner_id(&self) -> Uuid;
|
||||
}
|
||||
|
||||
impl Ownable for Media {
|
||||
fn owner_id(&self) -> Uuid {
|
||||
self.owner_id
|
||||
}
|
||||
}
|
||||
|
||||
impl Ownable for Album {
|
||||
fn owner_id(&self) -> Uuid {
|
||||
self.owner_id
|
||||
}
|
||||
}
|
||||
|
||||
impl Ownable for Person {
|
||||
fn owner_id(&self) -> Uuid {
|
||||
self.owner_id
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_admin(user: &User) -> bool {
|
||||
user.role == Role::Admin
|
||||
}
|
||||
@@ -39,3 +47,11 @@ pub fn can_contribute_to_album(
|
||||
) -> bool {
|
||||
is_owner(user_id, album) || share_permission == Some(AlbumPermission::Contribute)
|
||||
}
|
||||
|
||||
pub fn can_access_person(user_id: Uuid, person: &Person, share_permission: Option<PersonPermission>) -> bool {
|
||||
is_owner(user_id, person) || share_permission.is_some()
|
||||
}
|
||||
|
||||
pub fn can_edit_person(user_id: Uuid, person: &Person, share_permission: Option<PersonPermission>) -> bool {
|
||||
is_owner(user_id, person) || share_permission == Some(PersonPermission::CanUse)
|
||||
}
|
||||
@@ -93,6 +93,7 @@ pub struct Album {
|
||||
pub updated_at: chrono::DateTime<chrono::Utc>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Person {
|
||||
pub id: uuid::Uuid,
|
||||
pub owner_id: uuid::Uuid,
|
||||
@@ -100,6 +101,7 @@ pub struct Person {
|
||||
pub thumbnail_media_id: Option<uuid::Uuid>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FaceRegion {
|
||||
pub id: uuid::Uuid,
|
||||
pub media_id: uuid::Uuid,
|
||||
@@ -150,4 +152,40 @@ pub struct AlbumShare {
|
||||
pub struct MediaBundle {
|
||||
pub media: Media,
|
||||
pub metadata: Vec<MediaMetadata>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Tag {
|
||||
pub id: uuid::Uuid,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
|
||||
pub enum PersonPermission {
|
||||
View,
|
||||
CanUse,
|
||||
}
|
||||
|
||||
impl PersonPermission {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
PersonPermission::View => "view",
|
||||
PersonPermission::CanUse => "can_use",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for PersonPermission {
|
||||
fn from(s: &str) -> Self {
|
||||
match s {
|
||||
"can_use" => PersonPermission::CanUse,
|
||||
_ => PersonPermission::View,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PersonShare {
|
||||
pub person_id: uuid::Uuid,
|
||||
pub user_id: uuid::Uuid,
|
||||
pub permission: PersonPermission,
|
||||
}
|
||||
@@ -3,7 +3,7 @@ use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
error::CoreResult,
|
||||
models::{Album, AlbumPermission, Media, MediaMetadata, User}, schema::ListMediaOptions,
|
||||
models::{Album, AlbumPermission, FaceRegion, Media, MediaMetadata, Person, PersonPermission, Tag, User}, schema::ListMediaOptions,
|
||||
};
|
||||
|
||||
#[async_trait]
|
||||
@@ -57,4 +57,54 @@ pub trait AlbumShareRepository: Send + Sync {
|
||||
pub trait MediaMetadataRepository: Send + Sync {
|
||||
async fn create_batch(&self, metadata: &[MediaMetadata]) -> CoreResult<()>;
|
||||
async fn find_by_media_id(&self, media_id: Uuid) -> CoreResult<Vec<MediaMetadata>>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait TagRepository: Send + Sync {
|
||||
async fn find_or_create_tags(&self, tag_names: &[String]) -> CoreResult<Vec<Tag>>;
|
||||
async fn add_tags_to_media(&self, media_id: Uuid, tag_ids: &[Uuid]) -> CoreResult<()>;
|
||||
async fn remove_tags_from_media(&self, media_id: Uuid, tag_ids: &[Uuid]) -> CoreResult<()>;
|
||||
async fn list_tags_for_media(&self, media_id: Uuid) -> CoreResult<Vec<Tag>>;
|
||||
async fn find_tag_by_name(&self, name: &str) -> CoreResult<Option<Tag>>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait PersonRepository: Send + Sync {
|
||||
async fn create(&self, person: Person) -> CoreResult<()>;
|
||||
async fn find_by_id(&self, id: Uuid) -> CoreResult<Option<Person>>;
|
||||
async fn list_by_user(&self, user_id: Uuid) -> CoreResult<Vec<Person>>;
|
||||
async fn update(&self, person: Person) -> CoreResult<()>;
|
||||
async fn delete(&self, id: Uuid) -> CoreResult<()>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait FaceRegionRepository: Send + Sync {
|
||||
async fn create_batch(&self, face_regions: &[FaceRegion]) -> CoreResult<()>;
|
||||
async fn find_by_media_id(&self, media_id: Uuid) -> CoreResult<Vec<FaceRegion>>;
|
||||
async fn find_by_id(&self, face_region_id: Uuid) -> CoreResult<Option<FaceRegion>>;
|
||||
async fn update_person_id(&self, face_region_id: Uuid, person_id: Uuid) -> CoreResult<()>;
|
||||
async fn delete(&self, face_region_id: Uuid) -> CoreResult<()>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait PersonShareRepository: Send + Sync {
|
||||
async fn create_or_update_share(
|
||||
&self,
|
||||
person_id: Uuid,
|
||||
user_id: Uuid,
|
||||
permission: PersonPermission,
|
||||
) -> CoreResult<()>;
|
||||
|
||||
async fn remove_share(&self, person_id: Uuid, user_id: Uuid) -> CoreResult<()>;
|
||||
|
||||
async fn get_user_permission(
|
||||
&self,
|
||||
person_id: Uuid,
|
||||
user_id: Uuid,
|
||||
) -> CoreResult<Option<PersonPermission>>;
|
||||
|
||||
async fn list_people_shared_with_user(
|
||||
&self,
|
||||
user_id: Uuid,
|
||||
) -> CoreResult<Vec<(Person, PersonPermission)>>;
|
||||
}
|
||||
@@ -3,7 +3,7 @@ use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
error::CoreResult,
|
||||
models::{Album, Media, MediaBundle, User},
|
||||
models::{Album, FaceRegion, Media, MediaBundle, Person, PersonPermission, Tag, User},
|
||||
schema::{
|
||||
AddMediaToAlbumData, CreateAlbumData, CreateUserData, ListMediaOptions, LoginUserData, ShareAlbumData, UpdateAlbumData, UploadMediaData
|
||||
},
|
||||
@@ -40,3 +40,48 @@ pub trait AlbumService: Send + Sync {
|
||||
) -> CoreResult<Album>;
|
||||
async fn delete_album(&self, album_id: Uuid, user_id: Uuid) -> CoreResult<()>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait TagService: Send + Sync {
|
||||
async fn add_tags_to_media(&self, media_id: Uuid, tag_names: &[String], user_id: Uuid) -> CoreResult<Vec<Tag>>;
|
||||
async fn remove_tags_from_media(&self, media_id: Uuid, tag_names: &[String], user_id: Uuid) -> CoreResult<()>;
|
||||
async fn list_tags_for_media(&self, media_id: Uuid, user_id: Uuid) -> CoreResult<Vec<Tag>>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait PersonService: Send + Sync {
|
||||
async fn create_person(&self, name: &str, owner_id: Uuid) -> CoreResult<Person>;
|
||||
async fn get_person(&self, person_id: Uuid, user_id: Uuid) -> CoreResult<Person>;
|
||||
async fn list_people(&self, user_id: Uuid) -> CoreResult<Vec<Person>>;
|
||||
async fn update_person(
|
||||
&self,
|
||||
person_id: Uuid,
|
||||
name: &str,
|
||||
user_id: Uuid,
|
||||
) -> CoreResult<Person>;
|
||||
async fn delete_person(&self, person_id: Uuid, user_id: Uuid) -> CoreResult<()>;
|
||||
|
||||
async fn assign_face_to_person(
|
||||
&self,
|
||||
face_region_id: Uuid,
|
||||
person_id: Uuid,
|
||||
user_id: Uuid,
|
||||
) -> CoreResult<FaceRegion>;
|
||||
|
||||
async fn list_faces_for_media(&self, media_id: Uuid, user_id: Uuid) -> CoreResult<Vec<FaceRegion>>;
|
||||
|
||||
async fn share_person(
|
||||
&self,
|
||||
person_id: Uuid,
|
||||
target_user_id: Uuid,
|
||||
permission: PersonPermission,
|
||||
owner_id: Uuid,
|
||||
) -> CoreResult<()>;
|
||||
|
||||
async fn unshare_person(
|
||||
&self,
|
||||
person_id: Uuid,
|
||||
target_user_id: Uuid,
|
||||
owner_id: Uuid,
|
||||
) -> CoreResult<()>;
|
||||
}
|
||||
Reference in New Issue
Block a user