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 || '邮件发送失败') + } } }