use std::{ffi::OsStr, fs, path::Path};
use axum::{
extract::{Json, Multipart, Path as AxumPath}, http::StatusCode, response::IntoResponse, routing::{get, post}
};
use http::{header::{CONTENT_DISPOSITION, CONTENT_TYPE}, HeaderMap, HeaderValue};
use mime_guess::{from_path};
use serde_json::Value;
use crate::{app::App, route_builder::{RouteUpload, FILE_NAME_PARAM}};
fn create_upload_route(app: &mut App, upload_def: &RouteUpload) {
let route = upload_def.get_upload_route();
let download_route = upload_def.get_download_route();
let upload_path = upload_def.path.to_string_lossy().to_string();
let uploads_router = post(async move |mut multipart: Multipart| {
let mut file_name = "".to_string();
while let Some(field) = multipart.next_field().await.unwrap() {
let field_name = field.name().unwrap_or("file").to_string();
file_name = field.file_name()
.map(|name| name.to_string())
.unwrap_or_else(|| "uploaded_file.bin".to_string());
let data = field.bytes().await.unwrap();
println!("Received file '{}' in field '{}' with {} bytes", file_name, field_name, data.len());
let file_path = format!("{}/{}", upload_path, file_name);
tokio::fs::write(&file_path, &data).await.unwrap();
}
let response = Value::Object({
let mut map = serde_json::Map::new();
map.insert("status".to_string(), Value::String("success".to_string()));
map.insert("message".to_string(), Value::String("File uploaded successfully".to_string()));
map.insert("filename".to_string(), Value::String(file_name.clone()));
map.insert("filepath".to_string(), Value::String(download_route.replace(FILE_NAME_PARAM, &file_name)));
map
});
Json(response).into_response()
});
app.route(&route, uploads_router, Some("POST"), Some(&["upload".to_string()]));
}
fn create_download_route(app: &mut App, upload_def: &RouteUpload) {
let download_route = upload_def.get_download_route();
let download_path = upload_def.path.to_string_lossy().to_string();
let download_router = get(move |AxumPath(file_name): AxumPath<String>| {
async move {
let file_path = Path::new(&download_path).join(&file_name);
if !file_path.exists() {
return StatusCode::NOT_FOUND.into_response();
}
match tokio::fs::read(&file_path).await {
Ok(contents) => {
let mime_type = from_path(&file_path)
.first_or_octet_stream()
.to_string();
let mut headers = HeaderMap::new();
headers.insert(CONTENT_TYPE, HeaderValue::from_str(&mime_type).unwrap());
headers.insert(
CONTENT_DISPOSITION,
HeaderValue::from_str(&format!(
"attachment; filename=\"{}\"",
file_name
))
.unwrap(),
);
(headers, contents).into_response()
},
Err(_) => StatusCode::INTERNAL_SERVER_ERROR.into_response(),
}
}
});
app.route(&download_route, download_router, Some("GET"), Some(&["download".to_string()]));
}
fn create_uploaded_list_route(app: &mut App, upload_def: &RouteUpload) {
let route = upload_def.get_list_files_route();
let download_route = upload_def.get_download_route();
let upload_path = upload_def.path.to_string_lossy().to_string();
let upload_list_router = get(move || {
async move {
let upload_path = Path::new(&upload_path);
if !upload_path.exists() {
return StatusCode::NOT_FOUND.into_response();
}
let entries = fs::read_dir(upload_path).unwrap();
let array = entries
.filter_map(Result::ok)
.filter(|entry|
!entry.path().extension().and_then(OsStr::to_str).unwrap_or_default().eq_ignore_ascii_case("toml"))
.map(|entry| {
let value =
download_route.replace(FILE_NAME_PARAM, entry.file_name().to_str().unwrap());
Value::String(value)
}).collect();
let body = Value::Array(array);
Json(body).into_response()
}
});
app.route(&route, upload_list_router, Some("GET"), None);
}
pub fn build_upload_routes(app: &mut App, upload_def: &RouteUpload) {
create_upload_route(app, upload_def);
create_download_route(app, upload_def);
create_uploaded_list_route(app, upload_def);
}