协同办公

Odoo Web Push 为什么不是勾个浏览器权限就完事:设备注册、VAPID 密钥与失效清理链路讲透

很多人觉得 Web Push 只要浏览器允许通知即可。源码里的 `mail.push.device` 和 `mail.push` 说明事情复杂得多:要校验 VAPID 公钥、登记 endpoint 与 keys、入队 payload、批量投递、清理不可达设备,还可能重新触发 cron 继续发送。

协同办公
进阶 开发者 1 分钟阅读
0 评论 0 点赞 0 收藏 5 阅读

先说结论

浏览器弹出“允许通知”只是 Web Push 的开头,不是结尾。

在 Odoo 里,真正要跑通 Web Push,还要完成设备登记、VAPID 公钥校验、payload 入队、批量发送,以及对不可达 endpoint 的清理。

所以“明明浏览器允许了,为什么还是收不到”通常不是一句权限问题能解释的。


一、为什么要有 mail.push.device

mail.push.device 存的不是抽象用户偏好,而是具体浏览器设备订阅:

  • endpoint
  • keys
  • expiration_time
  • partner_id

这说明 Odoo 不是按“用户开了通知”做粗粒度管理,而是按一个个浏览器订阅终端来管理。

这非常合理,因为同一个人可能:

  • 办公电脑开了通知
  • 手机浏览器开了通知
  • 旧笔记本上的订阅已经失效

这些都不该混成一个状态。


二、为什么注册设备时要校验 VAPID 公钥

register_devices() 会校验前端传来的 vapid_public_key 是否与数据库一致。

这一步很关键,因为 Web Push 不是随便给个 endpoint 就能发。

VAPID 这一层是在确保:

  • 当前浏览器订阅确实属于这套推送身份
  • 后端和前端对使用的推送密钥是一致的

如果密钥不一致,设备就算登记了,也难以稳定接收。


三、为什么缺公钥时会重建密钥并清空设备

get_web_push_vapid_public_key() 里,如果系统发现公钥不存在,会:

  • 清空已有设备
  • 重新生成 VAPID 公私钥

这看起来激进,但逻辑是成立的。

因为一旦密钥体系丢失,旧设备订阅其实已经不再可信;继续留着只会制造“看起来已注册,实际上永远发不到”的假象。


四、消息为什么不是立刻直接推,而是先进队列

mail.push 模型本质上是待发送通知队列。

发送逻辑 _push_notification_to_endpoint() 会:

  • 取一批待发通知
  • 用私钥、公钥去推给 endpoint
  • 失败时记录不可达设备
  • 成功或失败后清掉本批通知
  • 如果还有剩余,再触发 cron 继续跑

这说明 Odoo 把推送当成异步批处理任务,而不是阻塞主业务动作的同步调用。


五、为什么要删不可达设备

如果某个设备 endpoint 已失效,源码会把它加入待删除集合,最终清掉。

这是很重要的健康机制。

否则系统会长期保留:

  • 早已卸载或过期的浏览器订阅
  • 无法送达的旧 endpoint
  • 重复报错的垃圾设备

长久下来,通知队列会越来越脏。


六、最容易误解的地方

误区 1:允许浏览器通知 = Odoo 一定能推送

不是,中间还有注册、密钥、队列、投递等多步。

误区 2:一个用户只对应一个推送设备

不是,设备是按 endpoint 细分的。

误区 3:设备注册成功后就永远有效

不是,endpoint 可能失效,密钥体系也可能变化。


七、排错顺序

当用户说“开了通知但没收到”时,建议按这个顺序查:

  1. 设备是否真的完成 register_devices()
  2. 前端传的 VAPID 公钥是否与后台一致
  3. 通知是否已进入 mail.push 队列
  4. 发送后该 endpoint 是否被判定为 unreachable
  5. 系统是否因为密钥缺失重建过 VAPID,导致旧设备全部失效

最后一句

Web Push 在 Odoo 里不是浏览器权限小开关,而是一条完整的设备生命周期管理链。

只有按这条链去理解,通知问题才排得清楚。

DISCUSSION

评论区

想参与讨论?先 登录 再发表评论。
还没有评论,你可以成为第一个留言的人。