Files
ospab.host/ospabhost/PUSH_NOTIFICATIONS_FIX.md
2025-11-23 14:35:16 +03:00

260 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Исправление 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