Skip to content

plugin-email/platform-objects: sys_email 加 headers_json + 有界 attachments_json —— 小附件与自定义 header 取得队列投递资格(#5172 一期) #5177

Description

@os-zhuang

Blocked-by: #5161(同包串行:#5160 → #5161 → 本单;#5161 在 email-plugin.ts,本单主体在 email-service.ts + platform-objects,文件面基本不相交,但同包不同批)

维护者两次拍板(2026-08-04,会话内):

  1. plugin-email: sys_email 无附件/自定义 header 列,带附件的邮件拿不到队列投递的持久化保证(#5160 落地后) #5172 按「headers 先行、附件走 storage 引用」拆分;
  2. 追加:邮件队列先不支持大附件 —— 小附件设上限内联进行,大附件维持退回内联投递;storage 引用降为二期(plugin-email: sys_email 无附件/自定义 header 列,带附件的邮件拿不到队列投递的持久化保证(#5160 落地后) #5172,已移出派发队列)。

本单因此覆盖两块同形改动(同三个文件里的加列 + 重建 + 资格判定,拆开纯属自我串行):

A. headers_json(终态方案)

  1. sys-email.object.ts 加 headers_json(可空 JSON 文本;老行读回 undefined 必须安全);
  2. send() 落行时持久化 headers(两种模式都落,兼有审计价值);
  3. rowToNormalized() 重建 headers;
  4. headers 不再是退回内联的理由。

B. attachments_json(一期:有界内联,schema 一次到位)

  1. sys-email.object.ts 加 attachments_json,元素形状现在就设计成二期可扩展:
    { filename, contentType, size, hash, inline?: string /* base64 */, storageKey?: string }
    
    一期只有 inline 有生产者;storageKey 是二期(plugin-email: sys_email 无附件/自定义 header 列,带附件的邮件拿不到队列投递的持久化保证(#5160 落地后) #5172)的扩展点 —— 将来支持大附件是给已声明字段补生产者,不动 schema、不迁移数据。字段本身要有 TSDoc 说明 storageKey 现阶段无生产者、由 plugin-email: sys_email 无附件/自定义 header 列,带附件的邮件拿不到队列投递的持久化保证(#5160 落地后) #5172 跟踪,免得被 liveness 扫描误判。
  2. 上限:整封合计 256KB 原始字节(PM 默认,dev 可凭实测理由调整并在 PR 说明);写成导出常量,不做配置项——少一个旋钮少一处漂移,有实证需求再提升为设置。
  3. 资格判定:≤ 上限 → 入队(内容 base64 进 inline);> 上限 → 维持现行为(退回内联投递 + info 说明,理由文案更新为「附件合计超过 X」)。超限不是错误,最坏结果=今天的现状。
  4. rowToNormalized() 重建附件(含 cid——检查 EmailAttachment.cid 是否也要进元素形状,应该要,内联图片依赖它);
  5. Buffer 与 string 两种 content 形态都要正确编码/还原(契约是 string | Buffer)。

不在本单

验收

  • queueDelivery 开 + send({ headers }) → queued,worker 投递的消息含全部自定义 header;
  • queueDelivery 开 + 小附件(≤上限,含中文文件名、cid 内联图、Buffer 与 base64 两种 content)→ queued,worker 投递的消息附件完整还原(真 DbQueueAdapter + wire 测试断言字节一致);
  • 大附件(>上限)→ 退回内联 + info,文案说明超限,投递内容完整不剥附件;
  • 老行(两列皆无)读取安全;默认(内联)模式行为不变,既有断言零改动;
  • sys_email 行体积有界:附件列最坏 ~350KB(256KB 的 base64),有一条用例断言超限内容不会落列。

Activity

  1. changed the title [-]plugin-email/platform-objects: sys_email 加 headers_json —— 自定义 header 邮件取得队列投递资格(#5172 拆分,终态方案)[/-] [+]plugin-email/platform-objects: sys_email 加 headers_json + 有界 attachments_json —— 小附件与自定义 header 取得队列投递资格(#5172 一期)[/+] on Aug 4, 2026
  2. self-assigned this
    on Aug 4, 2026
  3. os-zhuang commented on Aug 4, 2026

    @os-zhuang
    ContributorAuthor

    认领:PM 循环第 7 轮
    会话:session_017MCKJaEomEqg4tvz4SzdNd
    分支:claude/issue-5177-sys-email-headers-attachments
    Worktree:objectstack-issue-5177
    域:domain:services(跨域声明:主体在 plugin-email,加列在 platform-objects)
    文件面:packages/plugins/plugin-email/src/、packages/platform-objects/src/audit/sys-email.object.ts(越界即停,报告说明)

    Blocked-by 已解除:#5161 的 PR #5191 已合入(d25f20b69),落地判据核过。与在飞的 #5193(plugin-audit)、#5195(objectql)不同包,并批安全。


    Generated by Claude Code

  4. os-zhuang commented on Aug 4, 2026

    @os-zhuang
    ContributorAuthor

    复核结论:ACCEPT —— PR #5211,转 ready 并入合并队列。head 上 23 个 check run 全部 success/skipped。

    接手过程与真因

    原 dev 推送后被会话用量终止(零报告)。接手 agent 的诊断值得记录:「TypeScript Type Check」job 的红根本不是类型错误 —— 完整日志显示 tsc 119/119 全过,失败的是同 job 里后置的 pnpm check:i18n:给 sys-email.object.ts 加两列(各带 label/description)改变了 i18n schema,四份生成 bundle 未再生成。修复 945935ed6 只动四份 bundle,并顺手把两个新键翻译到 zh-CN/ja-JP/es-ES(merge 模式只回填源文,而 sys_email 其余字段三语全有——不补就是新的不一致)。这是今天第三次「job 名 ≠ 真因」,取完整日志的纪律又对了一次。

    接手 agent 还按协议逐条核了分支实现对 #5177 规格的符合性(原 agent 零证据):超限不落列有直接断言、上限在边界处含等号且按整封求和、smtp.wire.test.ts 的抽取是纯机械搬移(同 3 测试 16 断言)。

    对留裁的形状偏差:接受,理由如下

    持久化元素形状与 issue 字面规格有两处偏差,均予采纳:

    1. contentType 可选 —— 与 EmailAttachment.contentType?: string 契约一致;issue 形状没标可选性是我写漏,强制必填反而要在持久化层发明一个契约上不存在的约束。
    2. 新增必填 contentForm —— content: string | Buffer 两种形态的无损往返需要这个判别子:没有它,重建时无从知道该还原成 string 还是 Buffer,而两者经 nodemailer 的传输编码路径不同。这是把「往返无损」从愿望变成结构保证,符合本单的全部要义。

    该形状是 sys_email 列内 JSON(内部持久化面),不是公共 spec 面,属 PM 可裁范围;若维护者异议可否决重开。

    衍生 #5217(check-i18n-bundles 把「CLI 未构建」误报成 9 个 bundle 问题,误导本地复现的第一步)已按观察类标 finding,留分诊轮。

    至此邮件线一期收尾:headers 与 ≤256KB 附件获得队列投递资格,storageKey 扩展点就位,#5172(二期)按维护者拍板继续暂缓。


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions