init
This commit is contained in:
112
libertas_api/src/services/media_service.rs
Normal file
112
libertas_api/src/services/media_service.rs
Normal file
@@ -0,0 +1,112 @@
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use chrono::Datelike;
|
||||
use futures::stream::StreamExt;
|
||||
use libertas_core::{
|
||||
error::{CoreError, CoreResult},
|
||||
models::Media,
|
||||
repositories::MediaRepository,
|
||||
schema::UploadMediaData,
|
||||
services::MediaService,
|
||||
};
|
||||
use sha2::{Digest, Sha256};
|
||||
use tokio::{fs, io::AsyncWriteExt};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::config::Config;
|
||||
|
||||
pub struct MediaServiceImpl {
|
||||
repo: Arc<dyn MediaRepository>,
|
||||
config: Config,
|
||||
}
|
||||
|
||||
impl MediaServiceImpl {
|
||||
pub fn new(repo: Arc<dyn MediaRepository>, config: Config) -> Self {
|
||||
Self { repo, config }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl MediaService for MediaServiceImpl {
|
||||
async fn upload_media(&self, mut data: UploadMediaData<'_>) -> CoreResult<Media> {
|
||||
let mut hasher = Sha256::new();
|
||||
let mut file_bytes = Vec::new();
|
||||
|
||||
while let Some(chunk_result) = data.stream.next().await {
|
||||
let chunk = chunk_result.map_err(|e| CoreError::Io(e))?;
|
||||
hasher.update(&chunk);
|
||||
file_bytes.extend_from_slice(&chunk);
|
||||
}
|
||||
|
||||
let hash = format!("{:x}", hasher.finalize());
|
||||
|
||||
if self.repo.find_by_hash(&hash).await?.is_some() {
|
||||
return Err(CoreError::Duplicate(
|
||||
"A file with this content already exists".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let now = chrono::Utc::now();
|
||||
let year = now.year().to_string();
|
||||
let month = format!("{:02}", now.month());
|
||||
let mut dest_path = PathBuf::from(&self.config.media_library_path);
|
||||
dest_path.push(year.clone());
|
||||
dest_path.push(month.clone());
|
||||
|
||||
fs::create_dir_all(&dest_path)
|
||||
.await
|
||||
.map_err(|e| CoreError::Io(e))?;
|
||||
|
||||
dest_path.push(&data.filename);
|
||||
|
||||
let storage_path_str = PathBuf::from(&year)
|
||||
.join(&month)
|
||||
.join(&data.filename)
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
|
||||
let mut file = fs::File::create(&dest_path)
|
||||
.await
|
||||
.map_err(|e| CoreError::Io(e))?;
|
||||
|
||||
file.write_all(&file_bytes)
|
||||
.await
|
||||
.map_err(|e| CoreError::Io(e))?;
|
||||
|
||||
let media_model = Media {
|
||||
id: Uuid::new_v4(),
|
||||
owner_id: data.owner_id,
|
||||
storage_path: storage_path_str,
|
||||
original_filename: data.filename,
|
||||
mime_type: data.mime_type,
|
||||
hash,
|
||||
created_at: now,
|
||||
extracted_location: None,
|
||||
width: None,
|
||||
height: None,
|
||||
};
|
||||
|
||||
self.repo.create(&media_model).await?;
|
||||
|
||||
Ok(media_model)
|
||||
}
|
||||
|
||||
async fn get_media_details(&self, id: Uuid, user_id: Uuid) -> CoreResult<Media> {
|
||||
let media = self
|
||||
.repo
|
||||
.find_by_id(id)
|
||||
.await?
|
||||
.ok_or(CoreError::NotFound("Media".to_string(), id))?;
|
||||
|
||||
if media.owner_id != user_id {
|
||||
return Err(CoreError::Auth("Access denied".to_string()));
|
||||
}
|
||||
|
||||
Ok(media)
|
||||
}
|
||||
|
||||
async fn list_user_media(&self, user_id: Uuid) -> CoreResult<Vec<Media>> {
|
||||
self.repo.list_by_user(user_id).await
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user