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

10 KiB
Raw Blame History

Исправление Push-уведомлений

Проблема

Push-уведомления не работали по следующим причинам:

1. Кнопка "Включить уведомления" не зависела от состояния разрешения

До:

{!pushEnabled && 'Notification' in window && (
  <button onClick={handleEnablePush}>Включить уведомления</button>
)}

Проблема: Кнопка показывалась, даже если пользователь заблокировал уведомления (Notification.permission === 'denied'). Клик по ней приводил к ошибке, так как браузер не давал повторно запросить разрешение.

После:

{!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 при сборке

До:

// vite.config.ts
export default defineConfig({
  plugins: [react()],
})

Проблема: Файл public/service-worker.js не копировался автоматически в dist/ при сборке, что приводило к 404 ошибке при регистрации.

После:

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. Недостаточная диагностика ошибок

До:

console.error('Ошибка подключения Push-уведомлений:', error);

После:

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)

self.addEventListener('push', (event) => {
  const data = event.data.json();
  self.registration.showNotification(data.title, {
    body: data.body,
    icon: data.icon || '/favicon.svg',
    ...
  });
});

Проверка работы

1. Проверка VAPID ключей

curl https://ospab.host:5000/api/notifications/vapid-key \
  -H "Authorization: Bearer YOUR_TOKEN"

Должен вернуть:

{
  "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. Проверка в базе данных

SELECT * FROM PushSubscription WHERE userId = YOUR_USER_ID;

Должна быть запись с endpoint, p256dh, auth.

5. Тестовая отправка

Можно создать тестовую отправку через backend:

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/

Деплой

# 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