104 lines
2.8 KiB
Rust
104 lines
2.8 KiB
Rust
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<usize, StoreError> {
|
|
let files: Vec<StorageLocation> = 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)
|
|
}
|
|
}
|