From cd695514fe1b54b6b4b4505d75e4fe7318fb0198 Mon Sep 17 00:00:00 2001 From: Ashwin Kumar Sivakumar Date: Sun, 26 Jul 2026 19:25:53 +0530 Subject: [PATCH] Surface real B2 upload errors and enforce 10MB body limit on document uploads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Document upload endpoints (job_seekers, customers, companies, and the profession_shared crate used by 10 profession apps) returned a generic "File upload failed" 500 on any storage error, hiding the actual cause from both the API response and (for job_seekers/companies) the server logs, which only printed anyhow's outer context via Display instead of the full error chain via Debug. Also add an explicit DefaultBodyLimit(11MB) to every affected app's router — none had one, so axum's implicit 2MB default could silently reject uploads under the UI's advertised 10MB cap. --- apps/catering_services/src/main.rs | 3 ++- apps/companies/src/handlers/mod.rs | 4 ++-- apps/companies/src/main.rs | 3 ++- apps/customers/src/handlers.rs | 2 +- apps/customers/src/main.rs | 3 ++- apps/developers/src/main.rs | 3 ++- apps/fitness_trainers/src/main.rs | 3 ++- apps/graphic_designers/src/main.rs | 3 ++- apps/job_seekers/src/handlers.rs | 4 ++-- apps/job_seekers/src/main.rs | 3 ++- apps/makeup_artists/src/main.rs | 3 ++- apps/photographers/src/main.rs | 3 ++- apps/social_media_managers/src/main.rs | 3 ++- apps/tutors/src/main.rs | 3 ++- apps/ugc_content_creators/src/main.rs | 3 ++- apps/video_editors/src/main.rs | 3 ++- crates/contracts/src/profession_shared.rs | 2 +- 17 files changed, 32 insertions(+), 19 deletions(-) diff --git a/apps/catering_services/src/main.rs b/apps/catering_services/src/main.rs index e8b38ed..28fac13 100644 --- a/apps/catering_services/src/main.rs +++ b/apps/catering_services/src/main.rs @@ -5,7 +5,7 @@ mod handlers; mod admin; -use axum::{routing::get, Router}; +use axum::{extract::DefaultBodyLimit, routing::get, Router}; use std::net::SocketAddr; use std::sync::Arc; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; @@ -42,6 +42,7 @@ async fn main() { .nest("/api/catering-services", handlers::router()) .nest("/api/admin/catering-services", admin::router()) .route("/health", get(|| async { "Catering Services OK" })) + .layer(DefaultBodyLimit::max(11 * 1024 * 1024)) .with_state(state); let port: u16 = std::env::var("PORT") diff --git a/apps/companies/src/handlers/mod.rs b/apps/companies/src/handlers/mod.rs index c182d74..5d6aa65 100644 --- a/apps/companies/src/handlers/mod.rs +++ b/apps/companies/src/handlers/mod.rs @@ -605,8 +605,8 @@ async fn upload_documents( { Ok(u) => u, Err(e) => { - tracing::error!("B2 upload failed for company {}: {}", company.id, e); - return (StatusCode::INTERNAL_SERVER_ERROR, Json(serde_json::json!({ "error": "File upload failed" }))).into_response(); + tracing::error!("B2 upload failed for company {}: {:?}", company.id, e); + return (StatusCode::INTERNAL_SERVER_ERROR, Json(serde_json::json!({ "error": "storage_upload_failed", "detail": format!("{:?}", e) }))).into_response(); } }; diff --git a/apps/companies/src/main.rs b/apps/companies/src/main.rs index 545ecb4..b966bcd 100644 --- a/apps/companies/src/main.rs +++ b/apps/companies/src/main.rs @@ -4,7 +4,7 @@ mod handlers; -use axum::{routing::get, Router}; +use axum::{extract::DefaultBodyLimit, routing::get, Router}; use cache::RedisPool; use std::net::SocketAddr; use std::sync::Arc; @@ -51,6 +51,7 @@ async fn main() { .nest("/api/admin/companies", handlers::admin::router()) .route("/health", get(|| async { "Companies OK" })) + .layer(DefaultBodyLimit::max(11 * 1024 * 1024)) .with_state(state); let port: u16 = std::env::var("PORT") diff --git a/apps/customers/src/handlers.rs b/apps/customers/src/handlers.rs index 3637cdd..57daf09 100644 --- a/apps/customers/src/handlers.rs +++ b/apps/customers/src/handlers.rs @@ -136,7 +136,7 @@ async fn upload_document( Ok(url) => (StatusCode::OK, Json(serde_json::json!({ "url": url }))).into_response(), Err(e) => { tracing::error!("B2 upload failed: {:?}", e); - (StatusCode::INTERNAL_SERVER_ERROR, Json(serde_json::json!({ "error": "File upload failed" }))).into_response() + (StatusCode::INTERNAL_SERVER_ERROR, Json(serde_json::json!({ "error": "storage_upload_failed", "detail": format!("{:?}", e) }))).into_response() } } } diff --git a/apps/customers/src/main.rs b/apps/customers/src/main.rs index b34eaa1..7f2f4cc 100644 --- a/apps/customers/src/main.rs +++ b/apps/customers/src/main.rs @@ -3,7 +3,7 @@ mod handlers; mod admin; -use axum::{routing::get, Router}; +use axum::{extract::DefaultBodyLimit, routing::get, Router}; use std::net::SocketAddr; use std::sync::Arc; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; @@ -42,6 +42,7 @@ async fn main() { .nest("/api/customers", handlers::router()) .nest("/api/admin/leads", admin::router()) .route("/health", get(|| async { "Customers OK" })) + .layer(DefaultBodyLimit::max(11 * 1024 * 1024)) .with_state(state); let port: u16 = std::env::var("PORT") diff --git a/apps/developers/src/main.rs b/apps/developers/src/main.rs index 6e43b70..76c24f1 100644 --- a/apps/developers/src/main.rs +++ b/apps/developers/src/main.rs @@ -5,7 +5,7 @@ mod handlers; mod admin; -use axum::{routing::get, Router}; +use axum::{extract::DefaultBodyLimit, routing::get, Router}; use std::net::SocketAddr; use std::sync::Arc; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; @@ -42,6 +42,7 @@ async fn main() { .nest("/api/developers", handlers::router()) .nest("/api/admin/developers", admin::router()) .route("/health", get(|| async { "Developers OK" })) + .layer(DefaultBodyLimit::max(11 * 1024 * 1024)) .with_state(state); let port: u16 = std::env::var("PORT") diff --git a/apps/fitness_trainers/src/main.rs b/apps/fitness_trainers/src/main.rs index 2c25805..901423f 100644 --- a/apps/fitness_trainers/src/main.rs +++ b/apps/fitness_trainers/src/main.rs @@ -4,7 +4,7 @@ mod handlers; mod admin; -use axum::{routing::get, Router}; +use axum::{extract::DefaultBodyLimit, routing::get, Router}; use std::net::SocketAddr; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; use contracts::ProfessionState; @@ -39,6 +39,7 @@ async fn main() { .nest("/api/fitness-trainers", handlers::router()) .nest("/api/admin/fitness-trainers", admin::router()) .route("/health", get(|| async { "Fitness Trainers OK" })) + .layer(DefaultBodyLimit::max(11 * 1024 * 1024)) .with_state(state); let port: u16 = std::env::var("PORT") diff --git a/apps/graphic_designers/src/main.rs b/apps/graphic_designers/src/main.rs index 04ae2cb..f0633dc 100644 --- a/apps/graphic_designers/src/main.rs +++ b/apps/graphic_designers/src/main.rs @@ -5,7 +5,7 @@ mod handlers; mod admin; -use axum::{routing::get, Router}; +use axum::{extract::DefaultBodyLimit, routing::get, Router}; use std::net::SocketAddr; use std::sync::Arc; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; @@ -42,6 +42,7 @@ async fn main() { .nest("/api/graphic-designers", handlers::router()) .nest("/api/admin/graphic-designers", admin::router()) .route("/health", get(|| async { "Graphic Designers OK" })) + .layer(DefaultBodyLimit::max(11 * 1024 * 1024)) .with_state(state); let port: u16 = std::env::var("PORT") diff --git a/apps/job_seekers/src/handlers.rs b/apps/job_seekers/src/handlers.rs index 4690d55..9b7bfe6 100644 --- a/apps/job_seekers/src/handlers.rs +++ b/apps/job_seekers/src/handlers.rs @@ -776,8 +776,8 @@ async fn upload_document( { Ok(url) => url, Err(e) => { - tracing::error!("B2 upload failed: {}", e); - return (StatusCode::INTERNAL_SERVER_ERROR, Json(serde_json::json!({ "error": "File upload failed" }))).into_response(); + tracing::error!("B2 upload failed: {:?}", e); + return (StatusCode::INTERNAL_SERVER_ERROR, Json(serde_json::json!({ "error": "storage_upload_failed", "detail": format!("{:?}", e) }))).into_response(); } }; diff --git a/apps/job_seekers/src/main.rs b/apps/job_seekers/src/main.rs index 88eb745..bbd752d 100644 --- a/apps/job_seekers/src/main.rs +++ b/apps/job_seekers/src/main.rs @@ -3,7 +3,7 @@ mod handlers; -use axum::{routing::get, Router}; +use axum::{extract::DefaultBodyLimit, routing::get, Router}; use cache::RedisPool; use std::net::SocketAddr; use std::sync::Arc; @@ -47,6 +47,7 @@ async fn main() { let app = Router::new() .nest("/api/jobseeker", handlers::router()) .route("/health", get(|| async { "Job Seekers OK" })) + .layer(DefaultBodyLimit::max(11 * 1024 * 1024)) .with_state(state); let port: u16 = std::env::var("PORT") diff --git a/apps/makeup_artists/src/main.rs b/apps/makeup_artists/src/main.rs index 922ad6f..b1b64e3 100644 --- a/apps/makeup_artists/src/main.rs +++ b/apps/makeup_artists/src/main.rs @@ -4,7 +4,7 @@ mod handlers; mod admin; -use axum::{routing::get, Router}; +use axum::{extract::DefaultBodyLimit, routing::get, Router}; use std::net::SocketAddr; use std::sync::Arc; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; @@ -41,6 +41,7 @@ async fn main() { .nest("/api/makeup-artists", handlers::router()) .nest("/api/admin/makeup-artists", admin::router()) .route("/health", get(|| async { "Makeup Artists OK" })) + .layer(DefaultBodyLimit::max(11 * 1024 * 1024)) .with_state(state); let port: u16 = std::env::var("PORT") diff --git a/apps/photographers/src/main.rs b/apps/photographers/src/main.rs index bbe610c..e112940 100644 --- a/apps/photographers/src/main.rs +++ b/apps/photographers/src/main.rs @@ -4,7 +4,7 @@ mod handlers; mod admin; -use axum::{routing::get, Router}; +use axum::{extract::DefaultBodyLimit, routing::get, Router}; use std::net::SocketAddr; use std::sync::Arc; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; @@ -41,6 +41,7 @@ async fn main() { .nest("/api/photographers", handlers::router()) .nest("/api/admin/photographers", admin::router()) .route("/health", get(|| async { "Photographers OK" })) + .layer(DefaultBodyLimit::max(11 * 1024 * 1024)) .with_state(state); let port: u16 = std::env::var("PORT") diff --git a/apps/social_media_managers/src/main.rs b/apps/social_media_managers/src/main.rs index f466c18..a834740 100644 --- a/apps/social_media_managers/src/main.rs +++ b/apps/social_media_managers/src/main.rs @@ -6,7 +6,7 @@ mod handlers; mod admin; -use axum::{routing::get, Router}; +use axum::{extract::DefaultBodyLimit, routing::get, Router}; use std::net::SocketAddr; use std::sync::Arc; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; @@ -43,6 +43,7 @@ async fn main() { .nest("/api/social-media-managers", handlers::router()) .nest("/api/admin/social-media-managers", admin::router()) .route("/health", get(|| async { "Social Media Managers OK" })) + .layer(DefaultBodyLimit::max(11 * 1024 * 1024)) .with_state(state); let port: u16 = std::env::var("PORT") diff --git a/apps/tutors/src/main.rs b/apps/tutors/src/main.rs index d46f9ef..a9ac976 100644 --- a/apps/tutors/src/main.rs +++ b/apps/tutors/src/main.rs @@ -5,7 +5,7 @@ mod handlers; mod admin; -use axum::{routing::get, Router}; +use axum::{extract::DefaultBodyLimit, routing::get, Router}; use std::net::SocketAddr; use std::sync::Arc; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; @@ -42,6 +42,7 @@ async fn main() { .nest("/api/tutors", handlers::router()) .nest("/api/admin/tutors", admin::router()) .route("/health", get(|| async { "Tutors OK" })) + .layer(DefaultBodyLimit::max(11 * 1024 * 1024)) .with_state(state); let port: u16 = std::env::var("PORT") diff --git a/apps/ugc_content_creators/src/main.rs b/apps/ugc_content_creators/src/main.rs index 43b9e93..23f960d 100644 --- a/apps/ugc_content_creators/src/main.rs +++ b/apps/ugc_content_creators/src/main.rs @@ -4,7 +4,7 @@ mod handlers; -use axum::{routing::get, Router}; +use axum::{extract::DefaultBodyLimit, routing::get, Router}; use std::net::SocketAddr; use std::sync::Arc; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; @@ -40,6 +40,7 @@ async fn main() { let app = Router::new() .nest("/api/ugc-content-creators", handlers::router()) .route("/health", get(|| async { "UGC Content Creators OK" })) + .layer(DefaultBodyLimit::max(11 * 1024 * 1024)) .with_state(state); let port: u16 = std::env::var("PORT") diff --git a/apps/video_editors/src/main.rs b/apps/video_editors/src/main.rs index 32d66b6..04ec1bf 100644 --- a/apps/video_editors/src/main.rs +++ b/apps/video_editors/src/main.rs @@ -4,7 +4,7 @@ mod handlers; mod admin; -use axum::{routing::get, Router}; +use axum::{extract::DefaultBodyLimit, routing::get, Router}; use std::net::SocketAddr; use std::sync::Arc; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; @@ -41,6 +41,7 @@ async fn main() { .nest("/api/video-editors", handlers::router()) .nest("/api/admin/video-editors", admin::router()) .route("/health", get(|| async { "Video Editors OK" })) + .layer(DefaultBodyLimit::max(11 * 1024 * 1024)) .with_state(state); let port: u16 = std::env::var("PORT") diff --git a/crates/contracts/src/profession_shared.rs b/crates/contracts/src/profession_shared.rs index 53a61a1..2598346 100644 --- a/crates/contracts/src/profession_shared.rs +++ b/crates/contracts/src/profession_shared.rs @@ -898,7 +898,7 @@ async fn upload_document( // the outermost ".context()" message — that's what actually names // the root cause (auth, DNS, timeout, etc.). tracing::error!("B2 upload failed: {:?}", e); - return (StatusCode::INTERNAL_SERVER_ERROR, Json(serde_json::json!({ "error": "File upload failed" }))).into_response(); + return (StatusCode::INTERNAL_SERVER_ERROR, Json(serde_json::json!({ "error": "storage_upload_failed", "detail": format!("{:?}", e) }))).into_response(); } };