performance improvements

This commit is contained in:
2026-05-14 17:52:13 +02:00
parent 6bc51d6d14
commit 046f60dcb6
9 changed files with 299 additions and 134 deletions
+35 -17
View File
@@ -4,7 +4,8 @@ use axum::{
http::{HeaderMap, StatusCode},
};
use serde::Deserialize;
use std::{fs, sync::Arc};
use std::sync::Arc;
use tokio::fs;
use tracing::{error, info, warn};
use crate::{
@@ -76,15 +77,15 @@ pub async fn delete_upload(
let uploads_dir = state.data_dir.join("uploads");
let file_path = uploads_dir.join(&filename);
let canonical_dir = uploads_dir.canonicalize().map_err(|e| {
let canonical_dir = fs::canonicalize(&uploads_dir).await.map_err(|e| {
AppError::Internal("Path resolution".to_string(), Some(e.to_string()))
})?;
if let Ok(canonical_file) = file_path.canonicalize() {
if let Ok(canonical_file) = fs::canonicalize(&file_path).await {
if !canonical_file.starts_with(&canonical_dir) {
warn!("Refused delete outside uploads dir: {}", filename);
return Err(AppError::BadRequest("Invalid filename".to_string()));
}
fs::remove_file(canonical_file).map_err(|e| {
fs::remove_file(canonical_file).await.map_err(|e| {
error!("Delete error for file {}: {}", filename, e);
AppError::Internal("Delete error".to_string(), Some(e.to_string()))
})?;
@@ -104,15 +105,30 @@ pub async fn list_uploads(
let uploads_dir = state.data_dir.join("uploads");
let mut files = Vec::new();
if let Ok(entries) = fs::read_dir(uploads_dir) {
for entry in entries.flatten() {
let path = entry.path();
if path.is_file() {
if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
files.push(FileInfo {
name: name.to_string(),
url: format!("/uploads/{}", name),
});
if let Ok(mut rd) = fs::read_dir(&uploads_dir).await {
loop {
match rd.next_entry().await {
Ok(Some(entry)) => {
let path = entry.path();
let is_file = entry
.file_type()
.await
.map(|t| t.is_file())
.unwrap_or(false);
if !is_file {
continue;
}
if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
files.push(FileInfo {
name: name.to_string(),
url: format!("/uploads/{}", name),
});
}
}
Ok(None) => break,
Err(e) => {
warn!("Error iterating uploads dir: {}", e);
break;
}
}
}
@@ -178,7 +194,9 @@ pub async fn upload_file(
let uploads_dir = state.data_dir.join("uploads");
let target_path = uploads_dir.join(&final_name);
let final_path = if target_path.exists() && !query.replace.unwrap_or(false) {
let final_path = if fs::try_exists(&target_path).await.unwrap_or(false)
&& !query.replace.unwrap_or(false)
{
let timestamp = chrono::Utc::now().timestamp();
uploads_dir.join(format!("{}_{}", timestamp, final_name))
} else {
@@ -186,11 +204,11 @@ pub async fn upload_file(
};
// Final containment check.
let canonical_dir = uploads_dir.canonicalize().map_err(|e| {
let canonical_dir = fs::canonicalize(&uploads_dir).await.map_err(|e| {
AppError::Internal("Path resolution".to_string(), Some(e.to_string()))
})?;
if let Some(parent) = final_path.parent() {
let canonical_parent = parent.canonicalize().map_err(|e| {
let canonical_parent = fs::canonicalize(parent).await.map_err(|e| {
AppError::Internal("Path resolution".to_string(), Some(e.to_string()))
})?;
if canonical_parent != canonical_dir {
@@ -204,7 +222,7 @@ pub async fn upload_file(
.unwrap_or(&final_name)
.to_string();
fs::write(&final_path, &data).map_err(|e| {
fs::write(&final_path, &data).await.map_err(|e| {
error!("Failed to write file to {:?}: {}", final_path, e);
AppError::Internal("Write error".to_string(), Some(e.to_string()))
})?;