mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-31 16:24:33 +00:00
perf: full-stack performance and security hardening
Frontend performance: - Debounce DataGrid search (150ms) and replace O(n) row wrapping with index array - Replace QueryStore deep watcher with targeted field-level persistence snapshot - Extract shared nodeConfig computed in TreeItem (eliminates 15x redundant getConfig calls) - Clear tab result data on close to free memory - Add LRU eviction for completion caches (50 entry cap) - Replace savedSql version string join with numeric counter Memory leak fixes: - Store Tauri event unlisten handles and clean up on unmount - Clean up DataGrid search timer on unmount Backend security & resource management: - Add 50MB request body size limit - Reject file uploads >100MB and SQL files >200MB - Clean up SSE channels after transfer/import completes - Recover from poisoned mutex instead of panicking - Delete temporary upload files after preview - Validate SQL file paths to prevent directory traversal
This commit is contained in:
@@ -75,7 +75,53 @@ pub async fn execute_sql_file(
|
||||
let app = state.app.clone();
|
||||
let state_clone = state.clone();
|
||||
|
||||
let file_path = std::path::Path::new(&req.file_path);
|
||||
if !file_path.is_absolute() {
|
||||
return Err(AppError("File path must be absolute".to_string()));
|
||||
}
|
||||
if req.file_path.contains("..") {
|
||||
return Err(AppError("File path must not contain '..'".to_string()));
|
||||
}
|
||||
|
||||
tokio::spawn(async move {
|
||||
match std::fs::metadata(&req.file_path) {
|
||||
Ok(meta) if meta.len() > 200 * 1024 * 1024 => {
|
||||
let progress = dbx_core::sql::SqlFileProgress {
|
||||
execution_id: req.execution_id.clone(),
|
||||
status: dbx_core::sql::SqlFileStatus::Error,
|
||||
statement_index: 0,
|
||||
success_count: 0,
|
||||
failure_count: 0,
|
||||
affected_rows: 0,
|
||||
elapsed_ms: 0,
|
||||
statement_summary: String::new(),
|
||||
error: Some(format!("File too large: {} bytes (max {} bytes)", meta.len(), 200 * 1024 * 1024)),
|
||||
};
|
||||
if let Ok(json) = serde_json::to_string(&progress) {
|
||||
let _ = tx.send(json);
|
||||
}
|
||||
return;
|
||||
}
|
||||
Err(e) => {
|
||||
let progress = dbx_core::sql::SqlFileProgress {
|
||||
execution_id: req.execution_id.clone(),
|
||||
status: dbx_core::sql::SqlFileStatus::Error,
|
||||
statement_index: 0,
|
||||
success_count: 0,
|
||||
failure_count: 0,
|
||||
affected_rows: 0,
|
||||
elapsed_ms: 0,
|
||||
statement_summary: String::new(),
|
||||
error: Some(e.to_string()),
|
||||
};
|
||||
if let Ok(json) = serde_json::to_string(&progress) {
|
||||
let _ = tx.send(json);
|
||||
}
|
||||
return;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let file_content = match std::fs::read_to_string(&req.file_path) {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
@@ -196,7 +242,7 @@ pub async fn execute_sql_file(
|
||||
let _ = tx.send(json);
|
||||
}
|
||||
|
||||
state_clone.sse_channels.write().await.remove(&req.execution_id);
|
||||
state_clone.remove_sse_channel(&req.execution_id).await;
|
||||
});
|
||||
|
||||
Ok(Json(serde_json::json!({ "executionId": execution_id })))
|
||||
|
||||
@@ -34,12 +34,17 @@ pub async fn preview_import(
|
||||
let file_name = field.file_name().unwrap_or("upload.csv").to_string();
|
||||
let data = field.bytes().await.map_err(|e| AppError(e.to_string()))?;
|
||||
|
||||
if data.len() > 100 * 1024 * 1024 {
|
||||
return Err(AppError(format!("File too large: {} bytes (max {} bytes)", data.len(), 100 * 1024 * 1024)));
|
||||
}
|
||||
|
||||
let file_path = tmp_dir.join(&file_name);
|
||||
std::fs::write(&file_path, &data).map_err(|e| AppError(e.to_string()))?;
|
||||
|
||||
let file_path_str = file_path.to_string_lossy().to_string();
|
||||
let preview = table_import::preview_table_import_file_core(&file_path_str).map_err(AppError)?;
|
||||
|
||||
let preview = table_import::preview_table_import_file_core(&file_path_str);
|
||||
let _ = std::fs::remove_file(&file_path);
|
||||
let preview = preview.map_err(AppError)?;
|
||||
return Ok(Json(serde_json::to_value(preview).map_err(|e| AppError(e.to_string()))?));
|
||||
}
|
||||
|
||||
|
||||
@@ -139,8 +139,7 @@ pub async fn start_transfer(
|
||||
let _ = tx.send(json);
|
||||
}
|
||||
|
||||
// Clean up channel
|
||||
state_clone.sse_channels.write().await.remove(&req.transfer_id);
|
||||
state_clone.remove_sse_channel(&req.transfer_id).await;
|
||||
});
|
||||
|
||||
Ok(Json(serde_json::json!({ "transferId": transfer_id })))
|
||||
|
||||
Reference in New Issue
Block a user