Skip to content

Commit 132fda9

Browse files
committed
fix: enhance MIME type handling in AttachmentService and improve download URL persistence in useMessageSender
1 parent 9d51b35 commit 132fda9

File tree

2 files changed

+30
-9
lines changed

2 files changed

+30
-9
lines changed

packages/node/src/services/attachment-service.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,19 @@ const SystemClock: AttachmentServiceClock = {
2727

2828
const INIT_UPLOAD_TTL_SEC = 900;
2929

30+
const MIME_BY_EXT: Record<string, string> = {
31+
png: 'image/png',
32+
jpg: 'image/jpeg',
33+
jpeg: 'image/jpeg',
34+
gif: 'image/gif',
35+
webp: 'image/webp',
36+
svg: 'image/svg+xml',
37+
avif: 'image/avif',
38+
pdf: 'application/pdf',
39+
mp4: 'video/mp4',
40+
webm: 'video/webm',
41+
};
42+
3043
export class AttachmentService {
3144
private readonly uploads = new Map<string, PendingUpload>();
3245
private readonly clock: AttachmentServiceClock;
@@ -60,9 +73,17 @@ export class AttachmentService {
6073
}
6174
}
6275

63-
/** Get the MIME content-type for a stored upload. */
76+
/** Get the MIME content-type for a stored upload.
77+
* Falls back to deriving the type from the file extension so the correct
78+
* Content-Type is returned even after a server restart (when the in-memory
79+
* uploads map has been cleared).
80+
*/
6481
getContentType(objectKey: string): string {
65-
return this.uploads.get(objectKey)?.contentType ?? 'application/octet-stream';
82+
const stored = this.uploads.get(objectKey)?.contentType;
83+
if (stored && stored !== 'application/octet-stream') return stored;
84+
// Derive from extension — objectKey preserves the original filename suffix.
85+
const ext = objectKey.split('.').pop()?.toLowerCase() ?? '';
86+
return MIME_BY_EXT[ext] ?? stored ?? 'application/octet-stream';
6687
}
6788

6889
initUpload(input: {

packages/webapp/src/hooks/use-message-sender.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -257,14 +257,14 @@ export function useMessageSender() {
257257
fileContentType: input.file.type || "application/octet-stream",
258258
})
259259

260-
// downloadUrl is the node-served URL; send it as the payload so the
261-
// receiver can display the image directly from the sender's node.
260+
// downloadUrl is the node-served URL. Use it as displayText so the URL
261+
// is persistent after page reload (blob: URLs die on navigation). By this
262+
// point the file is already saved on the server (saved inline in
263+
// completeAttachmentUpload above), so the sender can fetch it immediately.
262264
const downloadUrl = (completed as { downloadUrl?: string }).downloadUrl
263-
?? `${initialized.uploadUrl.replace(/\/[^/]+$/, '')}` // fallback: strip objectKey from uploadUrl
264-
const displayText = contentType === "image"
265-
? URL.createObjectURL(input.file) // local blob for sender's own preview
266-
: input.file.name
267-
const rawPayloadText = downloadUrl // what the receiver actually uses
265+
?? `${initialized.uploadUrl.replace(/\/[^/]+$/, '')}` // fallback
266+
const displayText = contentType === "image" ? downloadUrl : input.file.name
267+
const rawPayloadText = downloadUrl
268268

269269
return sendEnvelope({
270270
conversationId: conversation.conversationId,

0 commit comments

Comments
 (0)