This commit is contained in:
2025-11-02 09:31:01 +01:00
commit 455e144ffb
37 changed files with 4193 additions and 0 deletions

View 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
}
}