Email: SDD
PRD · Tests · trạng thái: đã xây · chi tiết vận hành ở platform/emails
Luồng
domain (user, membership-activation, events, admin, ...)
-> sendEmail() / fireEmail() api.conan.school/src/shared/email/send.ts
1. kiểm email_preferences nếu là thư gắn kết (bỏ qua: trả E_UNSUBSCRIBED, KHÔNG ghi sổ)
2. EMAIL_CATALOG[kind].render(data) templates.ts, khung layout()
3. chèn ảnh theo dõi + withUtm()
4. thư gắn kết: link huỷ + List-Unsubscribe / List-Unsubscribe-Post
5. env.EMAIL.send(...) binding [[send_email]] name="EMAIL" (wrangler.toml)
6. INSERT email_deliveries mỗi lượt một dòng, kể cả hỏng
7. E_RECIPIENT_SUPPRESSED -> ghi email_preferences source='suppressed'
8. bản sao kiểm tra nếu wantsAuditCopy()Thành phần
| Thành phần | File | Ghi chú |
|---|---|---|
| Cổng gửi | src/shared/email/send.ts: sendEmail, fireEmail, lookupRecipient, unsubscribeToken, withUtm, auditCopyAppliesTo | sendEmail không ném lỗi, trả SendEmailResult. env.EMAIL.send chỉ được gọi ở file này. fireEmail dùng qua waitUntil. |
| Người gửi | EMAIL_FROM trong templates.ts | Conan School, [email protected]. |
| Bản sao kiểm tra | EMAIL_AUDIT_COPY trong templates.ts | Hiện mode: 'cc', exclude: ['login_code']. auditCopyAppliesTo() loại thêm cả nhóm gắn kết. Hai đường sinh bản sao cùng hỏi wantsAuditCopy(). Chế độ 'cc' không ghi dòng audit_of và chung ảnh theo dõi; chỗ hiện lượt mở ghi nhãn theo số liệu qua operatorOpensInKindStat() / operatorOpensForDelivery() (send.ts); modeSince ghi lúc chế độ hiện tại bắt đầu, thư trước mốc đó sạch. |
| Khung | layout() trong templates.ts, bảng màu EMAIL_PALETTE | Hex chép từ theme.css, được scripts/check-token-sync.mjs so từng cặp. |
| Tra người nhận | lookupRecipient(env, userId) | Hỏi cả users lẫn legacy_users. |
| Biên nhận Master | activateToMaster() ở src/domains/membership-activation/repository.ts | Gửi membership_activated cho mọi đường kích hoạt; admin duyệt tay gửi ở src/domains/admin/membership_claims.ts. |
| Nhắc sự kiện | src/shared/email/reminders.ts, cron 7 * * * * trong src/index.ts | claimScheduledRun(env, 'event-reminders:<giờ>') rồi NOT EXISTS trên email_deliveries với audit_of IS NULL. |
| Thư hằng ngày | src/shared/email/daily.ts, cron 0 1 * * * | Khoá INSERT INTO daily_email_runs(run_date); công tắc platform_settings.daily_email_enabled; trần MAX_PER_RUN = 500; nguồn users status='active'. |
| Báo cáo vận hành | src/shared/email/report.ts, cùng cron | claimScheduledRun theo ngày (ops-daily-report:) và thứ Hai (ops-weekly-report:). |
| Mã đăng nhập | POST /api/user/auth/request-code ở src/domains/user/index.ts | |
| Gửi thử | POST /api/system/email/smoke ở src/domains/system/index.ts | Xem catalog. |
Bảng D1
email_deliveries (20260904a, thêm audit_of ở 20260907a), email_opens (20260907a), email_preferences (20260907b, bù suppressed ở 20260909c), daily_email_runs (20260909a). Chi tiết sổ ở delivery-log, tuỳ chọn ở preferences.