use axum::{
extract::{Path, State, Query, Multipart},
http::StatusCode,
response::IntoResponse,
};
use serde::Deserialize;
use tracing::{debug, error};
use serde_json;
use crate::{
server::AppState,
models::{UploadResponse},
handlers::{error_response, success_response},
};
#[derive(Debug, Deserialize)]
pub struct ListQuery {
pub sort: Option<String>,
pub order: Option<String>,
pub limit: Option<usize>,
}
pub async fn list(
Path(path): Path<String>,
Query(_query): Query<ListQuery>,
State(state): State<AppState>,
) -> impl IntoResponse {
debug!("Listing files at path: {}", path);
match state.file_service.list_directory(&path).await {
Ok(directory) => success_response(StatusCode::OK, &serde_json::to_string(&directory).unwrap_or_default()),
Err(e) => {
error!("Failed to list directory: {}", e);
error_response(StatusCode::INTERNAL_SERVER_ERROR, &e.to_string())
}
}
}
pub async fn upload(
Path(path): Path<String>,
State(state): State<AppState>,
mut multipart: Multipart,
) -> impl IntoResponse {
debug!("Uploading file to path: {}", path);
let mut file_data = Vec::new();
let mut filename = String::new();
while let Some(field) = multipart.next_field().await.unwrap_or(None) {
let field_name = field.name().unwrap_or("").to_string();
if field_name == "file" {
filename = field.file_name().unwrap_or("upload").to_string();
file_data = field.bytes().await.unwrap_or_default().to_vec();
}
}
if file_data.is_empty() {
return error_response(StatusCode::BAD_REQUEST, "No file data received");
}
let full_path = if path.ends_with('/') {
format!("{}{}", path, filename)
} else {
path
};
match state.file_service.write_file(&full_path, &file_data).await {
Ok(_) => {
let response = UploadResponse {
path: full_path,
size: file_data.len() as u64,
message: "File uploaded successfully".to_string(),
};
success_response(StatusCode::CREATED, &serde_json::to_string(&response).unwrap_or_default())
}
Err(e) => {
error!("Failed to upload file: {}", e);
error_response(StatusCode::INTERNAL_SERVER_ERROR, &e.to_string())
}
}
}
pub async fn update(
Path(path): Path<String>,
State(state): State<AppState>,
body: axum::body::Bytes,
) -> impl IntoResponse {
debug!("Updating file at path: {}", path);
match state.file_service.write_file(&path, &body).await {
Ok(_) => success_response(StatusCode::OK, "File updated successfully"),
Err(e) => {
error!("Failed to update file: {}", e);
error_response(StatusCode::INTERNAL_SERVER_ERROR, &e.to_string())
}
}
}
pub async fn delete(
Path(path): Path<String>,
State(state): State<AppState>,
) -> impl IntoResponse {
debug!("Deleting path: {}", path);
match state.file_service.delete(&path).await {
Ok(_) => success_response(StatusCode::OK, "File deleted successfully"),
Err(e) => {
error!("Failed to delete file: {}", e);
error_response(StatusCode::INTERNAL_SERVER_ERROR, &e.to_string())
}
}
}
pub async fn metadata(
Path(path): Path<String>,
State(state): State<AppState>,
) -> impl IntoResponse {
debug!("Getting metadata for path: {}", path);
match state.file_service.get_metadata(&path).await {
Ok(metadata) => success_response(StatusCode::OK, &serde_json::to_string(&metadata).unwrap_or_default()),
Err(e) => {
error!("Failed to get metadata: {}", e);
error_response(StatusCode::INTERNAL_SERVER_ERROR, &e.to_string())
}
}
}
pub async fn download(
Path(path): Path<String>,
State(state): State<AppState>,
) -> impl IntoResponse {
debug!("Downloading file: {}", path);
match state.file_service.read_file(&path).await {
Ok(content) => {
let mime_type = mime_guess::from_path(&path)
.first_or_octet_stream()
.to_string();
(
StatusCode::OK,
[("Content-Type", mime_type.as_str())],
content
).into_response()
}
Err(e) => {
error!("Failed to download file: {}", e);
(
StatusCode::INTERNAL_SERVER_ERROR,
[("Content-Type", "application/json")],
format!(r#"{{"error": "{}"}}"#, e).into_bytes()
).into_response()
}
}
}