2025-10-06 20:49:38 -03:00
|
|
|
use actix_web::web;
|
2025-10-06 10:30:17 -03:00
|
|
|
use actix_multipart::Multipart;
|
2025-10-06 20:49:38 -03:00
|
|
|
use actix_web::{post, HttpResponse};
|
2025-10-06 10:30:17 -03:00
|
|
|
use std::io::Write;
|
2025-10-06 20:49:38 -03:00
|
|
|
use tempfile::NamedTempFile;
|
|
|
|
|
use tokio_stream::StreamExt;
|
2025-10-11 12:29:03 -03:00
|
|
|
use aws_sdk_s3 as s3;
|
|
|
|
|
use aws_sdk_s3::types::ByteStream;
|
2025-10-06 20:49:38 -03:00
|
|
|
use std::str::FromStr;
|
|
|
|
|
|
|
|
|
|
use crate::config::AppConfig;
|
|
|
|
|
use crate::shared::state::AppState;
|
|
|
|
|
|
2025-10-11 12:29:03 -03:00
|
|
|
pub async fn init_s3(config: &AppConfig) -> Result<s3::Client, Box<dyn std::error::Error>> {
|
|
|
|
|
let endpoint_url = if config.minio.use_ssl {
|
|
|
|
|
format!("https://{}", config.minio.server)
|
2025-10-06 20:49:38 -03:00
|
|
|
} else {
|
2025-10-11 12:29:03 -03:00
|
|
|
format!("http://{}", config.minio.server)
|
2025-10-06 20:49:38 -03:00
|
|
|
};
|
|
|
|
|
|
2025-10-11 12:29:03 -03:00
|
|
|
let config = aws_config::from_env()
|
|
|
|
|
.endpoint_url(&endpoint_url)
|
|
|
|
|
.region(aws_sdk_s3::config::Region::new("us-east-1"))
|
|
|
|
|
.credentials_provider(
|
|
|
|
|
s3::config::Credentials::new(
|
|
|
|
|
&config.minio.access_key,
|
|
|
|
|
&config.minio.secret_key,
|
|
|
|
|
None,
|
|
|
|
|
None,
|
|
|
|
|
"minio",
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
.load()
|
|
|
|
|
.await;
|
2025-10-06 20:49:38 -03:00
|
|
|
|
2025-10-11 12:29:03 -03:00
|
|
|
let client = s3::Client::new(&config);
|
|
|
|
|
Ok(client)
|
2025-10-06 20:49:38 -03:00
|
|
|
}
|
2025-10-06 10:30:17 -03:00
|
|
|
|
|
|
|
|
#[post("/files/upload/{folder_path}")]
|
|
|
|
|
pub async fn upload_file(
|
2025-10-06 20:49:38 -03:00
|
|
|
folder_path: web::Path<String>,
|
2025-10-06 10:30:17 -03:00
|
|
|
mut payload: Multipart,
|
2025-10-06 20:49:38 -03:00
|
|
|
state: web::Data<AppState>,
|
|
|
|
|
) -> Result<HttpResponse, actix_web::Error> {
|
|
|
|
|
let folder_path = folder_path.into_inner();
|
2025-10-06 10:30:17 -03:00
|
|
|
|
2025-10-06 20:49:38 -03:00
|
|
|
let mut temp_file = NamedTempFile::new().map_err(|e| {
|
|
|
|
|
actix_web::error::ErrorInternalServerError(format!("Failed to create temp file: {}", e))
|
|
|
|
|
})?;
|
2025-10-06 10:30:17 -03:00
|
|
|
|
2025-10-06 20:49:38 -03:00
|
|
|
let mut file_name: Option<String> = None;
|
|
|
|
|
|
|
|
|
|
while let Some(mut field) = payload.try_next().await? {
|
|
|
|
|
if let Some(disposition) = field.content_disposition() {
|
|
|
|
|
if let Some(name) = disposition.get_filename() {
|
|
|
|
|
file_name = Some(name.to_string());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while let Some(chunk) = field.try_next().await? {
|
|
|
|
|
temp_file.write_all(&chunk).map_err(|e| {
|
|
|
|
|
actix_web::error::ErrorInternalServerError(format!(
|
|
|
|
|
"Failed to write to temp file: {}",
|
|
|
|
|
e
|
|
|
|
|
))
|
|
|
|
|
})?;
|
2025-10-06 20:06:43 -03:00
|
|
|
}
|
|
|
|
|
}
|
2025-10-06 10:30:17 -03:00
|
|
|
|
2025-10-06 20:49:38 -03:00
|
|
|
let file_name = file_name.unwrap_or_else(|| "unnamed_file".to_string());
|
|
|
|
|
let object_name = format!("{}/{}", folder_path, file_name);
|
|
|
|
|
|
2025-10-11 12:29:03 -03:00
|
|
|
let client = state.s3_client.as_ref().ok_or_else(|| {
|
|
|
|
|
actix_web::error::ErrorInternalServerError("S3 client not initialized")
|
|
|
|
|
})?;
|
|
|
|
|
|
2025-10-06 20:49:38 -03:00
|
|
|
let bucket_name = state.config.as_ref().unwrap().minio.bucket.clone();
|
|
|
|
|
|
2025-10-11 12:29:03 -03:00
|
|
|
let body = ByteStream::from_path(temp_file.path()).await.map_err(|e| {
|
|
|
|
|
actix_web::error::ErrorInternalServerError(format!("Failed to read file: {}", e))
|
|
|
|
|
})?;
|
|
|
|
|
|
2025-10-06 20:49:38 -03:00
|
|
|
client
|
2025-10-11 12:29:03 -03:00
|
|
|
.put_object()
|
|
|
|
|
.bucket(&bucket_name)
|
|
|
|
|
.key(&object_name)
|
|
|
|
|
.body(body)
|
2025-10-06 20:49:38 -03:00
|
|
|
.send()
|
|
|
|
|
.await
|
|
|
|
|
.map_err(|e| {
|
|
|
|
|
actix_web::error::ErrorInternalServerError(format!(
|
2025-10-11 12:29:03 -03:00
|
|
|
"Failed to upload file to S3: {}",
|
2025-10-06 20:49:38 -03:00
|
|
|
e
|
|
|
|
|
))
|
|
|
|
|
})?;
|
|
|
|
|
|
|
|
|
|
temp_file.close().map_err(|e| {
|
|
|
|
|
actix_web::error::ErrorInternalServerError(format!("Failed to close temp file: {}", e))
|
|
|
|
|
})?;
|
|
|
|
|
|
|
|
|
|
Ok(HttpResponse::Ok().body(format!(
|
|
|
|
|
"Uploaded file '{}' to folder '{}'",
|
|
|
|
|
file_name, folder_path
|
|
|
|
|
)))
|
2025-10-06 10:30:17 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[post("/files/list/{folder_path}")]
|
|
|
|
|
pub async fn list_file(
|
2025-10-06 20:49:38 -03:00
|
|
|
folder_path: web::Path<String>,
|
|
|
|
|
state: web::Data<AppState>,
|
|
|
|
|
) -> Result<HttpResponse, actix_web::Error> {
|
|
|
|
|
let folder_path = folder_path.into_inner();
|
|
|
|
|
|
2025-10-11 12:29:03 -03:00
|
|
|
let client = state.s3_client.as_ref().ok_or_else(|| {
|
|
|
|
|
actix_web::error::ErrorInternalServerError("S3 client not initialized")
|
|
|
|
|
})?;
|
|
|
|
|
|
2025-10-06 20:49:38 -03:00
|
|
|
let bucket_name = "file-upload-rust-bucket";
|
|
|
|
|
|
2025-10-11 12:29:03 -03:00
|
|
|
let mut objects = client
|
|
|
|
|
.list_objects_v2()
|
|
|
|
|
.bucket(bucket_name)
|
|
|
|
|
.prefix(&folder_path)
|
|
|
|
|
.into_paginator()
|
|
|
|
|
.send();
|
2025-10-06 20:49:38 -03:00
|
|
|
|
|
|
|
|
let mut file_list = Vec::new();
|
|
|
|
|
|
2025-10-11 12:29:03 -03:00
|
|
|
while let Some(result) = objects.next().await {
|
|
|
|
|
match result {
|
|
|
|
|
Ok(output) => {
|
|
|
|
|
if let Some(contents) = output.contents {
|
|
|
|
|
for item in contents {
|
|
|
|
|
if let Some(key) = item.key {
|
|
|
|
|
file_list.push(key);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-10-06 20:49:38 -03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Err(e) => {
|
|
|
|
|
return Err(actix_web::error::ErrorInternalServerError(format!(
|
2025-10-11 12:29:03 -03:00
|
|
|
"Failed to list files in S3: {}",
|
2025-10-06 20:49:38 -03:00
|
|
|
e
|
|
|
|
|
)));
|
2025-10-06 10:30:17 -03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-06 20:49:38 -03:00
|
|
|
Ok(HttpResponse::Ok().json(file_list))
|
2025-10-06 10:30:17 -03:00
|
|
|
}
|