ซิงก์ทุกแท็บและทุกอุปกรณ์: ออกจากระบบ สิทธิ์ และตะกร้าสินค้า
ออกจากระบบทุกที่ในครั้งเดียว ใช้สิทธิ์ที่ถูกเพิกถอนทันที และให้ตะกร้าสินค้ากับการตั้งค่าเหมือนกันในทุกแท็บและทุกอุปกรณ์ที่ผู้ใช้เปิดอยู่
ผู้ใช้มักเปิดแอปของคุณไว้หลายแท็บและหลายอุปกรณ์ และแต่ละที่จะรู้ว่ามีอะไรเปลี่ยนก็ต่อเมื่อรีโหลด มีคนออกจากระบบบนคอมพิวเตอร์ที่ใช้ร่วมกัน แต่แท็บบนมือถือยังแสดงบัญชีของเขาอยู่ แอดมินถอนสิทธิ์แก้ไขของเพื่อนร่วมทีม แต่เพื่อนร่วมทีมยังแก้ไขต่อได้จนกว่าจะรีโหลด ใส่สินค้าลงตะกร้าบนมือถือ แต่แท็บบนแล็ปท็อปยังบอกว่าตะกร้าว่าง เมื่อใช้ Hibira เซิร์ฟเวอร์ของคุณแจ้งทุกที่ที่เปิดแอปอยู่ได้ในครั้งเดียว
ผู้ใช้ของคุณได้อะไร
- "ออกจากระบบทุกอุปกรณ์" ที่ปิดทุกเซสชันที่เปิดอยู่ทันทีจริง ๆ
- การเปลี่ยนบทบาทหรือถอนสิทธิ์มีผลโดยไม่ต้องรีโหลด
- ตะกร้า การตั้งค่า และจำนวนที่ยังไม่อ่านตรงกันทุกแท็บและทุกอุปกรณ์
สร้างด้วย
- การยืนยันตัวผู้ใช้ (
pusher.signin()) แบบเดียวกับการแจ้งเตือนในแอปแบบเรียลไทม์ ทุกการเชื่อมต่อที่ลงชื่อเข้าใช้จึงรู้ว่าเป็นของผู้ใช้คนไหน sendToUserเรียกครั้งเดียวถึงทุกแท็บและทุกอุปกรณ์ของผู้ใช้คนนั้นterminateUserConnectionsปิดทุกการเชื่อมต่อที่ลงชื่อเข้าใช้เป็นผู้ใช้คนนั้น
มีบางอย่างเปลี่ยนสำหรับผู้ใช้คนหนึ่ง
sendToUser()ออกจากระบบทุกอุปกรณ์
ขั้นตอนที่ 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>) และอนุญาตด้วยคุกกี้ของตะกร้าได้ - ส่งแค่จำนวนและยอดรวม ไม่ต้องส่งทั้งตะกร้า ขนาดของอีเวนต์ขึ้นอยู่กับแพ็กเกจ ดูที่ราคา