|
22 | 22 |
|
23 | 23 | 2. **`MailConfig` 当前已进入 monoengine 的编译 Config。** 结构位于 `src/config/model.rs`,字段包括 `enabled`、`provider`(默认 `smtp`)、`smtp_host`、`smtp_port`、`username`、兼容期 `password`、推荐的 `password_ref`、`from`、`starttls`、`dispatcher_batch_size`、`dispatcher_max_in_flight`、`retry_max_attempts`、`retry_backoff_base_secs`、`retry_backoff_max_secs`、外部模板覆盖字段 `template_default_locale` / `template_dir`,以及可选的 outbox 附件自动清理策略 `attachment_prune_enabled` / `attachment_prune_interval_secs` / `attachment_retention_days` / `attachment_prune_statuses`;`password` / `password_ref` 互斥且只适用于 SMTP provider,`mail.enabled = true` 且 `provider = "smtp"` 时要求 `smtp_host` / `from` 非空,dispatcher 限流值、retry policy 值、模板默认 locale、可选模板目录和附件清理策略值均要求非零/合法。 |
24 | 24 |
|
25 | | -3. **Notification 系统已接入主 crate,并在 mail 启用时启动 dispatcher**。`src/notification/{dispatcher, triggers, mod}.rs` + callisto 中的 `email_jobs`、`email_job_attachments`、`notification_event_types`、`user_notification_settings`、`user_notification_preferences` 等实体存在,触发器逻辑(`on_cl_comment_created` 等,尊重用户偏好)已从 mega 移植;`main.rs:18` 已声明 `mod notification;`,`AppContext::new` 在 vault 之后通过 `mail::mailer_from_config` 构造 SMTP 或 console mailer、创建 `EmailDispatcher` 并 `tokio::spawn`,HTTP graceful shutdown 已广播到 `notification_shutdown` 以取消 dispatcher。失败投递已有可配置 retry + dead-letter,dispatcher tick 已具备可配置批次/并发限流、结构化汇总日志、stale `sending` job 恢复、单 tick 背压测试、多 tick 高水位队列 drain 测试,以及跨独立 DB connection pool 的 claim 竞争基线测试;邮件作业管理 API 首批已落地(admin-only list / stats / failed retry / job prune / attachment metadata-download-delete-retention prune,其中附件 prune 可按 username / event_type_code 收窄);admin 事件类型 API 首批已落地(list / upsert);用户自助通知偏好 API 首批已落地(列出当前用户 settings/event prefs、更新 global enabled / delivery mode / preferred_locale、批量或单个 event preference);基础模板系统已落地并扩展出 template registry + locale fallback,CL 评论邮件已通过 registry 按 `user_notification_settings.preferred_locale` 渲染 subject/html/text 并默认 HTML 转义变量,且启动期可从 `mail.template_dir` 加载 TOML 模板覆盖内置模板;admin-only 模板管理 API 已落地(list 内置/外部模板、覆盖关系和来源路径,按管理员提供变量预览渲染,向 `mail.template_dir` 持久化 upsert 外部模板并热替换 registry);mailer 层已支持 HTML/Text + 附件 multipart 构造,outbox 已能持久化附件并由 dispatcher 投递;真实 SMTP/Mailpit 正路径基线已通过 `integration_mail_dispatcher_mailpit_sends_outbox_job` 覆盖,真实 SMTP transport 连接失败触发 retry 已通过 `integration_mail_dispatcher_smtp_failure_retries_outbox_job` 覆盖,真实 SMTP transport 连接失败达到尝试上限进入 dead-letter 已通过 `integration_mail_dispatcher_smtp_failure_dead_letters_outbox_job` 覆盖。当前仍需补齐更多业务触发器调用面、模板版本审计/回滚、更完整运维面,以及更完整 Mailpit/SMTP 故障矩阵、长时间压力形态高水位背压和真实多进程/黑盒 claim 竞争矩阵。 |
| 25 | +3. **Notification 系统已接入主 crate,并在 mail 启用时启动 dispatcher**。`src/notification/{dispatcher, triggers, mod}.rs` + callisto 中的 `email_jobs`、`email_job_attachments`、`notification_event_types`、`user_notification_settings`、`user_notification_preferences` 等实体存在,触发器逻辑(`on_cl_comment_created` 等,尊重用户偏好)已从 mega 移植;`main.rs:18` 已声明 `mod notification;`,`AppContext::new` 在 vault 之后通过 `mail::mailer_from_config` 构造 SMTP 或 console mailer、创建 `EmailDispatcher` 并 `tokio::spawn`,HTTP graceful shutdown 已广播到 `notification_shutdown` 以取消 dispatcher。失败投递已有可配置 retry + dead-letter,dispatcher tick 已具备可配置批次/并发限流、结构化汇总日志、stale `sending` job 恢复、单 tick 背压测试、多 tick 高水位队列 drain 测试,以及跨独立 DB connection pool 的 claim 竞争基线测试;邮件作业管理 API 首批已落地(admin-only list / stats / failed retry / job prune / attachment metadata-download-delete-retention prune,其中附件 prune 可按 username / event_type_code 收窄);admin 事件类型 API 首批已落地(list / upsert);用户自助通知偏好 API 首批已落地(列出当前用户 settings/event prefs、更新 global enabled / delivery mode / preferred_locale、批量或单个 event preference);基础模板系统已落地并扩展出 template registry + locale fallback,CL 评论邮件已通过 registry 按 `user_notification_settings.preferred_locale` 渲染 subject/html/text 并默认 HTML 转义变量,且启动期可从 `mail.template_dir` 加载 TOML 模板覆盖内置模板;admin-only 模板管理 API 已落地(list 内置/外部模板、覆盖关系和来源路径,按管理员提供变量预览渲染,向 `mail.template_dir` 持久化 upsert 外部模板并热替换 registry);mailer 层已支持 HTML/Text + 附件 multipart 构造,outbox 已能持久化附件并由 dispatcher 投递;真实 SMTP/Mailpit 正路径基线已通过 `integration_mail_dispatcher_mailpit_sends_outbox_job` 覆盖,真实 SMTP transport 连接失败触发 retry 已通过 `integration_mail_dispatcher_smtp_failure_retries_outbox_job` 覆盖,真实 SMTP transport 连接失败达到尝试上限进入 dead-letter 已通过 `integration_mail_dispatcher_smtp_failure_dead_letters_outbox_job` 覆盖,真实 SMTP 配置下缺失收件人直接 skipped 且不触发 retry 已通过 `integration_mail_dispatcher_smtp_skips_missing_recipient_without_retry` 覆盖。当前仍需补齐更多业务触发器调用面、模板版本审计/回滚、更完整运维面,以及更完整 Mailpit/SMTP 故障矩阵、长时间压力形态高水位背压和真实多进程/黑盒 claim 竞争矩阵。 |
26 | 26 |
|
27 | 27 | 4. **Vault 约束对 mail 的决定性影响**:`password_ref` 的真实值读取**必须**发生在 `VaultCore` 就绪之后。当前 `AppContext::new` 顺序为 `Storage::new (DB + object_storage + Buck 校验) → init_connection(redis) → VaultCore::new → 若 provider 为 SMTP 则解析 mail.password_ref → mailer_from_config + EmailDispatcher spawn → init_monorepo`。因此 mailer 的**构造时机**是 mail 模块设计的核心约束(详见「运行时注入与晚绑定构造」)。 |
28 | 28 |
|
|
47 | 47 | | 模板 / 富文本 / 附件 | **模板 registry + 启动期 TOML 覆盖 + 管理端审计/预览/持久化 upsert + HTML/Text + 用户 locale + outbox 附件 + 附件 metadata/下载/删除/保留期治理** | `src/mail/template.rs` 已提供轻量 `MailTemplate`、`MailTemplateRegistry`、`LocalizedMailTemplate` 和 `MailTemplateKey`,支持 `{{var}}` 渲染、HTML 变量默认转义、缺失变量脱敏诊断、按 locale 查找以及 language/default fallback;CL 评论触发器已按每个收件人的 `user_notification_settings.preferred_locale` 渲染 subject/html/text,并提供 `zh-CN` 首个本地化模板。`mail.template_default_locale` / `mail.template_dir` 已支持启动期从 TOML 文件加载 key/locale/subject/html/text 覆盖模板,覆盖项按 key + locale 替换内置模板且模板语法 fail-closed 校验;admin-only `GET /admin/mail-templates` 已支持审计内置/外部模板、来源路径和覆盖关系,`POST /admin/mail-templates/preview` 已支持按管理员提供变量预览 subject/html/text 渲染结果,`PUT /admin/mail-templates/{key}/{locale}` 已支持在 `mail.template_dir` 中创建或更新外部 TOML 模板、复用既有来源文件、拒绝坏模板语法并热替换通知模板 registry。`send_html(to, subject, html, text?)` 实现 alternative multipart;`send_html_with_attachments(...)` + `MailAttachment` 已支持 SMTP mixed multipart 附件构造,console provider 只记录附件数量/字节数且不输出正文;`email_job_attachments` 已支持 outbox 级附件持久化,dispatcher claim 后会读取附件并调用附件发送路径;admin-only `/admin/email-jobs/{id}/attachments` 已提供附件 metadata 审计视图(id、文件名、content type、字节数、创建时间)且不返回内容,`GET /admin/email-jobs/{job_id}/attachments/{attachment_id}/content` 已支持显式下载单个附件内容,`DELETE /admin/email-jobs/{job_id}/attachments/{attachment_id}` 已支持删除持久化附件,`POST /admin/email-jobs/attachments/prune` 已支持按保留期清理旧 `sent`/`skipped` 终态 job 的持久化附件,并可按 `username` / `event_type_code` 收窄清理范围;`mail.attachment_prune_*` 已支持 dispatcher 内置的可配置自动附件保留清理。仍无更高阶模板引擎、模板版本审计/回滚和按租户/事件策略预设。 | |
48 | 48 | | 与 user_notification_* / 事件类型 的完整联动 | **实体+存储+触发器骨架存在,API 首批落地** | callisto 实体 + NotificationStorage 方法 + triggers(cl.comment 等)已移植自 mega,dispatcher 常驻任务、mailer 注入和失败 dead-letter 基线已接入;admin-only 邮件作业管理 API 已支持按状态/用户/事件查询、状态统计、failed job 手动重排、旧 `sent`/`skipped` 终态 job 清理,以及查看、下载、删除或按保留期清理旧终态 outbox 附件;admin-only 事件类型 API 已支持 list/upsert;用户自助 API 已支持查询当前用户 settings/event preference effective 状态,并更新 global enabled、delivery mode、批量或单个 event preference。仍缺更多业务触发器调用面、更完整观测和运维控制。 | |
49 | 49 | | Profile / 热加载 / 集中校验对 mail 的支持 | **部分实现** | Profile、集中校验和 source warning 已接入 config 管线;热加载当前支持 `mail.enabled` true→false 关停 dispatcher,并支持 `mail.dispatcher_batch_size` / `mail.dispatcher_max_in_flight` 运行期调整 dispatcher 背压参数、`mail.retry_max_attempts` / `mail.retry_backoff_base_secs` / `mail.retry_backoff_max_secs` 运行期调整 retry/dead-letter 策略,以及 `mail.attachment_prune_*` 运行期调整自动附件保留策略。重新启用 mail、`mail.provider`、SMTP 参数、凭据变更和 `mail.template_*` 模板配置变更仍要求重启或后续动态 mailer/模板 registry 重建设计,该边界已有 config reload restart-required 矩阵测试。 | |
50 | | -| 测试与 CI 覆盖 | **部分实现** | mail 自身有构造/消息验证、provider 工厂、console provider、SMTP 附件 mixed multipart 构造、附件 content-type 校验、模板渲染/缺失变量/HTML 转义、template registry、locale fallback、外部 TOML 模板覆盖/来源路径审计/TOML 序列化 round-trip/重复 key-locale 拒绝测试;dispatcher 有使用 Noop 的集成风格测试(需 DB + migration),并已补充真实 SMTP/Mailpit 正路径测试 `integration_mail_dispatcher_mailpit_sends_outbox_job`,覆盖 pending outbox job 经 `SmtpMailer` 投递到 Mailpit 后进入 `sent` 且写入 `sent_at`,真实 SMTP 连接失败 retry 测试 `integration_mail_dispatcher_smtp_failure_retries_outbox_job`,覆盖 transport 错误后 job 回到 `pending`、`retry_count` 增加且 `next_retry_at` 写入,真实 SMTP 连接失败 dead-letter 测试 `integration_mail_dispatcher_smtp_failure_dead_letters_outbox_job`,覆盖达到尝试上限后 job 进入 `failed` 且不再写入 `next_retry_at`,以及 `dispatcher_drains_high_water_queue_across_bounded_ticks` 覆盖高水位队列按配置 batch 多 tick 排空;已覆盖 `password_ref` 解析失败脱敏、坏配置不 panic、`mail.enabled` 关停热加载、dispatcher 批次/并发限流热加载、retry policy 热加载、自动附件保留策略热加载、template 配置 restart-required 边界、provider/SMTP 参数/凭据重配只报告需重启、失败发送的 retry/dead-letter disposition、dispatcher 有界并发、单 tick 批次背压、stale `sending` 恢复、storage-level 并发 claim 竞争、跨独立 DB connection pool 的 claim 竞争基线、邮件作业 list/stats/failed retry 管理原语、outbox 附件持久化、附件 metadata 查询/内容读取/删除/保留期清理、dispatcher 自动附件保留清理和 dispatcher 附件投递、admin template list/preview/upsert 管理原语、用户 notification preference/settings response 映射和 update payload 校验、用户 preferred_locale 存储/响应校验,以及 CL 评论触发器按收件人 locale/覆盖 registry 的模板渲染。仍缺更完整 Mailpit/SMTP 故障矩阵、长时间压力形态高水位背压压测和真实多进程/黑盒 claim 竞争矩阵。 | |
| 50 | +| 测试与 CI 覆盖 | **部分实现** | mail 自身有构造/消息验证、provider 工厂、console provider、SMTP 附件 mixed multipart 构造、附件 content-type 校验、模板渲染/缺失变量/HTML 转义、template registry、locale fallback、外部 TOML 模板覆盖/来源路径审计/TOML 序列化 round-trip/重复 key-locale 拒绝测试;dispatcher 有使用 Noop 的集成风格测试(需 DB + migration),并已补充真实 SMTP/Mailpit 正路径测试 `integration_mail_dispatcher_mailpit_sends_outbox_job`,覆盖 pending outbox job 经 `SmtpMailer` 投递到 Mailpit 后进入 `sent` 且写入 `sent_at`,真实 SMTP 连接失败 retry 测试 `integration_mail_dispatcher_smtp_failure_retries_outbox_job`,覆盖 transport 错误后 job 回到 `pending`、`retry_count` 增加且 `next_retry_at` 写入,真实 SMTP 连接失败 dead-letter 测试 `integration_mail_dispatcher_smtp_failure_dead_letters_outbox_job`,覆盖达到尝试上限后 job 进入 `failed` 且不再写入 `next_retry_at`,真实 SMTP 配置下缺失收件人 skip 测试 `integration_mail_dispatcher_smtp_skips_missing_recipient_without_retry`,覆盖 job 进入 `skipped` 且不递增 retry,以及 `dispatcher_drains_high_water_queue_across_bounded_ticks` 覆盖高水位队列按配置 batch 多 tick 排空;已覆盖 `password_ref` 解析失败脱敏、坏配置不 panic、`mail.enabled` 关停热加载、dispatcher 批次/并发限流热加载、retry policy 热加载、自动附件保留策略热加载、template 配置 restart-required 边界、provider/SMTP 参数/凭据重配只报告需重启、失败发送的 retry/dead-letter disposition、dispatcher 有界并发、单 tick 批次背压、stale `sending` 恢复、storage-level 并发 claim 竞争、跨独立 DB connection pool 的 claim 竞争基线、邮件作业 list/stats/failed retry 管理原语、outbox 附件持久化、附件 metadata 查询/内容读取/删除/保留期清理、dispatcher 自动附件保留清理和 dispatcher 附件投递、admin template list/preview/upsert 管理原语、用户 notification preference/settings response 映射和 update payload 校验、用户 preferred_locale 存储/响应校验,以及 CL 评论触发器按收件人 locale/覆盖 registry 的模板渲染。仍缺更完整 Mailpit/SMTP 故障矩阵、长时间压力形态高水位背压压测和真实多进程/黑盒 claim 竞争矩阵。 | |
51 | 51 |
|
52 | 52 | **已知加载/启动/安全风险点(必须在相应阶段消除,与 config.md 风险点重叠)**: |
53 | 53 | - mailer 或 dispatcher 若被移动到 Storage::new / vault 前路径,会违反 vault 就绪顺序;当前代码位置正确,但需防止后续回归。 |
@@ -206,7 +206,7 @@ ConfigLoader + Config::new (含未解析 SecretRef 的 mail) |
206 | 206 |
|
207 | 207 | **阶段 0(已完成)**:激活一级 mail + MailConfig 入 Config + 修复 notification 引用 + 清理 shim。 |
208 | 208 |
|
209 | | -**阶段 1(已接入,基线已加固)**:在 service 启动路径中 late-construct mailer 并 spawn dispatcher 已落地;构造失败静默忽略已改为可诊断错误;失败发送已具备可配置 retry + dead-letter disposition,并按 base/max 配置执行指数退避;dispatcher 已具备每 tick 可配置批次/并发限流、结构化汇总日志、stale `sending` 恢复、单 tick 批次背压测试、多 tick 高水位队列 drain 测试、storage-level 并发 claim 竞争测试、跨独立 DB connection pool 的 claim 竞争基线、真实 SMTP/Mailpit 正路径集成测试,以及真实 SMTP 连接失败 retry 与 dead-letter 测试。剩余是完善 Mailpit/SMTP 故障矩阵、长时间压力形态高水位背压验证和真实多进程/黑盒 claim 竞争矩阵。 |
| 209 | +**阶段 1(已接入,基线已加固)**:在 service 启动路径中 late-construct mailer 并 spawn dispatcher 已落地;构造失败静默忽略已改为可诊断错误;失败发送已具备可配置 retry + dead-letter disposition,并按 base/max 配置执行指数退避;dispatcher 已具备每 tick 可配置批次/并发限流、结构化汇总日志、stale `sending` 恢复、单 tick 批次背压测试、多 tick 高水位队列 drain 测试、storage-level 并发 claim 竞争测试、跨独立 DB connection pool 的 claim 竞争基线、真实 SMTP/Mailpit 正路径集成测试,以及真实 SMTP 连接失败 retry/dead-letter 与缺失收件人 skip 测试。剩余是完善 Mailpit/SMTP 故障矩阵、长时间压力形态高水位背压验证和真实多进程/黑盒 claim 竞争矩阵。 |
210 | 210 |
|
211 | 211 | **阶段 2(已完成首批)**:与 config SecretRef 基础设施联动。`MailConfig` 已支持 `password_ref`,resolver 解析路径已在 `AppContext::new` 中落地,`config secret set/check` 已支持 mail password 引用。剩余是继续治理兼容期明文 `password` 的退场策略。 |
212 | 212 |
|
|
0 commit comments