260 lines
10 KiB
Markdown
260 lines
10 KiB
Markdown
# Исправление Push-уведомлений
|
||
|
||
## Проблема
|
||
|
||
Push-уведомления не работали по следующим причинам:
|
||
|
||
### 1. **Кнопка "Включить уведомления" не зависела от состояния разрешения**
|
||
|
||
#### До:
|
||
```tsx
|
||
{!pushEnabled && 'Notification' in window && (
|
||
<button onClick={handleEnablePush}>Включить уведомления</button>
|
||
)}
|
||
```
|
||
|
||
**Проблема:** Кнопка показывалась, даже если пользователь заблокировал уведомления (`Notification.permission === 'denied'`). Клик по ней приводил к ошибке, так как браузер не давал повторно запросить разрешение.
|
||
|
||
#### После:
|
||
```tsx
|
||
{!pushEnabled && 'Notification' in window && pushPermission !== 'denied' && (
|
||
<button onClick={handleEnablePush}>Включить уведомления</button>
|
||
)}
|
||
|
||
{pushPermission === 'denied' && (
|
||
<div className="alert alert-warning">
|
||
Push-уведомления заблокированы. Разрешите их в настройках браузера.
|
||
</div>
|
||
)}
|
||
```
|
||
|
||
**Решение:**
|
||
- Кнопка показывается только когда `pushPermission === 'default'` (не запрашивалось)
|
||
- Если `pushPermission === 'denied'`, показывается предупреждение с инструкцией
|
||
|
||
### 2. **Service Worker мог не копироваться в dist при сборке**
|
||
|
||
#### До:
|
||
```typescript
|
||
// vite.config.ts
|
||
export default defineConfig({
|
||
plugins: [react()],
|
||
})
|
||
```
|
||
|
||
**Проблема:** Файл `public/service-worker.js` не копировался автоматически в `dist/` при сборке, что приводило к 404 ошибке при регистрации.
|
||
|
||
#### После:
|
||
```typescript
|
||
export default defineConfig({
|
||
plugins: [
|
||
react(),
|
||
{
|
||
name: 'copy-service-worker',
|
||
writeBundle() {
|
||
copyFileSync(
|
||
resolve(__dirname, 'public/service-worker.js'),
|
||
resolve(__dirname, 'dist/service-worker.js')
|
||
)
|
||
console.log('✅ Service worker скопирован в dist/')
|
||
}
|
||
}
|
||
],
|
||
})
|
||
```
|
||
|
||
**Решение:** Добавлен плагин Vite, который автоматически копирует `service-worker.js` в корень `dist/` при каждой сборке.
|
||
|
||
### 3. **Недостаточная диагностика ошибок**
|
||
|
||
#### До:
|
||
```typescript
|
||
console.error('Ошибка подключения Push-уведомлений:', error);
|
||
```
|
||
|
||
#### После:
|
||
```typescript
|
||
console.log('📝 Регистрируем Service Worker...');
|
||
const registration = await navigator.serviceWorker.register('/service-worker.js');
|
||
console.log('✅ Service Worker зарегистрирован:', registration);
|
||
|
||
console.log('📝 Получаем VAPID ключ...');
|
||
const vapidPublicKey = await getVapidKey();
|
||
console.log('✅ VAPID ключ получен:', vapidPublicKey.substring(0, 20) + '...');
|
||
|
||
console.log('📝 Создаём Push подписку...');
|
||
const subscription = await registration.pushManager.subscribe({...});
|
||
console.log('✅ Push подписка создана:', subscription.endpoint);
|
||
```
|
||
|
||
**Решение:** Добавлены подробные логи на каждом этапе подключения Push-уведомлений для быстрой диагностики проблем.
|
||
|
||
## Архитектура Push-уведомлений
|
||
|
||
### Frontend (`notificationService.ts`)
|
||
```
|
||
1. Notification.requestPermission() → Запрос разрешения
|
||
2. navigator.serviceWorker.register() → Регистрация SW
|
||
3. GET /api/notifications/vapid-key → Получение публичного ключа
|
||
4. registration.pushManager.subscribe() → Создание подписки
|
||
5. POST /api/notifications/subscribe-push → Отправка подписки на сервер
|
||
```
|
||
|
||
### Backend
|
||
```
|
||
1. GET /api/notifications/vapid-key → Возвращает VAPID_PUBLIC_KEY из .env
|
||
2. POST /api/notifications/subscribe-push → Сохраняет подписку в PushSubscription
|
||
3. Отправка уведомлений → webpush.sendNotification() для каждой подписки
|
||
```
|
||
|
||
### Service Worker (`public/service-worker.js`)
|
||
```javascript
|
||
self.addEventListener('push', (event) => {
|
||
const data = event.data.json();
|
||
self.registration.showNotification(data.title, {
|
||
body: data.body,
|
||
icon: data.icon || '/favicon.svg',
|
||
...
|
||
});
|
||
});
|
||
```
|
||
|
||
## Проверка работы
|
||
|
||
### 1. Проверка VAPID ключей
|
||
```bash
|
||
curl https://ospab.host:5000/api/notifications/vapid-key \
|
||
-H "Authorization: Bearer YOUR_TOKEN"
|
||
```
|
||
|
||
Должен вернуть:
|
||
```json
|
||
{
|
||
"success": true,
|
||
"publicKey": "BPtLNi3TY1ifUWTkgZrhxoEH6ihDgknFcgzc3xzFQg07PeuJ1TsJDQZqA32VqlxUo03g_mG0yKCKqADb4r5fnsM"
|
||
}
|
||
```
|
||
|
||
### 2. Проверка Service Worker
|
||
Откройте `https://ospab.host` → DevTools → Application → Service Workers
|
||
|
||
Должен быть зарегистрирован: `/service-worker.js` со статусом **Activated**
|
||
|
||
### 3. Проверка подписки
|
||
После нажатия "Включить уведомления" в консоли должны появиться:
|
||
```
|
||
📝 Запрашиваем разрешение на уведомления...
|
||
📝 Результат запроса разрешения: granted
|
||
📝 Регистрируем Service Worker...
|
||
✅ Service Worker зарегистрирован: ServiceWorkerRegistration {...}
|
||
📝 Ожидаем готовности Service Worker...
|
||
✅ Service Worker готов
|
||
📝 Получаем VAPID ключ...
|
||
✅ VAPID ключ получен: BPtLNi3TY1ifUWTk...
|
||
📝 Создаём Push подписку...
|
||
✅ Push подписка создана: https://fcm.googleapis.com/fcm/send/...
|
||
📝 Отправляем подписку на сервер...
|
||
✅ Push-уведомления успешно подключены
|
||
```
|
||
|
||
### 4. Проверка в базе данных
|
||
```sql
|
||
SELECT * FROM PushSubscription WHERE userId = YOUR_USER_ID;
|
||
```
|
||
|
||
Должна быть запись с endpoint, p256dh, auth.
|
||
|
||
### 5. Тестовая отправка
|
||
Можно создать тестовую отправку через backend:
|
||
```typescript
|
||
import { sendPushNotification } from './modules/notification/push.service';
|
||
|
||
await sendPushNotification(userId, {
|
||
title: 'Тестовое уведомление',
|
||
body: 'Push-уведомления работают!',
|
||
icon: '/logo192.png'
|
||
});
|
||
```
|
||
|
||
## Состояния Notification.permission
|
||
|
||
| Состояние | Описание | UI |
|
||
|-----------|----------|-----|
|
||
| `default` | Разрешение не запрашивалось | Показывается синяя кнопка "Включить уведомления" |
|
||
| `granted` | Разрешение получено | Кнопка скрыта, уведомления работают |
|
||
| `denied` | Пользователь заблокировал | Показывается красное предупреждение с инструкцией |
|
||
|
||
## Разблокировка в браузерах
|
||
|
||
### Chrome/Edge
|
||
1. Нажмите на иконку 🔒 (замок) слева от адресной строки
|
||
2. Найдите "Уведомления"
|
||
3. Выберите "Разрешить"
|
||
4. Обновите страницу
|
||
|
||
### Firefox
|
||
1. Откройте Настройки → Приватность и защита
|
||
2. Прокрутите до раздела "Разрешения"
|
||
3. Нажмите "Настройки" рядом с "Уведомления"
|
||
4. Найдите `ospab.host` и измените на "Разрешить"
|
||
|
||
### Safari
|
||
1. Safari → Настройки → Веб-сайты
|
||
2. Выберите "Уведомления"
|
||
3. Найдите `ospab.host` и выберите "Разрешить"
|
||
|
||
## Файлы изменены
|
||
|
||
1. ✅ `frontend/src/pages/dashboard/notifications.tsx`
|
||
- Добавлено состояние `pushPermission`
|
||
- Условный рендеринг кнопки/предупреждения
|
||
- Обновление состояния после запроса
|
||
|
||
2. ✅ `frontend/src/services/notificationService.ts`
|
||
- Добавлены подробные логи в `requestPushPermission()`
|
||
- Эмодзи-маркеры для быстрого поиска в консоли
|
||
|
||
3. ✅ `frontend/vite.config.ts`
|
||
- Плагин копирования `service-worker.js` в `dist/`
|
||
|
||
## Деплой
|
||
|
||
```bash
|
||
# Frontend
|
||
cd frontend
|
||
npm run build
|
||
# Скопируйте dist/ на production сервер
|
||
|
||
# Backend (если были изменения)
|
||
cd backend
|
||
npm run build
|
||
pm2 restart ospab-backend
|
||
```
|
||
|
||
## Возможные проблемы
|
||
|
||
### Service Worker не регистрируется
|
||
- **Проверка:** DevTools → Application → Service Workers
|
||
- **Причина:** Файл `service-worker.js` не доступен по адресу `https://ospab.host/service-worker.js`
|
||
- **Решение:** Убедитесь, что файл скопирован в корень `dist/` и доступен через nginx
|
||
|
||
### 403 Forbidden при запросе VAPID ключа
|
||
- **Проверка:** Network → `/api/notifications/vapid-key` → Response
|
||
- **Причина:** Не передаётся токен авторизации
|
||
- **Решение:** Проверьте, что в localStorage есть `access_token`
|
||
|
||
### Push-уведомления не приходят
|
||
- **Проверка:** Console → Ошибки от `webpush.sendNotification()`
|
||
- **Причина:** Неправильные VAPID ключи или подписка устарела
|
||
- **Решение:** Перегенерируйте VAPID ключи (`npx web-push generate-vapid-keys`) и переподпишитесь
|
||
|
||
### Подписка создаётся, но не сохраняется в БД
|
||
- **Проверка:** Console → Network → `/api/notifications/subscribe-push` → Response
|
||
- **Причина:** Ошибка на backend при сохранении в Prisma
|
||
- **Решение:** Проверьте логи backend (`pm2 logs ospab-backend`)
|
||
|
||
---
|
||
|
||
**Статус:** ✅ Исправлено и готово к тестированию
|
||
**Дата:** 2025-01-20
|