← 使用情境

多分頁、多裝置同步:登出、權限與購物車

一次登出所有地方、撤銷的角色立即生效,並讓購物車與設定在使用者開著的每個分頁和裝置上保持一致。

使用者常在好幾個分頁、好幾台裝置上開著你的應用,而每一份都要重新整理才知道有變化。有人在共用電腦上登出了,手機上的分頁卻還顯示他的帳號。管理員撤掉了同事的編輯權限,同事卻能一直編輯到重新整理為止。手機上把商品加進購物車,筆電的分頁卻還說購物車是空的。用 Hibira,你的伺服器能一次通知應用的每一份副本。

使用者得到什麼

  • 「登出所有裝置」真的會立刻關閉每個打開的工作階段。
  • 角色變更或權限撤銷不必重新整理就生效。
  • 每個分頁、每台裝置上的購物車、設定和未讀數都一致。

用到的功能

  • 使用者驗證(pusher.signin()),做法同即時站內通知,讓每個登入的連線都屬於某位使用者。
  • sendToUser,一次呼叫就送到這位使用者的每個分頁和裝置。
  • terminateUserConnections,關閉以這位使用者登入的每個連線。

某位使用者的資料變了

你的伺服器登出、新角色、購物車
sendToUser()
Hibira
WebSocket
每個分頁和裝置屬於這位使用者

登出所有裝置

你的伺服器工作階段已撤銷
全部斷線
Hibira
關閉
每個連線以這位使用者登入
伺服器的一次呼叫,送到使用者打開的每一份應用

步驟 1:登出所有裝置

先在資料庫撤銷工作階段,再通知每一份打開的應用,最後關閉連線:

app.post('/account/sign-out-everywhere', requireLogin, async (req, res) => {
  const id = String(req.user.id);
  await db.sessions.revokeAll(id);
  await pusher.sendToUser(id, 'signed-out', {});
  await pusher.terminateUserConnections(id);
  res.sendStatus(204);
});

被關閉的連線無法再次登入,因為你的使用者驗證端點已經不接受被撤銷的工作階段。

步驟 2:權限變更立刻生效

管理員變更某人的角色時,送出一個提醒。頁面會向你的伺服器重新取得權限,伺服器仍然是唯一的依據:

await db.members.update(member.id, { role: 'viewer' });
await pusher.sendToUser(String(member.userId), 'permissions-changed', {});

步驟 3:讓每個地方的購物車都一樣

每次購物車變動後,送出頁首要顯示的摘要:

app.post('/api/cart/items', requireLogin, async (req, res) => {
  const cart = await db.carts.add(req.user.id, req.body);
  await pusher.sendToUser(String(req.user.id), 'cart', { count: cart.count, total: cart.total });
  res.json(cart);
});

步驟 4:在每個分頁做出反應

pusher.user.bind('signed-out', () => location.assign('/login'));
pusher.user.bind('permissions-changed', async () => {
  applyPermissions(await fetch('/api/me/permissions').then((r) => r.json()));
});
pusher.user.bind('cart', ({ count, total }) => cartBadge.set(count, total));

小提醒

  • 安全上不要相信事件本身:它只代表「請重新檢查」。你的 API 仍然要在每個請求檢查工作階段和角色。
  • BroadcastChannel 能同步同一個瀏覽器的分頁,但無法同步其他裝置,也收不到管理員或伺服器做的變更。
  • 訪客購物車可以一個購物車一個私人頻道(private-cart-<id>),用購物車的 cookie 授權。
  • 送數量和總額就好,不要送整個購物車;事件大小依方案而定,請看價格。