From ec0913683690ac2cca30cb2dd73cd97e209b70da Mon Sep 17 00:00:00 2001 From: BillionClaw Date: Tue, 17 Mar 2026 16:04:20 +0800 Subject: [PATCH] fix(webdav): enforce storage quota on WebDAV PUT uploads Adds storage quota checking to the WebDAV PUT handler, which was missing while present in other upload handlers (regular upload and chunked upload). The quota check happens after the file is spooled to a temp file (so we know the exact size) but before it's moved to permanent storage. If the quota is exceeded, the temp file is cleaned up and a 507 Insufficient Storage error is returned. Fixes #104 --- src/interfaces/api/handlers/webdav_handler.rs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/interfaces/api/handlers/webdav_handler.rs b/src/interfaces/api/handlers/webdav_handler.rs index 544f2faf..7d23599c 100755 --- a/src/interfaces/api/handlers/webdav_handler.rs +++ b/src/interfaces/api/handlers/webdav_handler.rs @@ -23,6 +23,7 @@ use crate::application::dtos::folder_dto::FolderDto; use crate::application::ports::file_ports::FileRetrievalUseCase; use crate::application::ports::file_ports::{FileManagementUseCase, FileUploadUseCase}; use crate::application::ports::inbound::FolderUseCase; +use crate::application::ports::storage_ports::StorageUsagePort; use crate::application::services::file_retrieval_service::FileRetrievalService; use crate::application::services::folder_service::FolderService; use crate::common::di::AppState; @@ -920,6 +921,27 @@ async fn handle_put( let hash = hasher.finalize().to_hex().to_string(); + // ── Quota enforcement ──────────────────────────────────── + if let Some(storage_svc) = state.storage_usage_service.as_ref() { + if let Err(err) = storage_svc + .check_storage_quota(user.id, total_bytes as u64) + .await + { + let _ = tokio::fs::remove_file(&temp_path).await; + tracing::warn!( + "⛔ WEBDAV PUT REJECTED (quota): user={}, file={}, size={}", + user.id, + path, + total_bytes + ); + return Err(AppError::new( + StatusCode::INSUFFICIENT_STORAGE, + err.message, + "QuotaExceeded", + )); + } + } + // ── Atomic store: temp file → dedup blob + DB metadata update ── let result = file_upload_service .update_file_streaming(