fix: prevent duplicate uploads with replace-or-cancel confirmation (#20)#400
Open
Padmajakachare1911 wants to merge 2 commits into
Open
fix: prevent duplicate uploads with replace-or-cancel confirmation (#20)#400Padmajakachare1911 wants to merge 2 commits into
Padmajakachare1911 wants to merge 2 commits into
Conversation
param20h
previously approved these changes
Jun 6, 2026
Owner
|
Merge COnflicts @Padmajakachare1911 |
param20h
approved these changes
Jun 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🔗 Related Issue
Closes #20
📋 Summary
Uploading a PDF with the same filename as an existing document silently
created a duplicate DB row and left orphaned vectors in ChromaDB. This PR
intercepts the duplicate at the upload route, returns a structured
409,and gives the user a modal to either cancel or fully replace the existing
document (file on disk + ChromaDB vectors + DB row reset + ingest
re-queued) - all in one atomic flow.
🛠️ Changes Made
backend/app/routes/documents.pyDocument()creation — queries for anexisting row matching
original_namescoped touser_id409 Conflictwith a structured body:{ "conflict": true, "existing_id": 42, "original_name": "report.pdf" }PUT /{doc_id}/replaceendpoint that:423 Lockedwhendocument.status == "processing"withmessage: "This document is still being processed. Please wait
before replacing it."
(
collection.delete(where={"document_id": doc_id}))status → "pending", updatesfilename,file_size,created_at(no schema changes)a fresh upload
user_id— two different usersuploading the same filename is not a conflict
frontend/src/components/document/ConfirmReplaceModal.tsx(new)lucide-react(no new dependencies)
chat history
423without closing the modalEscapekeydownfrontend/src/components/document/DocumentUpload.tsx409response before the normal success handler — storesexistingId,filename, andformDatainconflictMetastate,bails early without adding a ghost card to the document list
handleReplacecallsPUT /api/v1/documents/{existingId}/replacewith the same
FormData423setsreplaceErrorinside the open modal (does not close it)refreshDocuments()'inConfirmReplaceModal.tsx)frontend/e2e/duplicate-upload.spec.ts(new)backend/tests/test_document_upload_validation.py(updated)423guard✅ Test Results
Backend run with
PYTEST_DISABLE_PLUGIN_AUTOLOAD=1due to a globalweb3pytest plugin conflict on the dev machine (unrelated to this PR).Duplicate upload (409)
test_upload_document_returns_409_for_duplicate_original_nametest_upload_returns_409_when_original_name_already_exists(HTTP API)Replace flow
test_replace_document_resets_row_and_requeues_ingesttest_replace_document_returns_423_while_indexingFull suite
108 passed — entire
backend/tests/Playwright UI (390px + desktop)
409)PUT .../replace→ modal closes423while processing → error stays in modal7/7 passed in
test_document_upload_validation.py🔒 Edge Cases Covered
user_idstatus == "processing")423returned, modal shows error, stays opentry/except— vector failure does not block DB reset or file replacereturnon409prevents card ever appearing in list🖥️ Desktop / Mobile Regression
Zero. All changes are contained to the upload flow. No layout, sidebar,
or chat components were touched.
📦 Dependencies Added
None — uses Tailwind CSS and
lucide-reactalready present in theproject.
🧪 How to Test Locally
📝 Notes
PYTEST_DISABLE_PLUGIN_AUTOLOAD=1used in CI command to avoid apre-existing global
web3plugin conflict unrelated to this PR.All 108 tests pass cleanly with this flag.
"processing"(not"indexing") to matchthe actual values set by the ingest pipeline in this codebase.