diff --git a/mail-vue/src/components/email-scroll/index.vue b/mail-vue/src/components/email-scroll/index.vue index eb91acb..13fba5a 100644 --- a/mail-vue/src/components/email-scroll/index.vue +++ b/mail-vue/src/components/email-scroll/index.vue @@ -54,7 +54,7 @@ - + diff --git a/mail-vue/src/components/shadow-html/index.vue b/mail-vue/src/components/shadow-html/index.vue index 0652c1d..28f0347 100644 --- a/mail-vue/src/components/shadow-html/index.vue +++ b/mail-vue/src/components/shadow-html/index.vue @@ -67,7 +67,7 @@ function updateContent() { } img:not(table img) { - max-width: 100% !important; + max-width: 100%; height: auto !important; } diff --git a/mail-vue/src/components/tiny-editor/index.vue b/mail-vue/src/components/tiny-editor/index.vue index c82b33c..1783c3c 100644 --- a/mail-vue/src/components/tiny-editor/index.vue +++ b/mail-vue/src/components/tiny-editor/index.vue @@ -96,6 +96,8 @@ function initEditor() { statusbar: false, height: "100%", auto_focus: true, + relative_urls: false, //阻止 img标签域名和网站域名相同 自动把链接转换相对路径 + remove_script_host: false, // 阻止删除 URL 中的域名 forced_root_block: 'div', skin: `${uiStore.dark ? 'oxide-dark' : 'oxide'}`, content_css: `/tinymce/css/index.css,${uiStore.dark ? 'dark' : 'default'}`, diff --git a/mail-worker/package.json b/mail-worker/package.json index a7424da..0ff7612 100644 --- a/mail-worker/package.json +++ b/mail-worker/package.json @@ -22,7 +22,7 @@ "i18next": "^25.3.2", "linkedom": "^0.18.10", "postal-mime": "^2.4.3", - "resend": "^4.5.1", + "resend": "^6.4.1", "ua-parser-js": "^2.0.3", "uuid": "^11.1.0" } diff --git a/mail-worker/pnpm-lock.yaml b/mail-worker/pnpm-lock.yaml index 682452b..2720e88 100644 --- a/mail-worker/pnpm-lock.yaml +++ b/mail-worker/pnpm-lock.yaml @@ -13,7 +13,7 @@ importers: version: 3.882.0 '@cloudflare/vite-plugin': specifier: 1.6.0 - version: 1.6.0(rollup@4.50.0)(vite@6.3.5(@types/node@24.3.0))(workerd@1.20250310.0)(wrangler@4.33.1) + version: 1.6.0(rollup@4.50.0)(vite@6.3.5(@types/node@24.3.0))(workerd@1.20250823.0)(wrangler@4.33.1) dayjs: specifier: ^1.11.13 version: 1.11.18 @@ -33,8 +33,8 @@ importers: specifier: ^2.4.3 version: 2.4.4 resend: - specifier: ^4.5.1 - version: 4.8.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + specifier: ^6.4.1 + version: 6.4.1(@react-email/render@1.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)) ua-parser-js: specifier: ^2.0.3 version: 2.0.4 @@ -1416,12 +1416,18 @@ packages: '@speed-highlight/core@1.2.7': resolution: {integrity: sha512-0dxmVj4gxg3Jg879kvFS/msl4s9F3T9UXC1InxgOf7t5NvcPD97u/WTA5vL/IxWHMn7qSxBozqrnnE2wvl1m8g==} + '@stablelib/base64@1.0.1': + resolution: {integrity: sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==} + '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} '@types/node-fetch@2.6.13': resolution: {integrity: sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==} + '@types/node@22.19.0': + resolution: {integrity: sha512-xpr/lmLPQEj+TUnHmR+Ab91/glhJvsqcjB+yY0Ix9GO70H6Lb4FHH5GeqdOE5btAx7eIMwuHkp4H2MSkLcqWbA==} + '@types/node@24.3.0': resolution: {integrity: sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==} @@ -1726,6 +1732,9 @@ packages: resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} engines: {node: '>= 0.4'} + es6-promise@4.2.8: + resolution: {integrity: sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==} + esbuild@0.17.19: resolution: {integrity: sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==} engines: {node: '>=12'} @@ -1768,6 +1777,9 @@ packages: fast-deep-equal@2.0.1: resolution: {integrity: sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==} + fast-sha256@1.3.0: + resolution: {integrity: sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==} + fast-xml-parser@5.2.5: resolution: {integrity: sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==} hasBin: true @@ -1980,6 +1992,9 @@ packages: printable-characters@1.0.42: resolution: {integrity: sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==} + querystringify@2.2.0: + resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + react-dom@19.1.1: resolution: {integrity: sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==} peerDependencies: @@ -1992,9 +2007,17 @@ packages: resolution: {integrity: sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==} engines: {node: '>=0.10.0'} - resend@4.8.0: - resolution: {integrity: sha512-R8eBOFQDO6dzRTDmaMEdpqrkmgSjPpVXt4nGfWsZdYOet0kqra0xgbvTES6HmCriZEXbmGk3e0DiGIaLFTFSHA==} + requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + + resend@6.4.1: + resolution: {integrity: sha512-+P1gF6bYT0lKd0vsYyKKTHd6PfMH37yDgNRA6wAARQgQbYlc2BZYuo9sB/uZKkVg1oGSh0cgpnxNKGsZJutXMg==} engines: {node: '>=18'} + peerDependencies: + '@react-email/render': '*' + peerDependenciesMeta: + '@react-email/render': + optional: true rollup-plugin-inject@3.0.2: resolution: {integrity: sha512-ptg9PQwzs3orn4jkgXJ74bfs5vYz1NCZlSQMBUA0wKcGp5i5pA1AO3fOUEte8enhGUC+iapTCzEWw2jEFFUO/w==} @@ -2064,6 +2087,9 @@ packages: resolution: {integrity: sha512-5eG9FQjEjDbAlI5+kdpdyPIBMRH4GfTVDGREVupaZHmVoppknhM29b/S9BkQz7cathp85BVgRi/As3Siln7e0Q==} engines: {node: '>=18'} + svix@1.76.1: + resolution: {integrity: sha512-CRuDWBTgYfDnBLRaZdKp9VuoPcNUq9An14c/k+4YJ15Qc5Grvf66vp0jvTltd4t7OIRj+8lM1DAgvSgvf7hdLw==} + tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -2105,6 +2131,9 @@ packages: uhyphen@0.2.0: resolution: {integrity: sha512-qz3o9CHXmJJPGBdqzab7qAYuW8kQGKNEuoHFYrBwV6hWIMcpAmxDLXojcHfFr9US1Pe6zUswEIJIbLI610fuqA==} + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + undici-types@7.10.0: resolution: {integrity: sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==} @@ -2125,6 +2154,13 @@ packages: unenv@2.0.0-rc.19: resolution: {integrity: sha512-t/OMHBNAkknVCI7bVB9OWjUUAwhVv9vsPIAGnNUxnu3FxPQN11rjh0sksLMzc3g7IlTgvHmOTl4JM7JHpcv5wA==} + url-parse@1.5.10: + resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + + uuid@10.0.0: + resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} + hasBin: true + uuid@11.1.0: resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} hasBin: true @@ -2766,11 +2802,11 @@ snapshots: optionalDependencies: workerd: 1.20250310.0 - '@cloudflare/unenv-preset@2.3.2(unenv@2.0.0-rc.17)(workerd@1.20250310.0)': + '@cloudflare/unenv-preset@2.3.2(unenv@2.0.0-rc.17)(workerd@1.20250823.0)': dependencies: unenv: 2.0.0-rc.17 optionalDependencies: - workerd: 1.20250310.0 + workerd: 1.20250823.0 '@cloudflare/unenv-preset@2.7.0(unenv@2.0.0-rc.19)(workerd@1.20250823.0)': dependencies: @@ -2778,9 +2814,9 @@ snapshots: optionalDependencies: workerd: 1.20250823.0 - '@cloudflare/vite-plugin@1.6.0(rollup@4.50.0)(vite@6.3.5(@types/node@24.3.0))(workerd@1.20250310.0)(wrangler@4.33.1)': + '@cloudflare/vite-plugin@1.6.0(rollup@4.50.0)(vite@6.3.5(@types/node@24.3.0))(workerd@1.20250823.0)(wrangler@4.33.1)': dependencies: - '@cloudflare/unenv-preset': 2.3.2(unenv@2.0.0-rc.17)(workerd@1.20250310.0) + '@cloudflare/unenv-preset': 2.3.2(unenv@2.0.0-rc.17)(workerd@1.20250823.0) '@mjackson/node-fetch-server': 0.6.1 '@rollup/plugin-replace': 6.0.2(rollup@4.50.0) get-port: 7.1.0 @@ -3205,6 +3241,7 @@ snapshots: react: 19.1.1 react-dom: 19.1.1(react@19.1.1) react-promise-suspense: 0.3.4 + optional: true '@rollup/plugin-replace@6.0.2(rollup@4.50.0)': dependencies: @@ -3288,6 +3325,7 @@ snapshots: dependencies: domhandler: 5.0.3 selderee: 0.11.0 + optional: true '@sindresorhus/is@7.0.2': {} @@ -3782,6 +3820,8 @@ snapshots: '@speed-highlight/core@1.2.7': {} + '@stablelib/base64@1.0.1': {} + '@types/estree@1.0.8': {} '@types/node-fetch@2.6.13': @@ -3789,6 +3829,10 @@ snapshots: '@types/node': 24.3.0 form-data: 4.0.4 + '@types/node@22.19.0': + dependencies: + undici-types: 6.21.0 + '@types/node@24.3.0': dependencies: undici-types: 7.10.0 @@ -3926,7 +3970,8 @@ snapshots: deep-eql@5.0.2: {} - deepmerge@4.3.1: {} + deepmerge@4.3.1: + optional: true defu@6.1.4: {} @@ -3987,6 +4032,8 @@ snapshots: has-tostringtag: 1.0.2 hasown: 2.0.2 + es6-promise@4.2.8: {} + esbuild@0.17.19: optionalDependencies: '@esbuild/android-arm': 0.17.19 @@ -4085,7 +4132,10 @@ snapshots: exsolve@1.0.7: {} - fast-deep-equal@2.0.1: {} + fast-deep-equal@2.0.1: + optional: true + + fast-sha256@1.3.0: {} fast-xml-parser@5.2.5: dependencies: @@ -4158,6 +4208,7 @@ snapshots: dom-serializer: 2.0.0 htmlparser2: 8.0.2 selderee: 0.11.0 + optional: true htmlparser2@10.0.0: dependencies: @@ -4172,6 +4223,7 @@ snapshots: domhandler: 5.0.3 domutils: 3.2.2 entities: 4.5.0 + optional: true i18next@25.4.2: dependencies: @@ -4183,7 +4235,8 @@ snapshots: kleur@4.1.5: {} - leac@0.6.0: {} + leac@0.6.0: + optional: true linkedom@0.18.12: dependencies: @@ -4286,6 +4339,7 @@ snapshots: dependencies: leac: 0.6.0 peberminta: 0.9.0 + optional: true path-to-regexp@6.3.0: {} @@ -4293,7 +4347,8 @@ snapshots: pathval@2.0.1: {} - peberminta@0.9.0: {} + peberminta@0.9.0: + optional: true picocolors@1.1.1: {} @@ -4307,27 +4362,34 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - prettier@3.6.2: {} + prettier@3.6.2: + optional: true printable-characters@1.0.42: {} + querystringify@2.2.0: {} + react-dom@19.1.1(react@19.1.1): dependencies: react: 19.1.1 scheduler: 0.26.0 + optional: true react-promise-suspense@0.3.4: dependencies: fast-deep-equal: 2.0.1 + optional: true - react@19.1.1: {} + react@19.1.1: + optional: true - resend@4.8.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + requires-port@1.0.0: {} + + resend@6.4.1(@react-email/render@1.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1)): dependencies: + svix: 1.76.1 + optionalDependencies: '@react-email/render': 1.1.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - transitivePeerDependencies: - - react - - react-dom rollup-plugin-inject@3.0.2: dependencies: @@ -4370,11 +4432,13 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.50.0 fsevents: 2.3.3 - scheduler@0.26.0: {} + scheduler@0.26.0: + optional: true selderee@0.11.0: dependencies: parseley: 0.12.1 + optional: true semver@7.7.2: {} @@ -4431,6 +4495,15 @@ snapshots: supports-color@10.2.0: {} + svix@1.76.1: + dependencies: + '@stablelib/base64': 1.0.1 + '@types/node': 22.19.0 + es6-promise: 4.2.8 + fast-sha256: 1.3.0 + url-parse: 1.5.10 + uuid: 10.0.0 + tinybench@2.9.0: {} tinyexec@0.3.2: {} @@ -4466,6 +4539,8 @@ snapshots: uhyphen@0.2.0: {} + undici-types@6.21.0: {} + undici-types@7.10.0: {} undici@5.29.0: @@ -4498,6 +4573,13 @@ snapshots: pathe: 2.0.3 ufo: 1.6.1 + url-parse@1.5.10: + dependencies: + querystringify: 2.2.0 + requires-port: 1.0.0 + + uuid@10.0.0: {} + uuid@11.1.0: {} uuid@9.0.1: {} diff --git a/mail-worker/src/const/entity-const.js b/mail-worker/src/const/entity-const.js index 550ac79..aaf45c9 100644 --- a/mail-worker/src/const/entity-const.js +++ b/mail-worker/src/const/entity-const.js @@ -41,7 +41,8 @@ export const emailConst = { COMPLAINED: 4, DELAYED: 5, SAVING: 6, - NOONE: 7 + NOONE: 7, + FAILED: 8 } } diff --git a/mail-worker/src/service/att-service.js b/mail-worker/src/service/att-service.js index 26ce983..b06fbb0 100644 --- a/mail-worker/src/service/att-service.js +++ b/mail-worker/src/service/att-service.js @@ -1,13 +1,14 @@ import orm from '../entity/orm'; import { att } from '../entity/att'; -import { and, eq, isNull, inArray } from 'drizzle-orm'; +import { and, eq, isNull, inArray, desc } from 'drizzle-orm'; import r2Service from './r2-service'; import constant from '../const/constant'; import fileUtils from '../utils/file-utils'; import { attConst } from '../const/entity-const'; import { parseHTML } from 'linkedom'; +import { v4 as uuidv4 } from 'uuid'; import domainUtils from '../utils/domain-uitls'; -import BizError from '../error/biz-error'; +import settingService from "./setting-service"; const attService = { @@ -46,22 +47,27 @@ const attService = { ).all(); }, - async toImageUrlHtml(c, content, r2Domain) { + async toImageUrlHtml(c, content) { + + const { r2Domain } = await settingService.query(c); const { document } = parseHTML(content); const images = Array.from(document.querySelectorAll('img')); - const attDataList = []; + let imageDataList = []; for (const img of images) { + //邮件正文base64图片转cid附件 const src = img.getAttribute('src'); if (src && src.startsWith('data:image')) { const file = fileUtils.base64ToFile(src); const buff = await file.arrayBuffer(); + const cid = uuidv4().replace(/-/g, ''); const key = constant.ATTACHMENT_PREFIX + await fileUtils.getBuffHash(buff) + fileUtils.getExtFileName(file.name); - img.setAttribute('src', domainUtils.toOssDomain(r2Domain) + '/' + key); + + img.setAttribute('src', 'cid:' + cid); const attData = {}; attData.key = key; @@ -69,8 +75,24 @@ const attService = { attData.mimeType = file.type; attData.size = file.size; attData.buff = buff; + attData.content = fileUtils.base64ToDataStr(src); + attData.contentId = cid; - attDataList.push(attData); + imageDataList.push(attData); + } + + //邮件正文站内图片转cid附件 + if (src && src.startsWith(domainUtils.toOssDomain(r2Domain))) { + + const cid = uuidv4().replace(/-/g, '') + img.setAttribute('src', 'cid:' + cid); + + const attData = {}; + attData.key = src.replace(domainUtils.toOssDomain(r2Domain) + '/',''); + attData.path = src; + attData.contentId = cid; + attData.type = attConst.type.EMBED; + imageDataList.push(attData); } const hasInlineWidth = img.hasAttribute('width'); @@ -82,7 +104,26 @@ const attService = { img.setAttribute('style', newStyle); } } - return { attDataList, html: document.toString() }; + + //查询已有内嵌url图片信息 + const keys = [...new Set(imageDataList.filter(item => item.path).map(item => item.key))]; + const dbImageList = await this.selectOneByKeys(c, keys); + + //设置给当前附件 + imageDataList.forEach(image => { + dbImageList.forEach(dbImage => { + if (image.path && (image.key === dbImage.key)) { + image.size = dbImage.size; + image.filename = dbImage.filename; + image.mimeType = dbImage.mimeType; + image.contentType = dbImage.mimeType; + } + }) + }) + + imageDataList = imageDataList.filter(image => !image.path || image.size); + + return { imageDataList, html: document.toString() }; }, async saveSendAtt(c, attList, userId, accountId, emailId) { @@ -194,8 +235,14 @@ const attService = { }, async removeByAccountId(c, accountId) { - console.log(accountId) await this.removeAttByField(c, "account_id", [accountId]) + }, + + selectOneByKeys(c, keys) { + if (!keys || keys.length === 0) { + return [] + } + return orm(c).select().from(att).where(inArray(att.key, keys)).orderBy(desc(att.attId)).groupBy(att.key).all(); } }; diff --git a/mail-worker/src/service/email-service.js b/mail-worker/src/service/email-service.js index 1f09468..4976393 100644 --- a/mail-worker/src/service/email-service.js +++ b/mail-worker/src/service/email-service.js @@ -145,7 +145,7 @@ const emailService = { const { resendTokens, r2Domain, send } = await settingService.query(c); - let { attDataList, html } = await attService.toImageUrlHtml(c, content, r2Domain); + let { imageDataList, html } = await attService.toImageUrlHtml(c, content); if (send === settingConst.send.CLOSE) { throw new BizError(t('disabledSend'), 403); @@ -173,11 +173,11 @@ const emailService = { } - if (attDataList.length > 0 && !r2Domain) { + if (imageDataList.length > 0 && !r2Domain) { throw new BizError(t('noOsDomainSendPic')); } - if (attDataList.length > 0 && !await r2Service.hasOSS(c)) { + if (imageDataList.length > 0 && !await r2Service.hasOSS(c)) { throw new BizError(t('noOsSendPic')); } @@ -242,6 +242,7 @@ const emailService = { const resend = new Resend(resendToken); + //如果是分开发送 if (manyType === 'divide') { let sendFormList = []; @@ -275,7 +276,7 @@ const emailService = { subject: subject, text: text, html: html, - attachments: attachments + attachments: [...imageDataList, ...attachments] }; if (sendType === 'reply') { @@ -296,7 +297,10 @@ const emailService = { throw new BizError(error.message); } - html = this.imgReplace(html, null, r2Domain); + imageDataList = imageDataList.map(item => ({...item, contentId: `<${item.contentId}>`})) + + //把图片标签cid标签切换会通用url + html = this.imgReplace(html, imageDataList, r2Domain); const emailData = {}; emailData.sendEmail = accountRow.email; @@ -348,11 +352,12 @@ const emailService = { } const emailRowList = await Promise.all( + emailDataList.map(async (emailData) => { const emailRow = await orm(c).insert(email).values(emailData).returning().get(); - if (attDataList.length > 0) { - await attService.saveArticleAtt(c, attDataList, userId, accountId, emailRow.emailId); + if (imageDataList.length > 0) { + await attService.saveArticleAtt(c, imageDataList, userId, accountId, emailRow.emailId); } if (attachments?.length > 0 && await r2Service.hasOSS(c)) { diff --git a/mail-worker/src/service/resend-service.js b/mail-worker/src/service/resend-service.js index 37c10c0..f29f28e 100644 --- a/mail-worker/src/service/resend-service.js +++ b/mail-worker/src/service/resend-service.js @@ -34,6 +34,12 @@ const resendService = { params.message = null } + if (body.type === 'email.failed') { + params.status = emailConst.status.FAILED + params.resendEmailId = body.data.email_id + params.message = body.data.failed.reason + } + const emailRow = await emailService.updateEmailStatus(c, params) if (!emailRow) { diff --git a/mail-worker/src/utils/file-utils.js b/mail-worker/src/utils/file-utils.js index 413173c..9b70bec 100644 --- a/mail-worker/src/utils/file-utils.js +++ b/mail-worker/src/utils/file-utils.js @@ -14,6 +14,10 @@ const fileUtils = { return hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); }, + base64ToDataStr(base64) { + return base64.split(',')[1] || base64; + }, + base64ToUint8Array(base64) { const binaryStr = atob(base64); const len = binaryStr.length;