botserver/src/file/mod.rs

99 lines
3 KiB
Rust
Raw Normal View History

2025-10-06 10:30:17 -03:00
use actix_multipart::Multipart;
2025-10-11 20:02:14 -03:00
use actix_web::web;
2025-10-06 20:49:38 -03:00
use actix_web::{post, HttpResponse};
2025-10-28 14:00:52 -03:00
use log::{error, info};
use opendal::Operator;
2025-10-06 10:30:17 -03:00
use std::io::Write;
2025-10-06 20:49:38 -03:00
use tempfile::NamedTempFile;
2025-10-11 20:02:14 -03:00
use tokio_stream::StreamExt as TokioStreamExt;
2025-10-15 12:45:15 -03:00
use crate::config::DriveConfig;
2025-10-06 20:49:38 -03:00
use crate::shared::state::AppState;
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();
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());
2025-10-11 20:02:14 -03:00
let temp_file_path = temp_file.into_temp_path();
2025-10-28 14:00:52 -03:00
let op = state.get_ref().s3_operator.as_ref().ok_or_else(|| {
actix_web::error::ErrorInternalServerError("S3 operator is not initialized")
2025-10-15 12:45:15 -03:00
})?;
2025-10-28 14:00:52 -03:00
let s3_key = format!("{}/{}", folder_path, file_name);
match upload_to_s3(op, &s3_key, &temp_file_path).await {
2025-10-11 20:02:14 -03:00
Ok(_) => {
let _ = std::fs::remove_file(&temp_file_path);
Ok(HttpResponse::Ok().body(format!(
2025-10-28 14:00:52 -03:00
"Uploaded file '{}' to folder '{}'",
file_name, folder_path
2025-10-11 20:02:14 -03:00
)))
}
Err(e) => {
let _ = std::fs::remove_file(&temp_file_path);
Err(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
2025-10-11 20:02:14 -03:00
)))
}
}
2025-10-06 10:30:17 -03:00
}
2025-10-28 14:00:52 -03:00
pub async fn init_drive(cfg: &DriveConfig) -> Result<Operator, Box<dyn std::error::Error>> {
use opendal::services::S3;
use opendal::Operator;
let mut builder = S3::default();
builder.root("/");
builder.endpoint(&cfg.server);
builder.access_key_id(&cfg.access_key);
builder.secret_access_key(&cfg.secret_key);
if cfg.server.contains("minio") || cfg.server.contains("localhost") {
builder.enable_virtual_host_style();
}
2025-10-15 12:45:15 -03:00
2025-10-28 14:00:52 -03:00
let op = Operator::new(builder)?.finish();
info!("OpenDAL S3 operator initialized for bucket: {}", cfg.bucket);
Ok(op)
2025-10-11 20:02:14 -03:00
}
2025-10-06 20:49:38 -03:00
2025-10-11 20:02:14 -03:00
async fn upload_to_s3(
2025-10-28 14:00:52 -03:00
op: &Operator,
2025-10-11 20:02:14 -03:00
key: &str,
file_path: &std::path::Path,
2025-10-28 14:00:52 -03:00
) -> Result<(), opendal::Error> {
let data = std::fs::read(file_path)?;
op.write(key, data).await?;
2025-10-11 20:02:14 -03:00
Ok(())
2025-10-06 10:30:17 -03:00
}