use crate::model::image::{Image, ImageData, StorageLocation}; use crate::storage_manager::{Store, StoreError}; use async_trait::async_trait; use j_db::database::Database; use serde::{Deserialize, Serialize}; use std::collections::hash_map::DefaultHasher; use std::fs; use std::hash::Hasher; use std::os::unix::fs::MetadataExt; use std::path::PathBuf; use url::Url; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct FileStoreConfig { pub base_path: PathBuf, pub base_url: String, pub max_file_size: usize, pub max_total_storage: usize, } #[derive(Debug, Clone)] pub struct FileStore { pub config: FileStoreConfig, } impl FileStore { pub fn new(config: FileStoreConfig) -> Self { Self { config } } } #[async_trait] impl Store for FileStore { async fn store_img( &mut self, img_data: ImageData, file_name: &str, ) -> Result<(Url, StorageLocation), StoreError> { let img_data = match img_data { ImageData::Bytes(b) => b, ImageData::Link(_) => unimplemented!("No link support"), }; let mut hasher = DefaultHasher::new(); hasher.write(img_data.as_slice()); let disk_file_name = hex::encode(hasher.finish().to_be_bytes()); let file = PathBuf::from(file_name); let ext = file.extension().unwrap().to_str().unwrap(); let disk_file_name = format!("{}.{}", disk_file_name, ext); let path = self.config.base_path.join(&disk_file_name); tokio::fs::write(&path, img_data).await?; let img_link = Url::parse(&self.config.base_url) .unwrap() .join(&disk_file_name) .unwrap(); let storage_location = StorageLocation::FileStore { path }; Ok((img_link, storage_location)) } async fn delete_img(&mut self, img: Image) -> Result<(), StoreError> { match img.storage_location { StorageLocation::FileStore { path } => { tokio::fs::remove_file(path).await?; } StorageLocation::Link => unimplemented!("No link support"), } Ok(()) } fn max_image_size(&self) -> usize { self.config.max_file_size } fn max_total_storage(&self) -> usize { self.config.max_total_storage } async fn current_store_size(&self, db: &Database) -> Result { let files: Vec = db .filter(|_, _file: &Image| true) .unwrap() .map(|file: Image| file.storage_location.clone()) .collect(); let mut total_size = 0; for file in files { if let StorageLocation::FileStore { path } = file { let md = fs::metadata(path)?; total_size += md.size(); } } Ok(total_size as usize) } }