# ✅ Realtime Notification System - Setup Complete

## 🎉 Hoàn thành

Hệ thống notification realtime đã được setup đầy đủ với Soketi (Pusher protocol).

## 📦 Đã tạo

### 1. Database
- ✅ `notification_receives` - Lưu notification chính
- ✅ `notification_receive_users` - Lưu notification cho từng user

### 2. Models
- ✅ `NotificationReceive` - Model với UsesTenantConnection trait
- ✅ `NotificationReceiveUser` - Model với UsesTenantConnection trait

### 3. Controllers
- ✅ `NotificationReceiveController`
  - `POST /api/notification/receive` - Nhận notification từ external server
  - `GET /api/notification/user` - Lấy notifications của user
  - `POST /api/notification/{id}/read` - Đánh dấu đã đọc
  - `GET /api/notification/unread/count` - Đếm số chưa đọc

### 4. Jobs & Events
- ✅ `ProcessNotificationReceiveJob` - Xử lý async, gửi cho users
- ✅ `NewNotificationReceived` - Event broadcast qua Soketi

### 5. Configuration
- ✅ `config/broadcasting.php` - Cấu hình Soketi
- ✅ `config/logging.php` - Log channel riêng
- ✅ `routes/api.php` - API routes
- ✅ `routes/channels.php` - Broadcast authorization
- ✅ `bootstrap/app.php` - Load channels.php

### 6. Dependencies
- ✅ `pusher/pusher-php-server` - Installed

## 🔧 Cần làm để hoàn tất

### Bước 1: Restart services

```bash
cd /var/www/html/lms_hocmai

# Clear cache
php artisan config:clear
php artisan route:clear

# Restart queue worker
php artisan queue:restart

# Ensure Soketi running
ps aux | grep soketi
```

### Bước 2: React Code (Final)

**React đang dùng Private Channel - Đúng rồi!**

Đảm bảo React listen event name đúng:

```typescript
useEffect(() => {
  const userId = userInfo?.id;
  if (!userId) return;

  const channelName = `notification.user.${userId}`;
  
  // ✅ Private channel (need auth)
  channelRef.current = echo.private(channelName);
  
  // ✅ Listen event KHÔNG có dấu chấm (vì có namespace)
  channelRef.current.listen('notification.received', (event: any) => {
    console.log('🔔 New notification:', event);
    
    const newNotification: NotificationData = {
      id: event.notification_id,
      title: event.title,
      message: event.body,
      type: 'info',
      read: false,
      created_at: event.created_at,
      data: {
        notification_receive_id: event.notification_receive_id,
      }
    };

    setNotifications((prev) => [newNotification, ...prev]);
    setUnreadCount((prev) => prev + 1);
  });

  return () => {
    if (channelRef.current) {
      channelRef.current.stopListening('notification.received');
      echo.leave(channelName);
    }
  };
}, [userInfo?.id]);
```

### Bước 3: Test

**Test 1: Via Command**
```bash
cd /var/www/html/lms_hocmai
php artisan notification:test 1
```

**Test 2: Via API**
```bash
curl --location --request POST 'http://lms.hocmai.net/api/notification/receive' \
--header 'Content-Type: application/json' \
--data '{
  "code":"ICC",
  "key":"key_icc_prod",
  "data":{
      "type":2,
      "data":["PHONE_NUMBER"],
      "lms_id":[],
      "title":"Test notification",
      "body":"<p>Test content</p>"
  },
  "message_id": 999
}'
```

## 🔑 Key Points

### Event & Channel Mapping

| Component | Value |
|-----------|-------|
| Laravel Event | `NewNotificationReceived` |
| Broadcast Type | `PrivateChannel` |
| Channel Name | `notification.user.{userId}` |
| Event Name (broadcastAs) | `notification.received` |
| React Channel | `echo.private('notification.user.1')` |
| React Listen | `'notification.received'` (NO DOT!) |

### Authentication Flow

1. React connect WebSocket → Soketi
2. React subscribe private channel → Laravel auth endpoint
3. Laravel check authorization in `routes/channels.php`
4. Laravel authorize if `user->id === userId`
5. Soketi confirm subscription
6. Laravel broadcast event → Soketi → React

## 📊 Expected Flow

```
External Server
    ↓
POST /api/notification/receive
    ↓
NotificationReceiveController
    ↓
Save to DB (notification_receives)
    ↓
Dispatch ProcessNotificationReceiveJob
    ↓
Job: Query students by type
    ↓
For each student:
  - Create notification_receive_users record
  - Broadcast NewNotificationReceived event
    ↓
Soketi receives broadcast
    ↓
Soketi push to React client (if subscribed)
    ↓
React receive event → Update UI
```

## 🐛 Troubleshooting

### Issue 1: "Unauthenticated" error

**Cause:** User not authenticated or channel authorization failed

**Fix:**
- Ensure user logged in với Sanctum token
- Check `routes/channels.php` authorization logic
- Verify `user->id === userId`

### Issue 2: React không nhận notification

**Cause:** Event name không khớp

**Fix:**
- Laravel: `broadcastAs('notification.received')`
- React: `listen('notification.received')` (NO DOT!)
- NOT: `listen('.notification.received')` (WITH DOT!)

### Issue 3: Soketi connection refused

**Cause:** Soketi không chạy

**Fix:**
```bash
ps aux | grep soketi
soketi start
```

### Issue 4: Queue job không chạy

**Cause:** Queue worker không chạy

**Fix:**
```bash
php artisan queue:work
# hoặc
php artisan queue:restart
```

## 📝 Environment Variables

Cần có trong `.env`:

```bash
BROADCAST_DRIVER=pusher
QUEUE_CONNECTION=database

PUSHER_APP_ID=laravel-app
PUSHER_APP_KEY=app-key
PUSHER_APP_SECRET=app-secret
PUSHER_HOST=127.0.0.1
PUSHER_PORT=6001
PUSHER_SCHEME=http
PUSHER_ENCRYPTED=false
PUSHER_USE_TLS=false
PUSHER_APP_CLUSTER=mt1

NOTIFICATION_API_KEY=fzzNfwshOt
```

## 🧪 Testing Checklist

- [ ] Soketi running: `ps aux | grep soketi`
- [ ] Queue worker running: `ps aux | grep queue:work`
- [ ] Config cleared: `php artisan config:clear`
- [ ] Routes cleared: `php artisan route:clear`
- [ ] React subscribed to private channel
- [ ] React listen event name: `'notification.received'`
- [ ] User authenticated with Sanctum token
- [ ] Test broadcast command: `php artisan notification:test 1`
- [ ] Check React console for notification
- [ ] Check Laravel log: `storage/logs/notification/YYYY-MM-DD.log`

## 🎯 Final Commands

```bash
# 1. Clear all caches
php artisan config:clear && php artisan route:clear && php artisan cache:clear

# 2. Restart queue
php artisan queue:restart

# 3. Test broadcast
php artisan notification:test 1

# 4. Watch logs
tail -f storage/logs/notification/$(date +%Y-%m-%d).log

# 5. Check Soketi
ps aux | grep soketi
```

## ✅ Success Indicators

React console should show:

```
✅ WebSocket connected successfully
🔌 Socket ID: xxxxx
🔌 Subscribing to private channel: notification.user.1
✅ Channel subscribed successfully
🔔 New notification: {title: "...", body: "...", notification_id: ...}
✅ Notification added to state
```

UI should display notification với badge count!

## 🎉 System Ready!

Hệ thống realtime notification đã sẵn sàng hoạt động với:
- ✅ Multi-tenant support
- ✅ Private channel authentication
- ✅ Async queue processing
- ✅ Dedicated logging
- ✅ Type-based filtering (all/phone/email)
- ✅ Chunk processing (100 users/batch)

---

**Created:** 2025-10-10
**Soketi Version:** 1.0
**Laravel Version:** 11
**Status:** ✅ Complete


