feat: enhance album and media management with update and delete functionalities
This commit is contained in:
@@ -2,12 +2,13 @@ use axum::{
|
|||||||
Json, Router,
|
Json, Router,
|
||||||
extract::{Path, State},
|
extract::{Path, State},
|
||||||
http::StatusCode,
|
http::StatusCode,
|
||||||
|
routing::{get, post},
|
||||||
};
|
};
|
||||||
use libertas_core::{
|
use libertas_core::{
|
||||||
models::AlbumPermission,
|
models::{Album, AlbumPermission},
|
||||||
schema::{AddMediaToAlbumData, CreateAlbumData, ShareAlbumData},
|
schema::{AddMediaToAlbumData, CreateAlbumData, ShareAlbumData, UpdateAlbumData},
|
||||||
};
|
};
|
||||||
use serde::Deserialize;
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{error::ApiError, middleware::auth::UserId, state::AppState};
|
use crate::{error::ApiError, middleware::auth::UserId, state::AppState};
|
||||||
@@ -68,7 +69,7 @@ pub struct ShareAlbumRequest {
|
|||||||
|
|
||||||
async fn share_album(
|
async fn share_album(
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
UserId(owner_id): UserId, // The person sharing must be authenticated
|
UserId(owner_id): UserId,
|
||||||
Path(album_id): Path<Uuid>,
|
Path(album_id): Path<Uuid>,
|
||||||
Json(payload): Json<ShareAlbumRequest>,
|
Json(payload): Json<ShareAlbumRequest>,
|
||||||
) -> Result<StatusCode, ApiError> {
|
) -> Result<StatusCode, ApiError> {
|
||||||
@@ -83,9 +84,95 @@ async fn share_album(
|
|||||||
Ok(StatusCode::OK)
|
Ok(StatusCode::OK)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct AlbumResponse {
|
||||||
|
id: Uuid,
|
||||||
|
owner_id: Uuid,
|
||||||
|
name: String,
|
||||||
|
description: Option<String>,
|
||||||
|
is_public: bool,
|
||||||
|
created_at: chrono::DateTime<chrono::Utc>,
|
||||||
|
updated_at: chrono::DateTime<chrono::Utc>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Album> for AlbumResponse {
|
||||||
|
fn from(album: Album) -> Self {
|
||||||
|
Self {
|
||||||
|
id: album.id,
|
||||||
|
owner_id: album.owner_id,
|
||||||
|
name: album.name,
|
||||||
|
description: album.description,
|
||||||
|
is_public: album.is_public,
|
||||||
|
created_at: album.created_at,
|
||||||
|
updated_at: album.updated_at,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct UpdateAlbumRequest {
|
||||||
|
name: Option<String>,
|
||||||
|
description: Option<Option<String>>,
|
||||||
|
is_public: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn list_user_albums(
|
||||||
|
State(state): State<AppState>,
|
||||||
|
UserId(user_id): UserId,
|
||||||
|
) -> Result<Json<Vec<AlbumResponse>>, ApiError> {
|
||||||
|
let albums = state.album_service.list_user_albums(user_id).await?;
|
||||||
|
let response = albums.into_iter().map(AlbumResponse::from).collect();
|
||||||
|
Ok(Json(response))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_album_details(
|
||||||
|
State(state): State<AppState>,
|
||||||
|
UserId(user_id): UserId,
|
||||||
|
Path(album_id): Path<Uuid>,
|
||||||
|
) -> Result<Json<AlbumResponse>, ApiError> {
|
||||||
|
let album = state
|
||||||
|
.album_service
|
||||||
|
.get_album_details(album_id, user_id)
|
||||||
|
.await?;
|
||||||
|
Ok(Json(album.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update_album(
|
||||||
|
State(state): State<AppState>,
|
||||||
|
UserId(user_id): UserId,
|
||||||
|
Path(album_id): Path<Uuid>,
|
||||||
|
Json(payload): Json<UpdateAlbumRequest>,
|
||||||
|
) -> Result<Json<AlbumResponse>, ApiError> {
|
||||||
|
let data = UpdateAlbumData {
|
||||||
|
name: payload.name.as_deref(),
|
||||||
|
description: payload.description.as_ref().map(|opt_s| opt_s.as_deref()),
|
||||||
|
is_public: payload.is_public,
|
||||||
|
};
|
||||||
|
let album = state
|
||||||
|
.album_service
|
||||||
|
.update_album(album_id, user_id, data)
|
||||||
|
.await?;
|
||||||
|
Ok(Json(album.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete_album(
|
||||||
|
State(state): State<AppState>,
|
||||||
|
UserId(user_id): UserId,
|
||||||
|
Path(album_id): Path<Uuid>,
|
||||||
|
) -> Result<StatusCode, ApiError> {
|
||||||
|
state.album_service.delete_album(album_id, user_id).await?;
|
||||||
|
Ok(StatusCode::NO_CONTENT)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn album_routes() -> Router<AppState> {
|
pub fn album_routes() -> Router<AppState> {
|
||||||
Router::new()
|
Router::new()
|
||||||
.route("/", axum::routing::post(create_album))
|
.route("/", post(create_album).get(list_user_albums))
|
||||||
.route("/{album_id}/media", axum::routing::post(add_media_to_album))
|
.route(
|
||||||
.route("/{album_id}/share", axum::routing::post(share_album))
|
"/{id}",
|
||||||
|
get(get_album_details)
|
||||||
|
.put(update_album)
|
||||||
|
.delete(delete_album),
|
||||||
|
)
|
||||||
|
.route("/{id}/media", post(add_media_to_album))
|
||||||
|
.route("/{id}/share", post(share_album))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
use axum::{Json, extract::State, http::StatusCode};
|
use axum::{Json, extract::State, http::StatusCode};
|
||||||
use libertas_core::schema::{CreateUserData, LoginUserData};
|
use libertas_core::schema::{CreateUserData, LoginUserData, UserResponse};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
use crate::{error::ApiError, middleware::auth::UserId, state::AppState};
|
use crate::{error::ApiError, state::AppState};
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct RegisterRequest {
|
pub struct RegisterRequest {
|
||||||
@@ -12,13 +11,6 @@ pub struct RegisterRequest {
|
|||||||
pub password: String,
|
pub password: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
|
||||||
pub struct UserResponse {
|
|
||||||
id: Uuid,
|
|
||||||
username: String,
|
|
||||||
email: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn register(
|
pub async fn register(
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Json(payload): Json<RegisterRequest>,
|
Json(payload): Json<RegisterRequest>,
|
||||||
@@ -64,16 +56,8 @@ pub async fn login(
|
|||||||
Ok(Json(LoginResponse { token }))
|
Ok(Json(LoginResponse { token }))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_me(
|
pub fn auth_routes() -> axum::Router<AppState> {
|
||||||
State(state): State<AppState>,
|
axum::Router::new()
|
||||||
UserId(user_id): UserId,
|
.route("/register", axum::routing::post(register))
|
||||||
) -> Result<Json<UserResponse>, ApiError> {
|
.route("/login", axum::routing::post(login))
|
||||||
let user = state.user_service.get_user_details(user_id).await?;
|
|
||||||
|
|
||||||
let response = UserResponse {
|
|
||||||
id: user.id,
|
|
||||||
username: user.username,
|
|
||||||
email: user.email,
|
|
||||||
};
|
|
||||||
Ok(Json(response))
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,8 @@ impl From<Media> for MediaResponse {
|
|||||||
pub fn media_routes() -> Router<AppState> {
|
pub fn media_routes() -> Router<AppState> {
|
||||||
Router::new()
|
Router::new()
|
||||||
.route("/", post(upload_media))
|
.route("/", post(upload_media))
|
||||||
.route("/{media_id}/file", get(get_media_file))
|
.route("/{id}", get(get_media_details).delete(delete_media))
|
||||||
|
.route("/{id}/file", get(get_media_file))
|
||||||
.layer(DefaultBodyLimit::max(250 * 1024 * 1024))
|
.layer(DefaultBodyLimit::max(250 * 1024 * 1024))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,3 +105,21 @@ async fn get_media_file(
|
|||||||
)))
|
)))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_media_details(
|
||||||
|
State(state): State<AppState>,
|
||||||
|
UserId(user_id): UserId,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
) -> Result<Json<MediaResponse>, ApiError> {
|
||||||
|
let media = state.media_service.get_media_details(id, user_id).await?;
|
||||||
|
Ok(Json(media.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete_media(
|
||||||
|
State(state): State<AppState>,
|
||||||
|
UserId(user_id): UserId,
|
||||||
|
Path(id): Path<Uuid>,
|
||||||
|
) -> Result<StatusCode, ApiError> {
|
||||||
|
state.media_service.delete_media(id, user_id).await?;
|
||||||
|
Ok(StatusCode::NO_CONTENT)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
pub mod album_handlers;
|
pub mod album_handlers;
|
||||||
pub mod auth_handlers;
|
pub mod auth_handlers;
|
||||||
pub mod media_handlers;
|
pub mod media_handlers;
|
||||||
|
pub mod user_handlers;
|
||||||
|
|||||||
22
libertas_api/src/handlers/user_handlers.rs
Normal file
22
libertas_api/src/handlers/user_handlers.rs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
use axum::{Json, Router, extract::State};
|
||||||
|
use libertas_core::schema::UserResponse;
|
||||||
|
|
||||||
|
use crate::{error::ApiError, middleware::auth::UserId, state::AppState};
|
||||||
|
|
||||||
|
pub async fn get_me(
|
||||||
|
State(state): State<AppState>,
|
||||||
|
UserId(user_id): UserId,
|
||||||
|
) -> Result<Json<UserResponse>, ApiError> {
|
||||||
|
let user = state.user_service.get_user_details(user_id).await?;
|
||||||
|
|
||||||
|
let response = UserResponse {
|
||||||
|
id: user.id,
|
||||||
|
username: user.username,
|
||||||
|
email: user.email,
|
||||||
|
};
|
||||||
|
Ok(Json(response))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn user_routes() -> Router<AppState> {
|
||||||
|
Router::new().route("/me", axum::routing::get(get_me))
|
||||||
|
}
|
||||||
@@ -1,19 +1,13 @@
|
|||||||
use axum::{
|
use axum::{Router, routing::get};
|
||||||
Router,
|
|
||||||
routing::{get, post},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
handlers::{album_handlers, auth_handlers, media_handlers},
|
handlers::{album_handlers, auth_handlers, media_handlers, user_handlers},
|
||||||
state::AppState,
|
state::AppState,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn api_routes() -> Router<AppState> {
|
pub fn api_routes() -> Router<AppState> {
|
||||||
let auth_routes = Router::new()
|
let auth_routes = auth_handlers::auth_routes();
|
||||||
.route("/register", post(auth_handlers::register))
|
let user_routes = user_handlers::user_routes();
|
||||||
.route("/login", post(auth_handlers::login));
|
|
||||||
|
|
||||||
let user_routes = Router::new().route("/me", get(auth_handlers::get_me));
|
|
||||||
let media_routes = media_handlers::media_routes();
|
let media_routes = media_handlers::media_routes();
|
||||||
let album_routes = album_handlers::album_routes();
|
let album_routes = album_handlers::album_routes();
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use libertas_core::{
|
|||||||
error::{CoreError, CoreResult},
|
error::{CoreError, CoreResult},
|
||||||
models::Album,
|
models::Album,
|
||||||
repositories::{AlbumRepository, AlbumShareRepository, MediaRepository},
|
repositories::{AlbumRepository, AlbumShareRepository, MediaRepository},
|
||||||
schema::{AddMediaToAlbumData, CreateAlbumData, ShareAlbumData},
|
schema::{AddMediaToAlbumData, CreateAlbumData, ShareAlbumData, UpdateAlbumData},
|
||||||
services::AlbumService,
|
services::AlbumService,
|
||||||
};
|
};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
@@ -139,4 +139,70 @@ impl AlbumService for AlbumServiceImpl {
|
|||||||
.create_or_update_share(data.album_id, data.target_user_id, data.permission)
|
.create_or_update_share(data.album_id, data.target_user_id, data.permission)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn update_album(
|
||||||
|
&self,
|
||||||
|
album_id: Uuid,
|
||||||
|
user_id: Uuid,
|
||||||
|
data: UpdateAlbumData<'_>,
|
||||||
|
) -> CoreResult<Album> {
|
||||||
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -192,4 +192,46 @@ impl MediaService for MediaServiceImpl {
|
|||||||
|
|
||||||
Err(CoreError::Auth("Access denied".to_string()))
|
Err(CoreError::Auth("Access denied".to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn delete_media(&self, id: Uuid, user_id: Uuid) -> CoreResult<()> {
|
||||||
|
let media = self
|
||||||
|
.repo
|
||||||
|
.find_by_id(id)
|
||||||
|
.await?
|
||||||
|
.ok_or(CoreError::NotFound("Media".to_string(), id))?;
|
||||||
|
|
||||||
|
let user = self
|
||||||
|
.user_repo
|
||||||
|
.find_by_id(user_id)
|
||||||
|
.await?
|
||||||
|
.ok_or(CoreError::NotFound("User".to_string(), user_id))?;
|
||||||
|
|
||||||
|
if !authz::is_owner(user_id, &media) && !authz::is_admin(&user) {
|
||||||
|
return Err(CoreError::Auth("Access denied".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let full_path = PathBuf::from(&self.config.media_library_path).join(&media.storage_path);
|
||||||
|
self.repo.delete(id).await?;
|
||||||
|
|
||||||
|
let file_size = match fs::metadata(&full_path).await {
|
||||||
|
Ok(metadata) => metadata.len() as i64,
|
||||||
|
Err(_) => 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(e) = fs::remove_file(full_path).await {
|
||||||
|
tracing::error!("Failed to delete media file from disk: {}", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.user_repo
|
||||||
|
.update_storage_used(user.id, -file_size)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let job_payload = json!({ "media_id": id });
|
||||||
|
self.nats_client
|
||||||
|
.publish("media.deleted".to_string(), job_payload.to_string().into())
|
||||||
|
.await
|
||||||
|
.map_err(|e| CoreError::Unknown(format!("Failed to publish NATS job: {}", e)))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ pub struct User {
|
|||||||
pub storage_used: i64, // in bytes
|
pub storage_used: i64, // in bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, sqlx::FromRow)]
|
||||||
pub struct Album {
|
pub struct Album {
|
||||||
pub id: uuid::Uuid,
|
pub id: uuid::Uuid,
|
||||||
pub owner_id: uuid::Uuid,
|
pub owner_id: uuid::Uuid,
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ pub trait MediaRepository: Send + Sync {
|
|||||||
height: Option<i32>,
|
height: Option<i32>,
|
||||||
location: Option<String>,
|
location: Option<String>,
|
||||||
) -> CoreResult<()>;
|
) -> CoreResult<()>;
|
||||||
|
async fn delete(&self, id: Uuid) -> CoreResult<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@@ -36,6 +37,8 @@ pub trait AlbumRepository: Send + Sync {
|
|||||||
async fn find_by_id(&self, id: Uuid) -> CoreResult<Option<Album>>;
|
async fn find_by_id(&self, id: Uuid) -> CoreResult<Option<Album>>;
|
||||||
async fn list_by_user(&self, user_id: Uuid) -> CoreResult<Vec<Album>>;
|
async fn list_by_user(&self, user_id: Uuid) -> CoreResult<Vec<Album>>;
|
||||||
async fn add_media_to_album(&self, album_id: Uuid, media_ids: &[Uuid]) -> CoreResult<()>;
|
async fn add_media_to_album(&self, album_id: Uuid, media_ids: &[Uuid]) -> CoreResult<()>;
|
||||||
|
async fn update(&self, album: Album) -> CoreResult<()>;
|
||||||
|
async fn delete(&self, id: Uuid) -> CoreResult<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
use serde::Serialize;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::models::AlbumPermission;
|
use crate::models::AlbumPermission;
|
||||||
|
|
||||||
pub struct UploadMediaData<'a> {
|
pub struct UploadMediaData<'a> {
|
||||||
@@ -26,6 +29,12 @@ pub struct CreateAlbumData<'a> {
|
|||||||
pub is_public: bool,
|
pub is_public: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct UpdateAlbumData<'a> {
|
||||||
|
pub name: Option<&'a str>,
|
||||||
|
pub description: Option<Option<&'a str>>,
|
||||||
|
pub is_public: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct AddMediaToAlbumData {
|
pub struct AddMediaToAlbumData {
|
||||||
pub album_id: uuid::Uuid,
|
pub album_id: uuid::Uuid,
|
||||||
pub media_ids: Vec<uuid::Uuid>,
|
pub media_ids: Vec<uuid::Uuid>,
|
||||||
@@ -36,3 +45,10 @@ pub struct ShareAlbumData {
|
|||||||
pub target_user_id: uuid::Uuid,
|
pub target_user_id: uuid::Uuid,
|
||||||
pub permission: AlbumPermission,
|
pub permission: AlbumPermission,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct UserResponse {
|
||||||
|
pub id: Uuid,
|
||||||
|
pub username: String,
|
||||||
|
pub email: String,
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use crate::{
|
|||||||
models::{Album, Media, User},
|
models::{Album, Media, User},
|
||||||
schema::{
|
schema::{
|
||||||
AddMediaToAlbumData, CreateAlbumData, CreateUserData, LoginUserData, ShareAlbumData,
|
AddMediaToAlbumData, CreateAlbumData, CreateUserData, LoginUserData, ShareAlbumData,
|
||||||
UploadMediaData,
|
UpdateAlbumData, UploadMediaData,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -16,6 +16,7 @@ pub trait MediaService: Send + Sync {
|
|||||||
async fn get_media_details(&self, id: Uuid, user_id: Uuid) -> CoreResult<Media>;
|
async fn get_media_details(&self, id: Uuid, user_id: Uuid) -> CoreResult<Media>;
|
||||||
async fn list_user_media(&self, user_id: Uuid) -> CoreResult<Vec<Media>>;
|
async fn list_user_media(&self, user_id: Uuid) -> CoreResult<Vec<Media>>;
|
||||||
async fn get_media_filepath(&self, id: Uuid, user_id: Uuid) -> CoreResult<String>;
|
async fn get_media_filepath(&self, id: Uuid, user_id: Uuid) -> CoreResult<String>;
|
||||||
|
async fn delete_media(&self, id: Uuid, user_id: Uuid) -> CoreResult<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@@ -31,6 +32,12 @@ pub trait AlbumService: Send + Sync {
|
|||||||
async fn get_album_details(&self, album_id: Uuid, user_id: Uuid) -> CoreResult<Album>;
|
async fn get_album_details(&self, album_id: Uuid, user_id: Uuid) -> CoreResult<Album>;
|
||||||
async fn add_media_to_album(&self, data: AddMediaToAlbumData, user_id: Uuid) -> CoreResult<()>;
|
async fn add_media_to_album(&self, data: AddMediaToAlbumData, user_id: Uuid) -> CoreResult<()>;
|
||||||
async fn list_user_albums(&self, user_id: Uuid) -> CoreResult<Vec<Album>>;
|
async fn list_user_albums(&self, user_id: Uuid) -> CoreResult<Vec<Album>>;
|
||||||
|
|
||||||
async fn share_album(&self, data: ShareAlbumData, owner_id: Uuid) -> CoreResult<()>;
|
async fn share_album(&self, data: ShareAlbumData, owner_id: Uuid) -> CoreResult<()>;
|
||||||
|
async fn update_album(
|
||||||
|
&self,
|
||||||
|
album_id: Uuid,
|
||||||
|
user_id: Uuid,
|
||||||
|
data: UpdateAlbumData<'_>,
|
||||||
|
) -> CoreResult<Album>;
|
||||||
|
async fn delete_album(&self, album_id: Uuid, user_id: Uuid) -> CoreResult<()>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,4 +88,38 @@ impl AlbumRepository for PostgresAlbumRepository {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn update(&self, album: Album) -> CoreResult<()> {
|
||||||
|
sqlx::query!(
|
||||||
|
r#"
|
||||||
|
UPDATE albums
|
||||||
|
SET name = $1, description = $2, is_public = $3, updated_at = NOW()
|
||||||
|
WHERE id = $4
|
||||||
|
"#,
|
||||||
|
album.name,
|
||||||
|
album.description,
|
||||||
|
album.is_public,
|
||||||
|
album.id
|
||||||
|
)
|
||||||
|
.execute(&self.pool)
|
||||||
|
.await
|
||||||
|
.map_err(|e| CoreError::Database(e.to_string()))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete(&self, id: Uuid) -> CoreResult<()> {
|
||||||
|
sqlx::query!(
|
||||||
|
r#"
|
||||||
|
DELETE FROM albums
|
||||||
|
WHERE id = $1
|
||||||
|
"#,
|
||||||
|
id
|
||||||
|
)
|
||||||
|
.execute(&self.pool)
|
||||||
|
.await
|
||||||
|
.map_err(|e| CoreError::Database(e.to_string()))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,4 +115,19 @@ impl MediaRepository for PostgresMediaRepository {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn delete(&self, id: Uuid) -> CoreResult<()> {
|
||||||
|
sqlx::query!(
|
||||||
|
r#"
|
||||||
|
DELETE FROM media
|
||||||
|
WHERE id = $1
|
||||||
|
"#,
|
||||||
|
id
|
||||||
|
)
|
||||||
|
.execute(&self.pool)
|
||||||
|
.await
|
||||||
|
.map_err(|e| CoreError::Database(e.to_string()))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user