From b5a94b5eeed53c07434d3d8401f7b1d3c8216e7c Mon Sep 17 00:00:00 2001 From: Leo Vasanko Date: Tue, 5 May 2026 02:13:18 +0000 Subject: [PATCH] Frontend upload batch space check with 512 MiB margin - Verify single queue: uploads append to upqueue and are processed sequentially by one worker (parallel only within a single file). - Before accepting a new batch, calculate total size minus existing files that will be overwritten. - Reject the whole batch if free space < net need + 512 MiB margin. - Show a toast with human-readable needed vs available space. --- frontend/src/components/UploadButton.vue | 25 +++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/UploadButton.vue b/frontend/src/components/UploadButton.vue index f898775..da968fc 100644 --- a/frontend/src/components/UploadButton.vue +++ b/frontend/src/components/UploadButton.vue @@ -11,7 +11,7 @@ import { Doc } from '@/repositories/Document' import { getDocuments } from '@/stores/documentStore' import { useMainStore } from '@/stores/main' -import { collator } from '@/utils' +import { collator, formatSize } from '@/utils' import { onMounted, onUnmounted, ref } from 'vue' import { useRouter } from 'vue-router' @@ -44,6 +44,7 @@ type InflightBlock = { } const UPLOAD_BLOCK_SIZE = 16 << 20 // 16 MiB +const UPLOAD_MARGIN_BYTES = 512 * 1024 * 1024 // 512 MiB function pasteHandler(event: ClipboardEvent) { const items = Array.from(event.clipboardData?.items ?? []) const infiles = [] as File[] @@ -116,6 +117,28 @@ const uploadCloudFiles = (files: CloudFile[]) => { } if (!files.length) return files.sort((a, b) => collator.compare(a.cloudName, b.cloudName)) + + // Space check: reject the whole batch if there isn't enough free space. + const batchTotal = files.reduce((sum, f) => sum + f.file.size, 0) + const allDocs = getDocuments() + const docByPath = new Map() + for (const d of allDocs) { + const path = d.loc ? `${d.loc}/${d.name}` : d.name + docByPath.set(path, d) + } + let overwriteSize = 0 + for (const f of files) { + const existing = docByPath.get(f.cloudName) + if (existing && !existing.dir) overwriteSize += existing.size + } + const netNeed = batchTotal - overwriteSize + if (store.space.free < netNeed + UPLOAD_MARGIN_BYTES) { + store.showToast( + `Not enough free space (need ${formatSize(netNeed + UPLOAD_MARGIN_BYTES)}, have ${formatSize(store.space.free)})` + ) + return + } + // Optimistic update: ghost folders and files const now = Math.floor(Date.now() / 1000) const docs = getDocuments()