2025-02-25 09:00:35 -03:00
|
|
|
use minio::s3::client::Client;
|
|
|
|
use minio::s3::args::{BucketExistsArgs, MakeBucketArgs, RemoveObjectArgs, GetObjectArgs, PutObjectArgs, ListObjectsArgs};
|
|
|
|
use minio::s3::creds::StaticProvider;
|
|
|
|
use minio::s3::error::Error as MinioError;
|
|
|
|
use minio::s3::types::{BaseUrl, Item};
|
2025-02-24 11:40:46 -03:00
|
|
|
use std::io::Cursor;
|
2025-02-25 09:00:35 -03:00
|
|
|
use std::path::Path;
|
2025-02-24 11:40:46 -03:00
|
|
|
|
|
|
|
pub struct FileManager {
|
|
|
|
client: Client,
|
|
|
|
bucket_name: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FileManager {
|
|
|
|
pub async fn new(endpoint: &str, access_key: &str, secret_key: &str, bucket_name: &str, use_ssl: bool) -> Result<Self, MinioError> {
|
2025-02-25 09:00:35 -03:00
|
|
|
// Create BaseUrl from endpoint
|
|
|
|
let base_url = BaseUrl::from_string(endpoint)?;
|
|
|
|
let static_provider = StaticProvider::new(
|
|
|
|
access_key,
|
|
|
|
secret_key,
|
|
|
|
None,
|
|
|
|
);
|
|
|
|
let client = Client::new(base_url.clone(), Some(Box::new(static_provider)), None, None).unwrap();
|
|
|
|
|
|
|
|
|
2025-02-24 11:40:46 -03:00
|
|
|
Ok(Self {
|
|
|
|
client,
|
|
|
|
bucket_name: bucket_name.to_string(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn ensure_bucket_exists(&self) -> Result<(), MinioError> {
|
|
|
|
let exists = self.client
|
2025-02-25 09:00:35 -03:00
|
|
|
.bucket_exists(&BucketExistsArgs::new(&self.bucket_name)?)
|
2025-02-24 11:40:46 -03:00
|
|
|
.await?;
|
|
|
|
if !exists {
|
|
|
|
self.client
|
2025-02-25 09:00:35 -03:00
|
|
|
.make_bucket(&MakeBucketArgs::new(&self.bucket_name)?)
|
2025-02-24 11:40:46 -03:00
|
|
|
.await?;
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn upload_file(&self, path: &str, file_data: Vec<u8>) -> Result<(), MinioError> {
|
2025-02-25 09:00:35 -03:00
|
|
|
let reader = Cursor::new(&file_data);
|
|
|
|
let file_size = file_data.len() as u64;
|
|
|
|
|
|
|
|
let args = PutObjectArgs::new(
|
|
|
|
&self.bucket_name,
|
|
|
|
path,
|
|
|
|
reader,
|
|
|
|
Some(file_size),
|
|
|
|
None
|
|
|
|
)?;
|
|
|
|
|
2025-02-24 11:40:46 -03:00
|
|
|
self.client.put_object(&args).await?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn download_file(&self, path: &str) -> Result<Vec<u8>, MinioError> {
|
2025-02-25 09:00:35 -03:00
|
|
|
let args = GetObjectArgs::new(&self.bucket_name, path)?;
|
2025-02-24 11:40:46 -03:00
|
|
|
let object = self.client.get_object(&args).await?;
|
|
|
|
let data = object.bytes().await?;
|
|
|
|
Ok(data.to_vec())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn copy_file(&self, source_path: &str, destination_path: &str) -> Result<(), MinioError> {
|
2025-02-25 09:00:35 -03:00
|
|
|
// Download the source file
|
|
|
|
let data = self.download_file(source_path).await?;
|
|
|
|
|
|
|
|
// Upload it to the destination
|
|
|
|
let reader = Cursor::new(&data);
|
|
|
|
let file_size = data.len() as u64;
|
|
|
|
|
|
|
|
let args = PutObjectArgs::new(
|
|
|
|
&self.bucket_name,
|
|
|
|
destination_path,
|
|
|
|
reader,
|
|
|
|
Some(file_size),
|
|
|
|
None
|
|
|
|
)?;
|
|
|
|
|
|
|
|
self.client.put_object(&args).await?;
|
2025-02-24 11:40:46 -03:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn move_file(&self, source_path: &str, destination_path: &str) -> Result<(), MinioError> {
|
|
|
|
self.copy_file(source_path, destination_path).await?;
|
|
|
|
self.delete_file(source_path).await?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn delete_file(&self, path: &str) -> Result<(), MinioError> {
|
2025-02-25 09:00:35 -03:00
|
|
|
let args = RemoveObjectArgs::new(&self.bucket_name, path)?;
|
2025-02-24 11:40:46 -03:00
|
|
|
self.client.remove_object(&args).await?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn list_files(&self, prefix: &str) -> Result<Vec<String>, MinioError> {
|
2025-02-25 09:00:35 -03:00
|
|
|
// Create a predicate function that always returns true
|
|
|
|
let predicate = |_: Vec<Item>| -> bool { true };
|
|
|
|
|
|
|
|
let args = ListObjectsArgs::new(&self.bucket_name, &predicate)?;
|
2025-02-24 11:40:46 -03:00
|
|
|
let objects = self.client.list_objects(&args).await?;
|
2025-02-25 09:00:35 -03:00
|
|
|
|
|
|
|
// Filter objects based on prefix manually
|
|
|
|
let file_names: Vec<String> = objects
|
|
|
|
.into_iter()
|
|
|
|
.filter(|obj| obj.name().starts_with(prefix))
|
|
|
|
.map(|obj| obj.name().to_string())
|
|
|
|
.collect();
|
|
|
|
|
2025-02-24 11:40:46 -03:00
|
|
|
Ok(file_names)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn get_file_contents(&self, path: &str) -> Result<String, MinioError> {
|
|
|
|
let data = self.download_file(path).await?;
|
2025-02-25 09:00:35 -03:00
|
|
|
let contents = String::from_utf8(data)
|
|
|
|
.map_err(|_| MinioError::InvalidResponse(400, "Invalid UTF-8 sequence".to_string()))?;
|
2025-02-24 11:40:46 -03:00
|
|
|
Ok(contents)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn create_folder(&self, path: &str) -> Result<(), MinioError> {
|
|
|
|
let folder_path = if path.ends_with('/') {
|
|
|
|
path.to_string()
|
|
|
|
} else {
|
|
|
|
format!("{}/", path)
|
|
|
|
};
|
2025-02-25 09:00:35 -03:00
|
|
|
|
|
|
|
// Create empty file with folder path
|
2025-02-24 11:40:46 -03:00
|
|
|
self.upload_file(&folder_path, vec![]).await
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn share_folder(&self, path: &str) -> Result<String, MinioError> {
|
2025-02-25 09:00:35 -03:00
|
|
|
// This is just a placeholder implementation
|
2025-02-24 11:40:46 -03:00
|
|
|
Ok(format!("Folder shared: {}", path))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn search_files(&self, prefix: &str, query: &str) -> Result<Vec<String>, MinioError> {
|
|
|
|
let files = self.list_files(prefix).await?;
|
|
|
|
let results = files.into_iter().filter(|f| f.contains(query)).collect();
|
|
|
|
Ok(results)
|
|
|
|
}
|
|
|
|
}
|