fix(platform): human-readable storage quota error messages

Backend: Replace raw byte counts ("262,144,000 bytes") with formatted
sizes ("250 MB of your 1 GB quota") and include upgrade guidance.

Frontend: Parse 413 JSON response in direct-upload.ts to extract the
human-readable detail message instead of dumping raw JSON to the user.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nicholas Tindle
2026-04-23 15:23:24 -05:00
parent 64b7560291
commit f3b4b0e1d9
3 changed files with 35 additions and 10 deletions

View File

@@ -273,13 +273,16 @@ async def upload_file(
f"Failed to soft-delete over-quota file {workspace_file.id} "
f"in workspace {workspace.id}: {e}"
)
from backend.util.workspace import _format_bytes
raise fastapi.HTTPException(
status_code=413,
detail={
"message": "Storage limit exceeded (concurrent upload)",
"used_bytes": new_total,
"limit_bytes": storage_limit_bytes,
},
detail=(
f"Storage limit exceeded. "
f"You've used {_format_bytes(new_total)} of your "
f"{_format_bytes(storage_limit_bytes)} quota. "
f"Delete some files or upgrade your plan for more storage."
),
)
return UploadFileResponse(

View File

@@ -20,6 +20,17 @@ from backend.util.settings import Config
from backend.util.virus_scanner import scan_content_safe
from backend.util.workspace_storage import compute_file_checksum, get_workspace_storage
def _format_bytes(n: int) -> str:
"""Format bytes as a human-readable string (e.g. 250 MB, 1.0 GB)."""
if n < 1024:
return f"{n} B"
if n < 1024 * 1024:
return f"{n / 1024:.0f} KB"
if n < 1024 * 1024 * 1024:
return f"{n / (1024 * 1024):.0f} MB"
return f"{n / (1024 * 1024 * 1024):.1f} GB"
logger = logging.getLogger(__name__)
@@ -211,10 +222,11 @@ class WorkspaceManager:
projected_usage = current_usage + len(content)
if storage_limit > 0 and projected_usage > storage_limit:
used_pct = (current_usage / storage_limit) * 100
raise ValueError(
f"Storage limit exceeded: {current_usage:,} bytes used "
f"of {storage_limit:,} bytes ({used_pct:.1f}%)"
f"Storage limit exceeded. "
f"You've used {_format_bytes(current_usage)} of your "
f"{_format_bytes(storage_limit)} quota. "
f"Delete some files or upgrade your plan for more storage."
)
if storage_limit > 0 and projected_usage / storage_limit >= 0.8:
logger.warning(

View File

@@ -40,8 +40,18 @@ export async function uploadFileDirect(
});
if (!res.ok) {
const detail = await res.text().catch(() => res.statusText);
throw new Error(`Upload failed (${res.status}): ${detail}`);
let message: string;
try {
const body = await res.json();
// Backend returns { detail: "..." } or { detail: { message: "..." } }
message =
typeof body.detail === "string"
? body.detail
: (body.detail?.message ?? res.statusText);
} catch {
message = res.statusText;
}
throw new Error(message);
}
return res.json();