This document was written by AI and has been manually reviewed.
通知
Prism 在与 用户 webhook 相同的事件上向用户推送通知 — 应用变更、域名生命周期、安全因素变化、OAuth 同意授权/撤销、团队成员关系等等 — 不过通道是用户自己的邮箱与 Telegram,而不是任意 URL。每个用户自行决定哪些事件触发、推送到哪里。
支持两种配置方式:
- 偏好(preferences) — 扁平的
事件 → brief|full映射。简单,配起来快。 - 规则集(rulesets) — 命名的、按顺序的 match/action 数组,支持账号过滤与
stop。当你需要把同一事件中的某一封邮件静音、或把 brief / full 在不同通道间分开时表达力更强。
引擎会先评估规则集;偏好映射作为兼容路径,仅在没有规则集时使用。
事件
| 事件 | 触发条件 |
|---|---|
app.created / updated / deleted | 你的 OAuth 应用被改动 |
domain.added / verified / deleted | 你的域名被改动 |
connection.added / removed | 社交账号被绑定 / 解绑 |
connection.login | 通过已绑定的社交账号完成了一次登录 |
profile.updated | 显示名 / 头像等被改动 |
security.passkey_added / _removed | Passkey 添加 / 移除 |
security.totp_enabled / _disabled | TOTP 认证器启用 / 移除 |
token.created / revoked | PAT 被创建 / 撤销 |
team.member_added / _removed | 你被加入 / 移出团队 |
oauth.consent_granted / _revoked | 你授予 / 撤销了对某应用的 OAuth 授权 |
事件清单与 worker/lib/notifications.ts → USER_NOTIFICATION_EVENTS 一致。Webhook 使用同一组名称 — 这就是「能被通知什么」的唯一权威列表。
等级
每条投递有一个等级:
brief— 最少:事件名 + 时间 + 资源标签(应用名、域名等)。full— 在此之上还会包含永久链接、IP、User-Agent 以及关联元数据(如connection.login会加上来源 slug、provider 用户名、是否新设备等)。
通道
| 通道 | 配置方式 |
|---|---|
| 邮件 | 用户的已验证主邮箱 + user_emails 中任意已验证次要邮箱。HTML 正文构造时已 XSS-safe(所有插值都先经过 HTML 转义) |
| Telegram | 已绑定的 Telegram 社交连接 — 复用其 bot(由 tg_notify_source_slug 指定)发消息。需要管理员先创建一个 Telegram OAuth 源 |
tg_notify_source_slug 为空时,即使用户在规则中配置了 Telegram 通道,整个站点的 Telegram 投递仍然停用。邮件投递则需要管理员配置好邮件 provider。
偏好(扁平)
最简形式。存储于 user_notification_prefs.events(Telegram 走 tg_events 镜像),结构为 Record<string, "brief"|"full">。未列出的事件视为「关闭」。
GET /api/user/me/notifications:
{
"email_events": {
"app.created": "full",
"security.totp_enabled": "brief"
},
"tg_events": {
"connection.login": "full"
}
}PUT /api/user/me/notifications 接受相同结构并原子替换。
历史的 string[] 形态(所有事件都按 "full" 处理)仍由 parsePrefsEvents 兼容解析。
规则集
规则集是一组按顺序的规则。事件触发时,引擎从上向下遍历,把每条匹配规则的副作用累加,最后产出去重后的投递列表。
规则结构
{
"id": "rule_xyz",
"name": "burner 邮箱屏蔽域名噪音",
"enabled": true,
"match": {
"event": "domain.*",
"accounts": ["email:burner-id"]
},
"action": {
"type": "drop"
},
"stop": false
}| 字段 | 含义 |
|---|---|
id | 服务端生成的稳定 ID |
name | 人类可读标签(≤ 64 字符,可选) |
enabled | false 时跳过此规则,不影响状态 |
match.event | 通配符:* 匹配任意,? 匹配单字符,其余字面匹配。锚定匹配 — app 只匹配 app,不匹配 appx |
match.accounts | 可选。每项形如 email:<email_id> 或 tg:<connection_id>。限定该规则副作用只作用于这些账号 |
action.type | send(追加通道)或 drop(清空已积累的投递集合) |
action.channels(仅 send) | 数组,元素 { kind: "email", email_id, level } 或 { kind: "tg", connection_id, level } |
stop | true 在该规则触发后停止后续遍历 |
评估语义
- 多条
send累加。同一通道再次出现且等级更高,则升级(full > brief)。 drop清空目前积累的投递。配合上方更宽松的规则,可表达「除了 X 之外都发到主邮箱」。match.accounts过滤的是规则的副作用,而非匹配条件。带账号过滤的send会静默跳过不在列表中的通道;带账号过滤的drop只清空列出的账号,其它通道继续保留。stop: true停止遍历。常用作锚点,避免下方更通用的规则把当前规则的效果撤销。
完整示例
[
{
"id": "1",
"name": "全事件 → 主邮箱",
"match": { "event": "*" },
"action": {
"type": "send",
"channels": [{ "kind": "email", "email_id": "primary", "level": "full" }]
}
},
{
"id": "2",
"name": "应用事件再发到 Telegram(brief)",
"match": { "event": "app.*" },
"action": {
"type": "send",
"channels": [{ "kind": "tg", "connection_id": "tg_abc", "level": "brief" }]
}
},
{
"id": "3",
"name": "屏蔽域名噪音",
"match": { "event": "domain.*" },
"action": { "type": "drop" }
}
]security.totp_enabled → 主邮箱(full)。app.created → 主邮箱(full)+ Telegram(brief)。domain.verified → 无投递(规则 3 把规则 1 加进来的清空了)。
端点
GET /api/user/me/notification-rulesets
POST /api/user/me/notification-rulesets
PUT /api/user/me/notification-rulesets/:id
DELETE /api/user/me/notification-rulesets/:idPOST 调用 sanitizeRulesArray 进行校验:
- 最多 200 条。
- 未知的 action 类型或错误结构的 channel 一律
400。 - 未知事件名也
400(接受USER_NOTIFICATION_EVENTS中的所有事件以及*/?通配符)。
Telegram 配置
Telegram 投递复用 Telegram OAuth 源的 bot。
- 按照 社交登录配置 → Telegram 添加 Telegram 源,bot token 存于该源的
client_secret,并保持启用。 - 在 Admin → Settings → Notifications 把
tg_notify_source_slug设为该源的 slug。 - 想接收 Telegram 通知的用户需要在 资料 → 已关联账号 绑定自己的 Telegram(要使用与 BotFather 中
setdomain相同的域名)。该 connection 的provider_user_id即为 Prism 推送时的 chat ID。 - 在用户规则 / 偏好中加入 Telegram 通道。
Telegram 消息是纯文本,附带回到对应 Prism 页面的链接。
邮件渲染
邮件正文为 HTML:精简的内联 header 加一张关键字段表格。所有动态值都先经 esc() 转义(& < > " '),所有链接都通过 safeHref() 过滤(只放行 http: / https:)。纯文本备份从相同数据自动生成。
隐私提示
full等级会在安全相关事件(security.*、connection.login、token.*)上携带 IP 与 UA,便于发现陌生登录。如果你不希望这些信息落到邮箱里,请改用brief。- Telegram 消息会途经 Telegram 服务器。任何你不希望出现在 Telegram 聊天里的信息,就别用 Telegram 通道的
full等级 — 改用brief。