Consent 与隐私偏好
Necessary、Analytics、Marketing 分类、匿名 Cookie、撤回流程和未来 Provider 接入规则。
Consent 由 Analytics 或 Newsletter 派生,而不是固定 Core;两者都关闭时 UI、API 与客户端状态不进入 Web 产物。启用时它们共享同一份 Consent policy,不各自维护状态;快照不绑定账号、不写 D1,也不包含邮箱、Session、Prompt 正文或其他 PII。
分类与默认值
| 分类 | 默认值 | 用途 |
|---|---|---|
| Necessary | 允许 | 安全、认证、保存隐私选择,以及退订、suppression、删除履行 |
| Analytics | 拒绝 | 页面浏览和类型化产品事件 |
| Marketing | 拒绝 | Newsletter 营销发送、新增联系人和 Provider 正向同步 |
缺失 Cookie、损坏 JSON、未知字段、非布尔选择或不兼容 policy version 都会回到 optional denied。Necessary 不能被改写成 Marketing;撤回 Marketing 后,新增联系人和发送停止,但退订、suppression、Provider 联系人删除与删除请求履行仍必须继续。
状态与 HTTP 边界
GET /api/consent 返回当前快照:
{
"choices": { "analytics": false, "marketing": false },
"policyVersion": 1,
"status": "unconfigured",
"updatedAt": null
}PUT /api/consent 只接受 analytics 和 marketing 两个布尔字段。Worker 生成 policy version 与更新时间,并写入 tanplate-consent Cookie:
HttpOnlySameSite=LaxPath=/- HTTPS 下增加
Secure - 一年有效期
GET/PUT 均返回 Cache-Control: no-store。客户端不能直接读取 Cookie,只能消费并再次严格校验同源 API 返回的快照;不兼容或畸形响应不能授权 optional effect。
用户流程
Consent 能力启用时,首访者会在页面底部看到不遮挡主内容的 band,可以拒绝全部可选项、分别查看两类选择或全部接受。偏好 Dialog 将 Necessary 显示为始终启用,并为 Analytics/Marketing 提供独立 toggle。保存后 band 消失;顶栏盾牌按钮可以随时重开。保存会广播本页 typed snapshot,并写入不含选择与 PII 的 localStorage revision;其他打开的标签页收到 signal 后先把 optional cache 置为 denied,再从 API 刷新。
Provider 接入
Analytics/Newsletter 不得直接读取 Cookie 或另建同意布尔值。服务端 effect 使用 runConsentEffect,客户端脚本消费 API 快照与 Consent 同步事件,并继续调用同一 allowsConsentCategory / allowsConsentOperation policy。功能关闭、快照不兼容或同意不足时,gate 不调用 effect callback。
Analytics 已由 PRD 16 通过同一 policy 接入类型化 facade 和 Umami adapter;未同意或模块关闭时不请求配置、加载脚本或排队事件,撤回时清理匿名 Provider 状态。Newsletter 的真实 Provider 消费仍由 PRD 17 验收;Jobs/Webhooks 已由 PRD 15 验证。
验证
pnpm --filter @tanplate/consent test
pnpm --filter web test
pnpm --filter web exec playwright test tests/consent-preferences.spec.ts覆盖单一分类源、合规操作、无副作用 gating、Cookie/API snapshot fail-closed、静默本地化请求、本页/跨标签页同步,以及首访、独立选择、刷新、重开、撤回和损坏 Cookie 浏览器流程。