fix: Bug - Sending files between users doesn't really work
Stream oversized generic attachments to disk instead of silently dropping chunks, avoid loading completed file downloads into renderer memory, and surface a clear error when the browser client cannot receive a file. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
getWatchedAttachmentRoomIdFromUrl,
|
||||
isDirectMessageAttachmentRoomId,
|
||||
shouldCopyUploaderMediaToAppData
|
||||
shouldCopyUploaderMediaToAppData,
|
||||
shouldStreamAttachmentReceiveToDisk,
|
||||
canReceiveAttachment
|
||||
} from './attachment.logic';
|
||||
|
||||
describe('attachment logic', () => {
|
||||
@@ -44,4 +46,36 @@ describe('attachment logic', () => {
|
||||
mime: 'video/mp4'
|
||||
}, undefined, true)).toBe(false);
|
||||
});
|
||||
|
||||
it('streams oversized generic files to disk when the store supports it', () => {
|
||||
const capabilities = {
|
||||
canStreamToDisk: true,
|
||||
canPersistSize: (bytes: number) => bytes <= 256 * 1024 * 1024
|
||||
};
|
||||
|
||||
expect(shouldStreamAttachmentReceiveToDisk({
|
||||
size: 200 * 1024 * 1024,
|
||||
mime: 'application/zip',
|
||||
filePath: undefined
|
||||
}, capabilities)).toBe(true);
|
||||
});
|
||||
|
||||
it('receives browser-sized files in memory when disk streaming is unavailable', () => {
|
||||
const browserCapabilities = {
|
||||
canStreamToDisk: false,
|
||||
canPersistSize: (bytes: number) => bytes <= 50 * 1024 * 1024
|
||||
};
|
||||
|
||||
expect(canReceiveAttachment({
|
||||
size: 20 * 1024 * 1024,
|
||||
mime: 'application/zip',
|
||||
filePath: undefined
|
||||
}, browserCapabilities)).toBe(true);
|
||||
|
||||
expect(canReceiveAttachment({
|
||||
size: 200 * 1024 * 1024,
|
||||
mime: 'application/zip',
|
||||
filePath: undefined
|
||||
}, browserCapabilities)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -50,6 +50,49 @@ export function isDirectMessageAttachmentRoomId(roomId: string | null | undefine
|
||||
return !!roomId && roomId.startsWith(DIRECT_MESSAGE_ATTACHMENT_STORAGE_PREFIX);
|
||||
}
|
||||
|
||||
export interface AttachmentReceiveCapabilities {
|
||||
canStreamToDisk: boolean;
|
||||
canPersistSize: (bytes: number) => boolean;
|
||||
}
|
||||
|
||||
export function shouldStreamAttachmentReceiveToDisk(
|
||||
attachment: Pick<Attachment, 'size' | 'mime' | 'filePath'>,
|
||||
capabilities: AttachmentReceiveCapabilities
|
||||
): boolean {
|
||||
if (attachment.filePath?.trim()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!capabilities.canStreamToDisk || !capabilities.canPersistSize(attachment.size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (attachment.size > MAX_AUTO_SAVE_SIZE_BYTES) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return isAttachmentMedia(attachment);
|
||||
}
|
||||
|
||||
export function canReceiveAttachmentInMemory(
|
||||
attachment: Pick<Attachment, 'size'>,
|
||||
capabilities: AttachmentReceiveCapabilities
|
||||
): boolean {
|
||||
if (attachment.size <= MAX_AUTO_SAVE_SIZE_BYTES) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !capabilities.canStreamToDisk && capabilities.canPersistSize(attachment.size);
|
||||
}
|
||||
|
||||
export function canReceiveAttachment(
|
||||
attachment: Pick<Attachment, 'size' | 'mime' | 'filePath'>,
|
||||
capabilities: AttachmentReceiveCapabilities
|
||||
): boolean {
|
||||
return shouldStreamAttachmentReceiveToDisk(attachment, capabilities)
|
||||
|| canReceiveAttachmentInMemory(attachment, capabilities);
|
||||
}
|
||||
|
||||
function decodeUrlSegment(value: string): string {
|
||||
try {
|
||||
return decodeURIComponent(value);
|
||||
|
||||
Reference in New Issue
Block a user