Push-уведомления являются отличной альтернативой уведомлениям по электронной почте, нет необходимости в шаге проверки, улучшается UX и повышается вовлеченность пользователей в приложение.
Требования к push-уведомлениям
- Созданный проект Firebase
- ID проекта, можно найти в настройках проекта → вкладка Общие
- Ключ сервера для отправки push-уведомлений (используется на бэкенде)
- Публичный ключ Vapid, находится на странице Настройки проекта → Cloud Messaging → Сертификаты Web Push (используется на front-end).
- Конфигурация Firebase, находится в Настройки проекта → Общие → Ваши приложения
- Рабочий сервис обмена сообщениями Firebase
- HTTPS-соединение (localhost для локальной разработки)
Вспомогательные функции
getToken
- генерирует уникальный токен для браузера или получает уже сгенерированный токен
- запрашивает разрешение на получение push-уведомлений
- запускает работника службы сообщений Firebase.
Если пользователь блокирует push-уведомления, возникает ошибка FirebaseError
с кодом messaging/permission-blocked
. Если браузер пользователя не поддерживает API, необходимые для использования Firebase SDK, возникает ошибка FirebaseError
с кодом messaging/unsupported-browser
. Токен доступа становится недействительным, когда пользователь вручную блокирует уведомления в настройках браузера.
isSupported
- проверяет, поддерживаются ли все необходимые API для push-уведомлений.
- возвращает
Promise<boolean>
.
Его следует использовать в хуках useEffect
.
import { isSupported } from 'firebase/messaging';
// ...
useEffect(() => {
isSupported()
.then((isAvailable) => {
if (isAvailable) {
// ...
}
})
.catch(console.error);
}, []);
// ...
initializeApp
- должен быть вызван до запуска приложения
import { initializeApp } from 'firebase/app';
import { getMessaging, getToken, isSupported } from 'firebase/messaging';
import { firebaseConfig } from 'constants/config';
export const initializeFirebase = () => initializeApp(firebaseConfig);
export const getTokenForPushNotifications = async () => {
const messaging = getMessaging();
const token = await getToken(messaging, {
vapidKey: process.env.NEXT_PUBLIC_VAPID_KEY,
});
return token;
}
Рабочий орган службы сообщений Firebase
Для обработки фоновых уведомлений необходимо зарегистрировать следующий рабочий сервис. Пользовательский обработчик notificationclick
должен быть реализован до импорта библиотек firebase, нижеприведенная реализация открывает новое окно с заданным URL, если оно еще не открыто. Firebase автоматически проверяет наличие рабочих служб в /firebase-messaging-sw.js
, поэтому он должен быть общедоступен.
// /firebase-messaging-sw.js
/* eslint-disable no-unused-vars */
self.addEventListener("notificationclick", (event) => {
event.notification.close();
const DEFAULT_URL = "<URL>";
const url =
event.notification?.data?.FCM_MSG?.notification?.click_action ||
DEFAULT_URL;
event.waitUntil(
clients.matchAll({ type: "window" }).then((clientsArray) => {
const hadWindowToFocus = clientsArray.some((windowClient) =>
windowClient.url === url ? (windowClient.focus(), true) : false
);
if (!hadWindowToFocus)
clients
.openWindow(url)
.then((windowClient) => (windowClient ? windowClient.focus() : null));
})
);
});
let messaging = null;
try {
if (typeof importScripts === "function") {
importScripts("https://www.gstatic.com/firebasejs/8.10.0/firebase-app.js");
importScripts(
"https://www.gstatic.com/firebasejs/8.10.0/firebase-messaging.js"
);
firebase.initializeApp({
apiKey: "xxxxxx",
authDomain: "xxxxxx",
projectId: "xxxxxx",
storageBucket: "xxxxxx",
messagingSenderId: "xxxxxx",
appId: "xxxxxx",
measurementId: "xxxxxx",
});
messaging = firebase.messaging();
}
} catch (error) {
console.error(error);
}
Ключи сервера
Ключ сервера для API v1 может быть получен из JSON-файла ключа учетной записи сервиса, в этом случае JSON-файл должен быть закодирован и сохранен в переменной окружения, чтобы предотвратить раскрытие учетных данных в кодовой базе репозитория. JSON-файл ключа учетной записи сервиса можно загрузить, нажав кнопку Generate new private key на вкладке Project settings → Service accounts. Ключ сервера для устаревшего API можно найти на вкладке Настройки проекта → Cloud Messaging → Cloud Messaging API (Legacy), если он включен.
import * as serviceAccountKey from './serviceAccountKey.json';
const encodedServiceAccountKey = Buffer.from(
JSON.stringify(serviceAccountKey),
).toString('base64');
process.env.SERVICE_ACCOUNT_KEY = encodedServiceAccountKey;
import 'dotenv/config';
import * as googleAuth from 'google-auth-library';
(async () => {
const serviceAccountKeyEncoded = process.env.SERVICE_ACCOUNT_KEY;
const serviceAccountKeyDecoded = JSON.parse(
Buffer.from(serviceAccountKeyEncoded, 'base64').toString('ascii'),
);
const jwt = new googleAuth.JWT(
serviceAccountKeyDecoded.client_email,
null,
serviceAccountKeyDecoded.private_key,
['https://www.googleapis.com/auth/firebase.messaging'],
null,
);
const tokens = await jwt.authorize();
const authorizationHeader = `Bearer ${tokens.access_token}`;
console.log(authorizationHeader);
})();
Ручная отправка push-уведомления
URL иконки должен быть покрыт HTTPS, чтобы иконка могла быть правильно показана в уведомлении.
- наследие
curl --location --request POST 'https://fcm.googleapis.com/fcm/send'
--header 'Authorization: key=<SERVER_KEY>'
--header 'Content-Type: application/json'
--data-raw '{
"notification": {
"title": "Push notifications with Firebase",
"body": "Push notifications with Firebase body",
"click_action": "http://localhost:3000",
"icon": "https://picsum.photos/200"
},
"to": "<TOKEN>"
}'
Ответ содержит ключ success
со значением 1
при успешной отправке push-уведомления. Ответ содержит ключ failure
со значением 1
при неудачной отправке push-уведомления. В этом случае ключ results
представляет собой массив с объектами ошибок, некоторые из имен ошибок: InvalidRegistration
и NotRegistered
.
- API v1
curl --location --request POST 'https://fcm.googleapis.com/v1/projects/<PROJECT_ID>/messages:send'
--header 'Authorization: Bearer <TOKEN_DERIVED_FROM_SERVICE_ACCOUNT_KEY>'
--header 'Content-Type: application/json'
--data-raw '{
"message": {
"notification": {
"title": "Push notifications with Firebase",
"body": "Push notifications with Firebase body"
},
"webpush": {
"fcmOptions": {
"link": "http://localhost:3000"
},
"notification": {
"icon": "https://picsum.photos/200"
}
},
"token": "<TOKEN>"
}
}'
Успешный ответ возвращает JSON с ключом name
, который представляет идентификатор уведомления в формате projects/{project_id}/messages/{message_id}
. Ошибка с кодом 400 возникает, когда тело запроса не соответствует действительности. Ошибка с кодом 401 возникает, если срок действия производного токена истек.