-
Notifications
You must be signed in to change notification settings - Fork 56
Bugfix cmgt1i14v0000kqlieevxyedw #1116
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
b8c48f7
a8a8177
436e50d
50b8f42
ffdb45b
4822d77
d72a6d6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -548,6 +548,93 @@ export const handleAttachmentDeleteApi = async (c: Context) => { | |
} | ||
} | ||
|
||
export const handleAttachmentUploadCleanup = async (c: Context) => { | ||
const { sub } = c.get(JwtPayloadKey) | ||
const email = sub | ||
|
||
try { | ||
const { fileId } = await c.req.json() | ||
if (!fileId) { | ||
throw new HTTPException(400, { message: "File ID is required for cleanup" }) | ||
} | ||
|
||
loggerWithChild({ email }).info( | ||
`Cleaning up aborted upload for file ID: ${fileId}` | ||
) | ||
|
||
// Validate fileId to prevent path traversal | ||
if ( | ||
fileId.includes("..") || | ||
fileId.includes("/") || | ||
fileId.includes("\\") | ||
) { | ||
throw new HTTPException(400, { message: "Invalid file ID" }) | ||
} | ||
|
||
// Check if this is an image attachment that might have been partially written to disk | ||
const imageBaseDir = path.resolve( | ||
process.env.IMAGE_DIR || "downloads/xyne_images_db", | ||
) | ||
const imageDir = path.join(imageBaseDir, fileId) | ||
|
||
try { | ||
// Try to remove the directory if it exists (for image uploads) | ||
await fs.access(imageDir) | ||
await fs.rm(imageDir, { recursive: true, force: true }) | ||
loggerWithChild({ email }).info( | ||
`Cleaned up image directory for aborted upload: ${imageDir}` | ||
) | ||
} catch (error) { | ||
// Directory might not exist, which is fine for aborted uploads | ||
loggerWithChild({ email }).debug( | ||
`No image directory to cleanup for file ID: ${fileId}` | ||
) | ||
} | ||
|
||
// Try to delete from Vespa if the document was partially created | ||
try { | ||
const vespaIds = expandSheetIds(fileId) | ||
for (const vespaId of vespaIds) { | ||
// Check if document exists before trying to delete | ||
const doc = await GetDocument(fileSchema, vespaId) | ||
if (doc && doc.fields) { | ||
const fields = doc.fields as any | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using |
||
const permissions = Array.isArray(fields.permissions) ? fields.permissions as string[] : [] | ||
|
||
// Only delete if user has permissions | ||
if (permissions.includes(email)) { | ||
await DeleteDocument(vespaId, fileSchema) | ||
// Also try to delete any associated images | ||
await DeleteImages(vespaId) | ||
loggerWithChild({ email }).info( | ||
`Cleaned up Vespa document for aborted upload: ${vespaId}` | ||
) | ||
} | ||
} | ||
} | ||
} catch (error) { | ||
// Document might not exist in Vespa, which is fine for aborted uploads | ||
loggerWithChild({ email }).debug( | ||
`No Vespa document to cleanup for file ID: ${fileId}` | ||
) | ||
} | ||
|
||
return c.json({ | ||
success: true, | ||
message: "Cleanup completed for aborted upload" | ||
}) | ||
} catch (error) { | ||
if (error instanceof HTTPException) { | ||
throw error | ||
} | ||
loggerWithChild({ email }).error( | ||
error, | ||
"Error during attachment upload cleanup" | ||
) | ||
throw new HTTPException(500, { message: "Internal server error during cleanup" }) | ||
} | ||
} | ||
|
||
Comment on lines
+551
to
+637
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chainCleanup can’t find aborted uploads: wrong fileId is sent from the client The cleanup endpoint expects the fileId used on server (attf_${uuid}), but the client calls it with selectedFile.id (client-only). On aborted uploads, the server never returned its fileId, so this endpoint will often no-op and leave partial files/docs behind. Fix by aligning IDs:
Additionally:
To confirm the mismatch, run: 🏁 Script executed: #!/bin/bash
# 1) Locate generateFileId and see its format
rg -nP --type=ts '(export\s+)?function\s+generateFileId|\bgenerateFileId\s*\(' -C3
# 2) Check if frontend already appends any client id to the upload form
rg -nP --type=tsx -C3 'formData\.append\([^)]*attachment|upload-attachment'
# 3) Confirm server constructs its own attf_ fileId
rg -nP --type=ts -C3 'upload-attachment|attf_' server Length of output: 3353 🏁 Script executed: #!/bin/bash
# 1. Show handleAttachmentUpload definition and context
rg -nP 'export const handleAttachmentUpload' -C50 server/api/files.ts
# 2. Show JSON response calls within that handler
rg -nP 'c\.json\(' -C5 server/api/files.ts
# 3. Search for client-side fetch to /files/upload-attachment
rg -nP 'fetch\([^)]*files/upload-attachment' -C10 frontend/src Length of output: 9655 🏁 Script executed: #!/bin/bash
# 1. Find client call to cleanup endpoint
rg -n "upload-attachment/cleanup" -C5 frontend/src
# 2. Search for any “cleanup” references in attachment components
rg -n "cleanup" -C5 frontend/src
# 3. Locate generateFileId implementation and its prefix
rg -nP "function\s+generateFileId" -C5 frontend/src Length of output: 21777 Align cleanup ID with upload
|
||
/** | ||
* Serve attachment file by fileId | ||
*/ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Avoid double-decrementing uploadingFilesCount on abort
You decrement here and again in the upload promise’s finally, which can drive the counter negative and break “wait for uploads” logic.
Apply this diff:
📝 Committable suggestion
🤖 Prompt for AI Agents