From 07c364c776ba62de00da244f1b8a1de3e4e0d136 Mon Sep 17 00:00:00 2001 From: Artyom Savchenko Date: Wed, 4 Jun 2025 14:37:59 +0700 Subject: [PATCH] UBERF-11423: Fix attachments in emails (#9166) Signed-off-by: Artem Savchenko --- common/config/rush/pnpm-lock.yaml | 3 ++- services/mail/mail-common/package.json | 3 ++- services/mail/mail-common/src/message.ts | 14 ++++++++------ services/mail/mail-common/src/utils.ts | 23 +++++++++++++++++++++-- 4 files changed, 33 insertions(+), 10 deletions(-) diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 928c6590ee..e41c05fd4a 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -4381,7 +4381,7 @@ packages: version: 0.0.0 '@rush-temp/mail-common@file:projects/mail-common.tgz': - resolution: {integrity: sha512-3mF2CTdNdTUi6sq+KOG6Tz1Jknrfp8+WggKdc/ZSgy3u5ASp8PcXTad3ihjXD0gRatFfrvNI+m3A90gB2zJMNg==, tarball: file:projects/mail-common.tgz} + resolution: {integrity: sha512-sdNydMgE2+6RUZLuoxs9vR5TuVhx0ZtgzXtR7GW2AqlG3O31iBo/WyST90ar4jftmjASKrJ4bSObnkuX7dnHyA==, tarball: file:projects/mail-common.tgz} version: 0.0.0 '@rush-temp/mail@file:projects/mail.tgz': @@ -20010,6 +20010,7 @@ snapshots: eslint-plugin-n: 15.7.0(eslint@8.56.0) eslint-plugin-node: 11.1.0(eslint@8.56.0) eslint-plugin-promise: 6.1.1(eslint@8.56.0) + image-size: 1.1.1 jest: 29.7.0(@types/node@22.15.29)(ts-node@10.9.2(@types/node@22.15.29)(typescript@5.3.3)) kafkajs: 2.2.4 prettier: 3.2.5 diff --git a/services/mail/mail-common/package.json b/services/mail/mail-common/package.json index adf5f45799..35266a9a0c 100644 --- a/services/mail/mail-common/package.json +++ b/services/mail/mail-common/package.json @@ -63,6 +63,7 @@ "kafkajs": "^2.2.4", "sanitize-html": "^2.15.0", "turndown": "^7.2.0", - "uuid": "^8.3.2" + "uuid": "^8.3.2", + "image-size": "^1.1.1" } } diff --git a/services/mail/mail-common/src/message.ts b/services/mail/mail-common/src/message.ts index d98921af92..1ef5e5529b 100644 --- a/services/mail/mail-common/src/message.ts +++ b/services/mail/mail-common/src/message.ts @@ -44,7 +44,7 @@ import { generateMessageId } from '@hcengineering/communication-shared' import { BaseConfig, type Attachment } from './types' import { EmailMessage, MailRecipient, MessageData } from './types' -import { getMdContent } from './utils' +import { getBlobMetadata, getMdContent } from './utils' import { PersonCacheFactory } from './person' import { PersonSpacesCacheFactory } from './personSpaces' import { ChannelCache, ChannelCacheFactory } from './channel' @@ -252,11 +252,11 @@ async function saveMessageToSpaces ( } const messageId = await createMailMessage(producer, config, messageData, threadId) + await createFiles(ctx, producer, config, attachments, messageData, threadId, messageId) if (!isReply) { await addCollaborators(producer, config, messageData, threadId) await createMailThread(producer, config, messageData, messageId) } - await createFiles(producer, config, attachments, messageData, threadId, messageId) await threadLookup.setThreadId(mailId, space._id, threadId) }) @@ -305,6 +305,7 @@ async function createMailMessage ( } async function createFiles ( + ctx: MeasureContext, producer: Producer, config: BaseConfig, attachments: Attachment[], @@ -313,9 +314,9 @@ async function createFiles ( messageId: MessageID ): Promise { const fileData: Buffer[] = attachments.map((a) => { - const creeateFileEvent: CreateFileEvent = { + const createFileEvent: CreateFileEvent = { type: MessageRequestEventType.CreateFile, - card: threadId, + card: messageData.isReply ? threadId : messageData.channel, message: messageId, messageCreated: messageData.created, creator: messageData.modifiedBy, @@ -323,10 +324,11 @@ async function createFiles ( blobId: a.id as Ref, type: a.contentType, filename: a.name, - size: a.data.length + size: a.data.length, + meta: getBlobMetadata(ctx, a) } } - return Buffer.from(JSON.stringify(creeateFileEvent)) + return Buffer.from(JSON.stringify(createFileEvent)) }) const fileEvents = fileData.map((data) => ({ key: Buffer.from(messageData.channel ?? messageData.spaceId), diff --git a/services/mail/mail-common/src/utils.ts b/services/mail/mail-common/src/utils.ts index d707da101c..16914b60f7 100644 --- a/services/mail/mail-common/src/utils.ts +++ b/services/mail/mail-common/src/utils.ts @@ -14,9 +14,10 @@ // import TurndownService from 'turndown' import sanitizeHtml from 'sanitize-html' +import { imageSize } from 'image-size' -import { MeasureContext } from '@hcengineering/core' -import { EmailContact, EmailMessage } from './types' +import { BlobMetadata, MeasureContext } from '@hcengineering/core' +import { Attachment, EmailContact, EmailMessage } from './types' const NAME_EMAIL_PATTERN = /^(?:"?([^"<]+)"?\s*)?<([^>]+)>$/ const NAME_SEGMENT_REGEX = /[\s,;]+/ @@ -131,3 +132,21 @@ export function parseNameFromEmailHeader (headerValue: string | undefined): Emai export function normalizeEmail (email: string): string { return email.toLowerCase().trim() } + +export function getBlobMetadata (ctx: MeasureContext, attachment: Attachment): BlobMetadata | undefined { + try { + const dimensions = imageSize(attachment.data) + return dimensions != null + ? { + originalHeight: dimensions.height, + originalWidth: dimensions.width + } + : undefined + } catch (error: any) { + ctx.warn('Failed to get blob metadata', { + error: error.message, + attachmentId: attachment.id + }) + return undefined + } +}