BÁO CÁO RÀ SOÁT THỰC TRẠNG WEB PUSH NOTIFICATION
Dưới đây là **Báo cáo Rà soát Toàn diện Thực trạng Hệ thống Web Push Notification** của **VinhPhatERP v3**, được thực hiện dựa trên việc phân tích mã nguồn thực tế, kiểm tra cấu hình VAPID, chạy bộ kiểm thử tự động và trích xuất dữ liệu chẩn đoán trực tiếp từ cơ sở dữ liệu Supabase.
---
# 📋 BÁO CÁO RÀ SOÁT THỰC TRẠNG WEB PUSH NOTIFICATION
## 1. Tổng quan Hiện trạng (Executive Summary)
Hệ thống Web Push Notification của VinhPhatERP v3 là một phân hệ quan trọng phục vụ:
- **Thông báo tin nhắn trò chuyện (Chat)** giữa nội bộ công ty và cổng khách hàng (Customer Portal), bao gồm hiển thị Lock Screen trên iOS/Android và badge số đếm đỏ ngoài icon ứng dụng.
- **Thông báo tác nghiệp ERP**: Tiến độ đơn hàng (`order_progress`), duyệt mua hàng (`purchase_order`), công nợ, kho bãi.
- **Bảo mật và an toàn dữ liệu**: Đã cấu hình NIST P-256 Single Source of Truth cho VAPID, cơ chế tự động che số tiền nhạy cảm trên màn hình khóa, và tự động thu hồi token trên thiết bị khi người dùng đăng xuất.
---
## 2. Kiến trúc & Luồng Xử lý (End-to-End Architecture)
Hệ thống được thiết kế theo mô hình 4 tầng khép kín:
```text
[Trình duyệt / PWA (Client)]
│
├── Service Worker (public/sw.js)
│ ├── Bắt sự kiện 'push' -> showNotification + navigator.setAppBadge
│ └── Bắt sự kiện 'notificationclick' -> focus tab / NAVIGATE_TO_CHAT / Deep Link
│
├── Frontend UI & Lifecycle
│ ├── Đăng ký SW tại src/main.tsx
│ ├── usePushSubscription.ts (Tự động đồng bộ / silent re-sync trên AppShell & NotificationBell)
│ └── NotificationSettingsCard.tsx (Giao diện cấu hình trạng thái FSM tại trang Cài đặt)
│
▼
[Database & Transactional Layer (Supabase PostgreSQL)]
├── Bảng public.push_subscriptions (Quản lý thiết bị multi-device, RLS theo auth.uid())
├── Bảng public.notification_outbox (Hàng đợi transactional chống mất thông báo)
├── Bảng public.notification_delivery_logs (Log kết quả gửi, latency_ms, HTTP status code)
└── Triggers: trg_chat_message_inserted -> net.http_post sang Edge Function
│
▼
[Dispatching Layer]
├── Edge Function (supabase/functions/send-web-push/index.ts)
│ ├── Fast 202 Response ( `***`) để tránh lộ bí mật kinh doanh trên màn hình khóa.
3. **Xử lý đặc thù iOS Safari PWA:** Đã có cơ chế phát hiện Safari tab thường (`isIOSNonStandalone`) và chặn đăng ký sai mục đích, đồng thời hiển thị modal hướng dẫn người dùng "Thêm vào Màn hình chính" (Add to Home Screen) mới được bật thông báo.
4. **Tự động dọn dẹp khi Đăng xuất (Signout Revocation):** Khi người dùng nhấn Đăng xuất, [AuthProvider.tsx](file:///d:/VinhPhatERP_v3/src/features/auth/AuthProvider.tsx#L184-L196) lập tức gọi `revokeCurrentDevicePushSubscription()` để hủy đăng ký trên trình duyệt và đánh dấu `revoked_at` trên database, ngăn ngừa lộ thông báo trên máy tính dùng chung.
5. **Cơ chế Tự phục hồi (Self-Healing):** [usePushSubscription.ts](file:///d:/VinhPhatERP_v3/src/shared/hooks/usePushSubscription.ts#L78-L120) tự động kiểm tra xem subscription của trình duyệt có khớp khóa VAPID hiện tại không; nếu cũ sẽ tự động unsubscribe và đăng ký mới âm thầm khi có quyền.
---
## 5. Các Vấn Đề & Rủi Ro Phát Hiện (Findings & Technical Debt)
| Mức độ | Vấn đề | Chi tiết | Vị trí |
| :--- | :--- | :--- | :--- |
| **Trung bình** | **Thiếu tài nguyên `badge-72.png`** | [web-push.service.ts](file:///d:/VinhPhatERP_v3/server/src/services/web-push.service.ts#L131) cấu hình `badge: '/badge-72.png'`, nhưng tệp này không tồn tại trong thư mục `public/` (trong khi [sw.js](file:///d:/VinhPhatERP_v3/public/sw.js#L57) dùng `/icon-192.png`). Khi backend server gửi push trực tiếp, thiết bị có thể nhận lỗi 404 khi tải badge. | `server/src/services/web-push.service.ts` |
| **Trung bình** | **Trùng lặp kiến trúc (Architectural Duality)** | Tồn tại 2 implementation song song: `usePushSubscription.ts` (ở shared) và `PushSubscriptionService` + `NotificationFacade` (ở features/notifications). Các layout chính (`AppShell`, `NotificationBell`, `PushNotificationBanner`) đang dùng hook cũ, trong khi trang Cài đặt dùng facade mới. Cách sinh `device_id` cũng đang lệch chuẩn (`crypto.randomUUID()` vs `dev_${Math.random()}`). | `src/shared/hooks/usePushSubscription.ts` & `src/features/notifications/` |
| **Thấp** | **Độ phủ thiết bị người dùng còn thấp** | Hiện tại toàn bộ hệ thống chỉ có **1 thiết bị admin** có subscription active. Các tài khoản khách hàng (Customer Portal) và nhân viên khác sau đợt rotate VAPID chưa kích hoạt lại token mới do chưa mở PWA hoặc chưa nhấn bật lại. | Cơ sở dữ liệu |
| **Thấp** | **Trigger chat không ghi outbox nếu chưa có recipient** | Trong `trg_fn_chat_message_inserted`, nếu `v_recipients` rỗng (ví dụ khách hàng chưa vào phòng hoặc chưa có profile hợp lệ), trigger sẽ bỏ qua không ghi vào outbox. Điều này làm mất dấu vết telemetry để điều tra tại sao thông báo không được gửi. | `supabase/migrations/20260922152000_fix_chat_messages_client_id_and_trigger.sql` |
---
## 6. Khuyến Nghị Đề Xuất (Next Steps)
1. **Khắc phục tài nguyên thiếu:** Tạo hoặc đồng bộ `public/badge-72.png` (hoặc sửa [web-push.service.ts](file:///d:/VinhPhatERP_v3/server/src/services/web-push.service.ts) trỏ về `/icon-192.png` như `sw.js`).
2. **Quy hoạch mã nguồn (Refactor Unified Hook):** Hợp nhất `usePushSubscription.ts` về sử dụng tầng Application `PushSubscriptionService` / `NotificationFacade` để đồng nhất domain logic, chuẩn hóa việc sinh `device_id` theo `crypto.randomUUID()`.
3. **Thúc đẩy kích hoạt lại thông báo trên Portal:** Đảm bảo [PushNotificationBanner](file:///d:/VinhPhatERP_v3/src/features/portal-shared/components/PushNotificationBanner.tsx) hiển thị nổi bật trên Customer Portal khi khách hàng đăng nhập để khuyến khích họ bật lại push notification sau đợt rotate key.
4. **Bổ sung audit log trong trigger:** Bổ sung việc ghi nhận trạng thái vào log/outbox kể cả khi không tìm thấy người nhận để tiện theo dõi vận hành.
---
*Báo cáo được khởi tạo tự động theo quy chuẩn AI Governance của VinhPhatERP v3.*
sử dụng gemini code nó quần em tơi bời
sử dụng gemini code nó quần em tơi bời