mirror of
https://github.com/RightNow-AI/openfang.git
synced 2026-07-30 06:32:17 +00:00
fix: Fix the error when uploading files with Unicode characters in filenames
This commit is contained in:
Generated
+24
@@ -404,6 +404,7 @@ dependencies = [
|
||||
"matchit",
|
||||
"memchr",
|
||||
"mime",
|
||||
"multer",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"serde_core",
|
||||
@@ -3481,6 +3482,23 @@ dependencies = [
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "multer"
|
||||
version = "3.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"encoding_rs",
|
||||
"futures-util",
|
||||
"http",
|
||||
"httparse",
|
||||
"memchr",
|
||||
"mime",
|
||||
"spin",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.18"
|
||||
@@ -6038,6 +6056,12 @@ dependencies = [
|
||||
"system-deps",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||
|
||||
[[package]]
|
||||
name = "spinning_top"
|
||||
version = "0.3.0"
|
||||
|
||||
+1
-1
@@ -84,7 +84,7 @@ url = "2"
|
||||
wasmtime = "41"
|
||||
|
||||
# HTTP server (for API daemon)
|
||||
axum = { version = "0.8", features = ["ws"] }
|
||||
axum = { version = "0.8", features = ["ws", "multipart"] }
|
||||
tower = "0.5"
|
||||
tower-http = { version = "0.6", features = ["cors", "trace", "compression-gzip", "compression-br"] }
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Route handlers for the OpenFang API.
|
||||
|
||||
use crate::types::*;
|
||||
use axum::extract::{Path, Query, State};
|
||||
use axum::extract::{Multipart, Path, Query, State};
|
||||
use axum::http::StatusCode;
|
||||
use axum::response::IntoResponse;
|
||||
use axum::Json;
|
||||
@@ -9319,16 +9319,14 @@ fn is_allowed_content_type(ct: &str) -> bool {
|
||||
|
||||
/// POST /api/agents/{id}/upload — Upload a file attachment.
|
||||
///
|
||||
/// Accepts raw body bytes. The client must set:
|
||||
/// - `Content-Type` header (e.g., `image/png`, `text/plain`, `application/pdf`)
|
||||
/// - `X-Filename` header (original filename)
|
||||
/// Accepts multipart/form-data. The client must include a file field with:
|
||||
/// - `Content-Type` field (e.g., `image/png`, `text/plain`, `application/pdf`)
|
||||
/// - `filename` attribute (original filename)
|
||||
pub async fn upload_file(
|
||||
State(state): State<Arc<AppState>>,
|
||||
Path(id): Path<String>,
|
||||
headers: axum::http::HeaderMap,
|
||||
body: axum::body::Bytes,
|
||||
mut multipart: Multipart,
|
||||
) -> impl IntoResponse {
|
||||
// Validate agent ID format
|
||||
let _agent_id: AgentId = match id.parse() {
|
||||
Ok(id) => id,
|
||||
Err(_) => {
|
||||
@@ -9339,12 +9337,62 @@ pub async fn upload_file(
|
||||
}
|
||||
};
|
||||
|
||||
// Extract content type
|
||||
let content_type = headers
|
||||
.get(axum::http::header::CONTENT_TYPE)
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.unwrap_or("application/octet-stream")
|
||||
.to_string();
|
||||
let mut file_data: Option<(Option<String>, String, axum::body::Bytes)> = None;
|
||||
let mut filename_from_field: Option<String> = None;
|
||||
|
||||
while let Some(field) = multipart.next_field().await.transpose() {
|
||||
let field = match field {
|
||||
Ok(f) => f,
|
||||
Err(e) => {
|
||||
return (
|
||||
StatusCode::BAD_REQUEST,
|
||||
Json(serde_json::json!({"error": format!("Failed to read field: {}", e)})),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let field_name = field.name().unwrap_or("").to_string();
|
||||
|
||||
match field_name.as_str() {
|
||||
"file" => {
|
||||
let filename_attr = field.file_name().map(|s| s.to_string());
|
||||
let content_type = field
|
||||
.content_type()
|
||||
.unwrap_or("application/octet-stream")
|
||||
.to_string();
|
||||
let bytes = match field.bytes().await {
|
||||
Ok(b) => b,
|
||||
Err(e) => {
|
||||
return (
|
||||
StatusCode::BAD_REQUEST,
|
||||
Json(
|
||||
serde_json::json!({"error": format!("Failed to read file: {}", e)}),
|
||||
),
|
||||
);
|
||||
}
|
||||
};
|
||||
file_data = Some((filename_attr, content_type, bytes));
|
||||
}
|
||||
"filename" => {
|
||||
filename_from_field = field.text().await.ok();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let (filename_attr, content_type, body) = match file_data {
|
||||
Some(data) => data,
|
||||
None => {
|
||||
return (
|
||||
StatusCode::BAD_REQUEST,
|
||||
Json(serde_json::json!({"error": "No file provided"})),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let filename = filename_from_field
|
||||
.or(filename_attr)
|
||||
.unwrap_or_else(|| "upload".to_string());
|
||||
|
||||
if !is_allowed_content_type(&content_type) {
|
||||
return (
|
||||
@@ -9355,13 +9403,6 @@ pub async fn upload_file(
|
||||
);
|
||||
}
|
||||
|
||||
// Extract filename from header
|
||||
let filename = headers
|
||||
.get("X-Filename")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.unwrap_or("upload")
|
||||
.to_string();
|
||||
|
||||
// Validate size
|
||||
if body.len() > MAX_UPLOAD_SIZE {
|
||||
return (
|
||||
|
||||
@@ -297,15 +297,15 @@ var OpenFangAPI = (function() {
|
||||
function getToken() { return _authToken; }
|
||||
|
||||
function upload(agentId, file) {
|
||||
var hdrs = {
|
||||
'Content-Type': file.type || 'application/octet-stream',
|
||||
'X-Filename': file.name
|
||||
};
|
||||
var hdrs = {};
|
||||
if (_authToken) hdrs['Authorization'] = 'Bearer ' + _authToken;
|
||||
var form = new FormData();
|
||||
form.append('file', file);
|
||||
form.append('filename', file.name);
|
||||
return fetch(BASE + '/api/agents/' + agentId + '/upload', {
|
||||
method: 'POST',
|
||||
headers: hdrs,
|
||||
body: file
|
||||
body: form
|
||||
}).then(function(r) {
|
||||
if (!r.ok) throw new Error('Upload failed');
|
||||
return r.json();
|
||||
|
||||
Reference in New Issue
Block a user