From 03fd87df13bbff8e30360e5a0ed08b40fefb993d Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Sat, 8 Aug 2026 13:43:35 +0800 Subject: [PATCH] =?UTF-8?q?fix(mail):=20=E5=8F=91=E4=BF=A1=E6=94=B9?= =?UTF-8?q?=E7=94=A8=20JMAP=20bodyValues=20=E6=A0=87=E5=87=86=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F=E5=B9=B6=E6=A3=80=E6=9F=A5=E6=8A=95=E9=80=92=E7=BB=93?= =?UTF-8?q?=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stalwart v0.11 不允许 textBody/htmlBody 内联 value(invalidProperties Cannot set property),改用 partId+bodyValues;补 EmailSubmission 响应检查避免误报成功 --- src/utils/jmap.ts | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/utils/jmap.ts b/src/utils/jmap.ts index 0bbbb6d..f31da0a 100644 --- a/src/utils/jmap.ts +++ b/src/utils/jmap.ts @@ -159,8 +159,16 @@ export class JmapClient { to: opts.to.map((t) => ({ name: t.name || '', email: t.email })), subject: opts.subject, } - if (opts.htmlBody) emailData.htmlBody = [{ type: 'text/html', value: opts.htmlBody }] - if (opts.textBody) emailData.textBody = [{ type: 'text/plain', value: opts.textBody }] + // 正文必须用标准 JMAP bodyValues + partId 引用方式: + // Stalwart v0.11 不允许在 textBody/htmlBody 数组里内联 value(报 invalidProperties "Cannot set property.") + if (opts.htmlBody) { + emailData.htmlBody = [{ partId: '2', type: 'text/html' }] + emailData.bodyValues = { '2': { value: opts.htmlBody } } + } + if (opts.textBody) { + emailData.textBody = [{ partId: '1', type: 'text/plain' }] + emailData.bodyValues = { ...(emailData.bodyValues as Record), '1': { value: opts.textBody } } + } const result = await this.request([ ['Email/set', { accountId: this.accountId, create: { d1: emailData } }, 'c1'], @@ -186,5 +194,13 @@ export class JmapClient { const firstErr = Object.values(err.notCreated || {})[0] as { type?: string; description?: string } | undefined throw new Error(firstErr?.description || '创建邮件失败') } + + // 检查 EmailSubmission/set 结果(methodResponses[1]):Email/set 成功不代表已投递 + const [, submitted] = result.methodResponses[1] || [] + const submittedData = submitted as unknown as { created?: Record; notCreated?: Record } + if (!submittedData?.created) { + const subErr = Object.values(submittedData?.notCreated || {})[0] as { type?: string; description?: string } | undefined + throw new Error(subErr?.description || '邮件发送失败') + } } }