← 使用情境

即時站內通知

回覆、提及與核准一發生,就在使用者每個開著的分頁裡顯示;使用者離線時改用推播通知。

事情一發生產品就有反應,使用者是感受得到的:自己的貼文有了留言、申請被核准、隊友提到了自己。每 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 方案上,請在應用程式設定裡把你網站的網域列為允許的來源。