← 使用场景

实时站内通知

回复、提及与审批一发生,就在用户每个打开的标签页里显示;用户离线时改用推送通知。

事情一发生产品就有反应,用户是感受得到的:自己的帖子有了留言、申请被批准、队友提到了自己。每 30 秒轮询一次既慢又浪费请求。用 Hibira,你的服务器只送一次通知,用户开着的每个标签页与设备都会在几毫秒内显示。用户离线时,改由推送通知接手。

用户得到什么

  • 事情一发生,铃铛徽章与提示就在每个开着的标签页里更新。
  • App 关着时,手机上也收到同一条通知。
  • 不必刷新就能看到新动态,计数也不会过时。

用到的功能

  • 用户验证(pusher.signin()),每位用户都有只有自己能读的频道。
  • 官方服务器 SDK 的 sendToUser,送到该用户在每个节点上的每个连接。
  • 通过兼容 Beams 的 API 发送推送通知,给没有连接的用户。
  • 通知内容需要端到端加密时,使用私有加密频道(private-encrypted-)。

步骤 1:让用户在连接上登录

在频道授权路由旁边加上用户验证路由。它在你的服务器上运行,使用应用密钥。

// Express
app.post('/pusher/user-auth', requireLogin, express.urlencoded({ extended: false }), (req, res) => {
  const auth = pusher.authenticateUser(req.body.socket_id, {
    id: String(req.user.id),
    user_info: { name: req.user.name },
  });
  res.json(auth);
});

在浏览器里,照 JavaScript 指南连接并登录:

const pusher = new Pusher(import.meta.env.VITE_PUSHER_APP_KEY, {
  cluster: 'ap',
  wsHost: 'ws.hibira.io',
  wsPort: 443,
  wssPort: 443,
  forceTLS: true,
  enabledTransports: ['ws', 'wss'],
  userAuthentication: { endpoint: '/pusher/user-auth', transport: 'ajax' },
});
pusher.signin();

pusher.user.bind('notification', (n) => {
  bell.increment();
  toast.show(n.title, { href: n.url });
});

步骤 2:从服务器发送

先把通知存进数据库,刷新后清单才还在,然后再发送:

const n = await db.notifications.create({ userId, title: 'Ann replied to your comment', url: '/posts/42#c7' });
await pusher.sendToUser(String(userId), 'notification', { id: n.id, title: n.title, url: n.url });

一次 sendToUser 会送到用户所有的标签页与设备,不论有多少连接收到,都只算一条消息。

步骤 3:离线时改用推送

让每个已登录的页面订阅一个私有频道,例如 private-user-42,并让 webhook 端点订阅 channel_occupied 与 channel_vacated。这样你的服务器不必询问就知道谁在线:

app.post('/pusher/webhook', express.raw({ type: 'application/json' }), async (req, res) => {
  const webhook = pusher.webhook({ headers: req.headers, rawBody: req.body.toString() });
  if (!webhook.isValid()) return res.sendStatus(401);
  for (const e of webhook.getEvents()) {
    const id = e.channel.match(/^private-user-(.+)$/)?.[1];
    if (id) await db.users.update(id, { online: e.name === 'channel_occupied' });
  }
  res.sendStatus(200);
});

用户离线时,照推送通知指南发送到他们的设备:

if (!(await db.users.isOnline(userId))) {
  await beams.publishToUsers([String(userId)], {
    web: { notification: { title: n.title, deep_link: 'https://your-site.example' + n.url } },
    fcm: { notification: { title: n.title } },
    apns: { aps: { alert: { title: n.title } } },
  });
}

小提醒

  • 内容保持精简(ID、标题和链接),用户打开时再加载细节。事件上限是 10 KB(Free 方案为 4 KB)。
  • 在服务器上把通知标为已读,并送出 notifications-read 事件,每个标签页的徽章就会同时清除。
  • 健康、金融或人事数据,请改送到 private-encrypted-user-42 频道:Hibira 只转送密文。
  • 在 Free 方案上,请在应用设置里把你网站的域名列为允许的来源。