# 🔔 Realtime Notification System với Soketi - Hướng dẫn cài đặt

## ✅ Đã hoàn thành

1. ✅ Cài đặt Soketi server
2. ✅ Cài đặt pusher-php-server package
3. ✅ Tạo migrations cho `notification_receives` và `notification_receive_users`
4. ✅ Tạo Models với UsesTenantConnection trait
5. ✅ Tạo NotificationReceiveController với tenant connection handling
6. ✅ Tạo ProcessNotificationReceiveJob cho async processing
7. ✅ Tạo NewNotificationReceived event cho broadcasting
8. ✅ Cấu hình broadcasting và logging
9. ✅ Thêm API routes

## 📝 Cần cấu hình thủ công

### 1. Cập nhật file `.env`

Thêm các dòng sau vào file `/var/www/html/lms_hocmai/.env`:

```bash
# Broadcasting Configuration (Soketi/Pusher)
BROADCAST_DRIVER=pusher
QUEUE_CONNECTION=database

# Pusher/Soketi Configuration
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 (để xác thực request từ external server)
NOTIFICATION_API_KEY=fzzNfwshOt
```

### 2. Chạy Migrations

```bash
cd /var/www/html/lms_hocmai
php artisan migrate
```

### 3. Khởi động Queue Worker

Queue worker cần chạy để xử lý notification jobs:

```bash
php artisan queue:work --daemon
```

Hoặc sử dụng supervisor để chạy queue worker tự động:

**Tạo file supervisor config: `/etc/supervisor/conf.d/laravel-worker.conf`**

```ini
[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/html/lms_hocmai/artisan queue:work --sleep=3 --tries=3 --max-time=3600
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=www-data
numprocs=2
redirect_stderr=true
stdout_logfile=/var/www/html/lms_hocmai/storage/logs/worker.log
stopwaitsecs=3600
```

Sau đó reload supervisor:
```bash
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start laravel-worker:*
```

### 4. Khởi động Soketi Server

Soketi đã được cài đặt sẵn. Chạy lệnh sau để khởi động:

```bash
soketi start
```

Hoặc chạy trong background với PM2:

```bash
pm2 start soketi --name soketi-server -- start
pm2 save
pm2 startup
```

Hoặc với systemd:

**Tạo file: `/etc/systemd/system/soketi.service`**

```ini
[Unit]
Description=Soketi WebSocket Server
After=network.target

[Service]
Type=simple
User=www-data
WorkingDirectory=/var/www/html/lms_hocmai
ExecStart=/usr/bin/soketi start
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target
```

Enable và start service:
```bash
sudo systemctl daemon-reload
sudo systemctl enable soketi
sudo systemctl start soketi
sudo systemctl status soketi
```

## 🧪 Test Notification System

### 1. Test với curl

```bash
curl --location --request POST 'http://lms.hocmai.net/api/notification/receive' \
--header 'Content-Type: application/json' \
--data '{
  "code": "BOS",
  "key": "fzzNfwshOt",
  "data": {
    "type": 1,
    "data": [],
    "lms_id": [],
    "title": "Test Soketi notification",
    "body": "<p>Hello từ Soketi</p>"
  },
  "message_id": 1
}'
```

### 2. Response mẫu

```json
{
  "status": "ok",
  "message": "Notification received successfully",
  "notification_id": 1,
  "students_count": 150,
  "students": [
    {
      "id": 1,
      "name": "Nguyễn Văn A",
      "email": "nguyenvana@example.com",
      "phone": "0987654321"
    }
  ]
}
```

## 📡 API Endpoints

### 1. Nhận notification từ external server (Public)
**POST** `/api/notification/receive`

**Request Body:**
```json
{
  "code": "BOS",
  "key": "fzzNfwshOt",
  "data": {
    "type": 1,
    "data": [],
    "lms_id": [],
    "title": "Test notification",
    "body": "<p>Test content</p>"
  },
  "message_id": 1
}
```

**Type values:**
- `1`: Tất cả students
- `2`: Filter theo phone (cần cung cấp array phones trong `data.data`)
- `3`: Filter theo email (cần cung cấp array emails trong `data.data`)

### 2. Lấy notifications của user (Authenticated)
**GET** `/api/notification/user?user_id={user_id}`

**Headers:**
```
Authorization: Bearer {token}
```

### 3. Đánh dấu notification đã đọc (Authenticated)
**POST** `/api/notification/{id}/read`

**Headers:**
```
Authorization: Bearer {token}
```

### 4. Lấy số lượng notification chưa đọc (Authenticated)
**GET** `/api/notification/unread/count?user_id={user_id}`

**Headers:**
```
Authorization: Bearer {token}
```

## 🔍 Kiểm tra Logs

Logs được lưu riêng tại:
```
/var/www/html/lms_hocmai/storage/logs/notification/YYYY-MM-DD.log
```

Xem logs realtime:
```bash
tail -f /var/www/html/lms_hocmai/storage/logs/notification/$(date +%Y-%m-%d).log
```

## 📊 Database Schema

### Table: `notification_receives`
| Column | Type | Description |
|--------|------|-------------|
| id | increments | Primary key |
| tenant_code | string | Tenant code (BOS, ICC, etc) |
| message_id | integer | Message ID từ external server |
| title | string | Tiêu đề thông báo |
| body | text | Nội dung thông báo |
| data | json | Full data payload |
| created_at | timestamp | |
| updated_at | timestamp | |

### Table: `notification_receive_users`
| Column | Type | Description |
|--------|------|-------------|
| id | increments | Primary key |
| notification_receive_id | integer | FK to notification_receives |
| user_id | integer | Student ID |
| name | string | Student name |
| email | string | Student email |
| phone | string | Student phone |
| read_at | timestamp (nullable) | Thời gian đọc |
| created_at | timestamp | |
| updated_at | timestamp | |

## 🎯 React/Next.js Client Integration

### Install Pusher JS
```bash
npm install pusher-js
# hoặc
yarn add pusher-js
```

### Example React Hook
```javascript
import { useEffect, useState } from 'react';
import Pusher from 'pusher-js';

export const useNotifications = (userId) => {
  const [notifications, setNotifications] = useState([]);
  const [unreadCount, setUnreadCount] = useState(0);

  useEffect(() => {
    // Initialize Pusher
    const pusher = new Pusher('app-key', {
      cluster: 'mt1',
      wsHost: '127.0.0.1',
      wsPort: 6001,
      forceTLS: false,
      disableStats: true,
      enabledTransports: ['ws', 'wss'],
    });

    // Subscribe to user channel
    const channel = pusher.subscribe(`notification.user.${userId}`);

    // Listen for notification events
    channel.bind('notification.received', (data) => {
      console.log('New notification:', data);
      
      // Add to notifications list
      setNotifications((prev) => [data, ...prev]);
      
      // Increment unread count
      setUnreadCount((prev) => prev + 1);
      
      // Show toast notification (optional)
      // toast.success(data.title);
    });

    // Cleanup
    return () => {
      channel.unbind_all();
      channel.unsubscribe();
      pusher.disconnect();
    };
  }, [userId]);

  return { notifications, unreadCount };
};
```

### Example Usage in Component
```javascript
import { useNotifications } from './hooks/useNotifications';

function NotificationBell() {
  const { notifications, unreadCount } = useNotifications(123); // user_id = 123

  return (
    <div className="notification-bell">
      <button>
        🔔
        {unreadCount > 0 && (
          <span className="badge">{unreadCount}</span>
        )}
      </button>
      
      <div className="notification-list">
        {notifications.map((notif) => (
          <div key={notif.notification_id}>
            <h4>{notif.title}</h4>
            <div dangerouslySetInnerHTML={{ __html: notif.body }} />
            <small>{notif.created_at}</small>
          </div>
        ))}
      </div>
    </div>
  );
}
```

## 🔧 Troubleshooting

### 1. Soketi không kết nối được
- Kiểm tra Soketi đang chạy: `ps aux | grep soketi`
- Kiểm tra port 6001 đang mở: `netstat -tulpn | grep 6001`
- Kiểm tra firewall: `sudo ufw status`

### 2. Job không chạy
- Kiểm tra queue worker: `ps aux | grep queue:work`
- Kiểm tra failed jobs: `php artisan queue:failed`
- Retry failed jobs: `php artisan queue:retry all`

### 3. Broadcasting không hoạt động
- Kiểm tra `.env` có `BROADCAST_DRIVER=pusher`
- Clear config cache: `php artisan config:clear`
- Kiểm tra Pusher credentials trong `.env`

### 4. Logs không ghi
- Kiểm tra permission: `sudo chown -R www-data:www-data storage/logs`
- Tạo thư mục nếu chưa có: `mkdir -p storage/logs/notification`

## 📚 Tài liệu tham khảo

- [Soketi Documentation](https://docs.soketi.app/)
- [Laravel Broadcasting](https://laravel.com/docs/11.x/broadcasting)
- [Pusher JS Client](https://pusher.com/docs/channels/using_channels/client-api)
- [Laravel Queues](https://laravel.com/docs/11.x/queues)

## ✅ Checklist hoàn thành

- [ ] Đã thêm config vào `.env`
- [ ] Đã chạy `php artisan migrate`
- [ ] Đã khởi động Soketi server
- [ ] Đã khởi động queue worker
- [ ] Đã test API với curl
- [ ] Đã integrate với React/Next.js client
- [ ] Đã setup supervisor cho queue worker
- [ ] Đã setup systemd/pm2 cho Soketi

---

**🎉 Hệ thống Realtime Notification đã sẵn sàng!**

