fix(#104): enforce storage quota on uploads & fix usage tracking
Backend: - Add QuotaExceeded error kind mapped to HTTP 507 Insufficient Storage - Add check_storage_quota() and get_user_storage_info() to StorageUsagePort - Enforce quota in upload_file_with_cache, upload_file_with_thumbnails (AuthUser extractor) - Enforce quota in chunked upload create_upload handler - Add update_user_storage_usage_by_username() for username-based lookup - Fix extract_username_from_path() to handle subfolders (take first segment only) - Fix maybe_update_storage_usage() to use username-based lookup instead of passing username to get_user_by_id (which always failed silently) Frontend: - Parse and display quota error messages on upload failure (507 / QuotaExceeded) - Stop remaining uploads when quota is exceeded - Call refreshUserData() after uploads to update storage usage display - Bump service worker cache to v9
This commit is contained in:
@@ -20,6 +20,7 @@ use std::sync::Arc;
|
||||
use crate::application::ports::chunked_upload_ports::DEFAULT_CHUNK_SIZE;
|
||||
use crate::common::di::AppState;
|
||||
use crate::domain::errors::ErrorKind;
|
||||
use crate::interfaces::middleware::auth::AuthUser;
|
||||
|
||||
/// Request body for creating an upload session
|
||||
#[derive(Debug, Deserialize)]
|
||||
@@ -75,6 +76,7 @@ impl ChunkedUploadHandler {
|
||||
/// ```
|
||||
pub async fn create_upload(
|
||||
State(state): State<Arc<AppState>>,
|
||||
auth_user: AuthUser,
|
||||
Json(request): Json<CreateUploadRequest>,
|
||||
) -> impl IntoResponse {
|
||||
let chunked_service = &state.core.chunked_upload_service;
|
||||
@@ -100,6 +102,30 @@ impl ChunkedUploadHandler {
|
||||
.into_response();
|
||||
}
|
||||
|
||||
// ── Quota enforcement ────────────────────────────────────
|
||||
if let Some(storage_svc) = state.storage_usage_service.as_ref() {
|
||||
if let Err(err) = storage_svc
|
||||
.check_storage_quota(&auth_user.id, request.total_size)
|
||||
.await
|
||||
{
|
||||
tracing::warn!(
|
||||
"⛔ CHUNKED UPLOAD REJECTED (quota): user={}, file={}, size={} — {}",
|
||||
auth_user.username,
|
||||
request.filename,
|
||||
request.total_size,
|
||||
err.message
|
||||
);
|
||||
return (
|
||||
StatusCode::INSUFFICIENT_STORAGE,
|
||||
Json(serde_json::json!({
|
||||
"error": err.message,
|
||||
"error_type": "QuotaExceeded"
|
||||
})),
|
||||
)
|
||||
.into_response();
|
||||
}
|
||||
}
|
||||
|
||||
// Validate chunk size if provided
|
||||
let chunk_size = request.chunk_size.unwrap_or(DEFAULT_CHUNK_SIZE);
|
||||
if chunk_size < 1024 * 1024 {
|
||||
|
||||
Reference in New Issue
Block a user