[PHP, JavaScript] Браузерные Push-уведомления на Javascript и PHP
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
ПредисловиеВ попытке найти годную статью по настройке уведомлений в браузере, я получал только статьи где в основном описывалось использование совместно с Firebase, но мне такой вариант не особенно то и подходил.В данной статье не будут "размусолены" принципы работы и тонкости Push уведомлений, только код, только хардкор.
Важные замечанияPush-уведомления работают только с HTTPS.
К слову, в добавок с HTTPS должен присутствовать валидный SSL сертификат, подойдет и Let's EncryptДля разработки подойдёт localhost. Проблем возникнуть не должно, но если все же возникли данная статья поможет разобраться с ними.
Да будет кодАвторизация (VAPID)Для начала стоит установить библиотеку WebPush в ваш php проект:
$ composer require minishlink/web-push
Далее для авторизации вашего сервера браузером (VAPID), вам нужно сгенерироватьпубличный и приватный ssh ключи. Данные ключи понадобятся как на сервере, так и на клиенте (за исключением того что на клиенте нужен лишь публичный).Чтобы сгенерировать несжатый публичный и приватный ключ, закодированный в Base64, введите следующее в свой Linux bash:
$ openssl ecparam -genkey -name prime256v1 -out private_key.pem
$ openssl ec -in private_key.pem -pubout -outform DER|tail -c 65|base64|tr -d '=' |tr '/+' '_-' >> public_key.txt
$ openssl ec -in private_key.pem -outform DER|tail -c +8|head -c 32|base64|tr -d '=' |tr '/+' '_-' >> private_key.txt
Так же автор библиотеки предоставляет генерацию vapid ключей с помощью встроенного метода:
$vapidKeysInBase64 = VAPID::createVapidKeys();
Подписка Этап 1 (JS)В начале стоит проверить наличие поддержки ServiceWorker, PushManager, а так же showNotification в браузере: app.js
function checkNotificationSupported() {
return new Promise((fulfilled, reject) => {
if (!('serviceWorker' in navigator)) {
reject(new Error('Service workers are not supported by this browser'));
return;
}
if (!('PushManager' in window)) {
reject(new Error('Push notifications are not supported by this browser'));
return;
}
if (!('showNotification' in ServiceWorkerRegistration.prototype)) {
reject(new Error('Notifications are not supported by this browser'));
return;
}
fulfilled();
})
}
Создаем файл sw.js и далее регистрируем его:app.js
navigator.serviceWorker.register('sw.js').then(() => {
console.log('[SW] Service worker has been registered');
}, e => {
console.error('[SW] Service worker registration failed', e);
}
);
Так же нам понадобится функция для проверки состояния подписки:app.js
function checkNotificationPermission() {
return new Promise((fulfilled, reject) => {
if (Notification.permission === 'denied') {
return reject(new Error('Push messages are blocked.'));
}
if (Notification.permission === 'granted') {
return fulfilled();
}
if (Notification.permission === 'default') {
return Notification.requestPermission().then(result => {
if (result !== 'granted') {
reject(new Error('Bad permission result'));
} else {
fulfilled();
}
});
}
return reject(new Error('Unknown permission'));
});
}
С сервера нам нужно получить публичный ssh ключ сгенерированный выше:
<script>
window.applicationServerKey = '<?= $yourPublicKeyFromServer ?>'
</script>
Далее на ваше усмотрение, вешаем вызов окна на разрешение получение уведомлений. В моем примере человек через 10 секунд получает предложение подписаться.app.js
document.addEventListener('DOMContentLoaded', documentLoadHandler);
function documentLoadHandler() {
checkNotificationSupported()
.then(() => {
setTimeout(() => {
serviceWorkerRegistration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(window.applicationServerKey),
})
.then(successSubscriptionHandler, errorSubscriptionHandler)
}, 10000);
},
console.error
);
}
function urlBase64ToUint8Array(base64String) {
const padding = '='.repeat((4 - (base64String.length % 4)) % 4);
const base64 = (base64String + padding).replace(/\-/g, '+').replace(/_/g, '/');
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
function errorSubscriptionHandler(err) {
if (Notification.permission === 'denied') {
console.warn('Notifications are denied by the user.');
} else {
console.error('Impossible to subscribe to push notifications', err);
}
}
Далее если процесс получения разрешения подписки прошел успешно вызываем функцию successSubscriptionHandlerФормируем данные пользователя для дальнейшей отправки уведомлений.app.js
function successSubscriptionHandler(subscriptionData) {
const key = subscription.getKey('p256dh');
const token = subscription.getKey('auth');
const contentEncoding = (PushManager.supportedContentEncodings || ['aesgcm'])[0];
const body = new FormData();
body.set('endpoint', subscription.endpoint);
body.set('publicKey', key ? btoa(String.fromCharCode.apply(null, new Uint8Array(key))) : null);
body.set('authToken', token ? btoa(String.fromCharCode.apply(null, new Uint8Array(token))) : null);
body.set('contentEncoding', contentEncoding);
return fetch('src/push_subscription.php', {
method,
body,
}).then(() => subscription);
}
Так же нам нужно сформировать отправляемое уведомление
Вы можете манипулировать данными уведомления при помощи Post Message API
self.addEventListener('push', function (event) {
if (!(self.Notification && self.Notification.permission === 'granted')) {
return;
}
const sendNotification = body => {
const title = "Заголовок уведомления";
return self.registration.showNotification(title, {
body,
});
};
if (event.data) {
const message = event.data.text();
event.waitUntil(sendNotification(message));
}
});
Этап 2 (PHP)
Необходим php версии 7+
Далее в файле subscribeUserToPushNotifications на который мы сделали запрос с фронта при получении разрешения на подписку, мы формируем данные пользователя subscribeUserToPushNotifications.php
<?php
$subscription = $_POST;
if (!isset($subscription['endpoint'])) {
echo 'Error: not a subscription';
return;
}
// save subscription from => $subscription
На данном этапе мы можем записать данные пользователя в Базу данных (Ну или что у вас там), для последующей отправки уведомлений.Непосредственно сама отправка происходит следующим образомДостаем юзера с места его сохранения, и далее создаем объект подписчика:pushNotificationToClient.php
<?php
use Minishlink\WebPush\WebPush;
use Minishlink\WebPush\Subscription;
$subscription = Subscription::create($subscriptionData);
Далее формируем VAPID для авторизации:pushNotificationToClient.php
<?php
$auth = array(
'VAPID' => array(
'subject' => 'https://your-project-domain.com',
'publicKey' => file_get_contents(__DIR__ . '/your_project/keys/public_key.txt'),
'privateKey' => file_get_contents(__DIR__ . '/your_project/keys/private_key.txt'),
)
);
После того как сформировали нужные данные, создаем новый объект WebPush:pushNotificationToClient.php
<?php
$webPush = new WebPush($auth);
Ура! Наконец мы можем отправить запрос на отправку Push уведомления
<?php
$report = $webPush->sendOneNotification(
$subscription,
"Тело пуш уведомления, оно поступило в тело sw.js"
);
Важное замечаниеДля отправки уведомлений в итерации, стоит использовать функцию, с теми же параметрами, что и в функции выше:$webPush->queueNotification
Полезные источники
- О технологии push
- О WebPush от хабровчанина
- Библиотека WebPush
- Пример использования от разработчика библиотеки
===========
Источник:
habr.com
===========
Похожие новости:
- [JavaScript, VueJS, TypeScript] Из Vue 2 на Vue 3 – Migration Helper
- [JavaScript] @teqfw/di
- [Разработка веб-сайтов, PHP, Разработка мобильных приложений, Управление разработкой] Кто, где, когда: система компонентов для разделения зон ответственности команды
- [Информационная безопасность, Криптография, JavaScript, Node.JS, Криптовалюты] Поиск коллизий в SHA-256 на платформе Node.js при помощи Bitcoin Hasher
- [JavaScript, ReactJS] react-router: Три метода рендеринга маршрутов (компонентный, рендеринговый и дочерний) (перевод)
- [JavaScript, Веб-аналитика, Интернет-маркетинг, Повышение конверсии, Поисковая оптимизация] Как настроить Facebook Conversion API с помощью GTM Server Side
- [Разработка веб-сайтов, PHP, Symfony, Yii, Laravel] Зачем нужен static при объявлении анонимных функций?
- [Работа с видео, JavaScript, Программирование, Видеоконференцсвязь] Streaming multiple RTSP IP cameras on YouTube and/or Facebook
- [JavaScript, Программирование, Карьера в IT-индустрии] Публичное техническое собеседование на мидл фронтенд-разработчика: 15 июня в 19.00
- [Разработка веб-сайтов, Работа с видео, Программирование, Видеоконференцсвязь] Стриминг множества RTSP IP камер на YouTube и/или Facebook
Теги для поиска: #_php, #_javascript, #_push, #_pushnotification, #_pushuvedomlenija (push-уведомления), #_webpush, #_php, #_javascript, #_serviceworker, #_php, #_javascript
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 18:48
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
ПредисловиеВ попытке найти годную статью по настройке уведомлений в браузере, я получал только статьи где в основном описывалось использование совместно с Firebase, но мне такой вариант не особенно то и подходил.В данной статье не будут "размусолены" принципы работы и тонкости Push уведомлений, только код, только хардкор. Важные замечанияPush-уведомления работают только с HTTPS.
К слову, в добавок с HTTPS должен присутствовать валидный SSL сертификат, подойдет и Let's EncryptДля разработки подойдёт localhost. Проблем возникнуть не должно, но если все же возникли данная статья поможет разобраться с ними. $ composer require minishlink/web-push
$ openssl ecparam -genkey -name prime256v1 -out private_key.pem
$ openssl ec -in private_key.pem -pubout -outform DER|tail -c 65|base64|tr -d '=' |tr '/+' '_-' >> public_key.txt $ openssl ec -in private_key.pem -outform DER|tail -c +8|head -c 32|base64|tr -d '=' |tr '/+' '_-' >> private_key.txt $vapidKeysInBase64 = VAPID::createVapidKeys();
function checkNotificationSupported() {
return new Promise((fulfilled, reject) => { if (!('serviceWorker' in navigator)) { reject(new Error('Service workers are not supported by this browser')); return; } if (!('PushManager' in window)) { reject(new Error('Push notifications are not supported by this browser')); return; } if (!('showNotification' in ServiceWorkerRegistration.prototype)) { reject(new Error('Notifications are not supported by this browser')); return; } fulfilled(); }) } navigator.serviceWorker.register('sw.js').then(() => {
console.log('[SW] Service worker has been registered'); }, e => { console.error('[SW] Service worker registration failed', e); } ); function checkNotificationPermission() {
return new Promise((fulfilled, reject) => { if (Notification.permission === 'denied') { return reject(new Error('Push messages are blocked.')); } if (Notification.permission === 'granted') { return fulfilled(); } if (Notification.permission === 'default') { return Notification.requestPermission().then(result => { if (result !== 'granted') { reject(new Error('Bad permission result')); } else { fulfilled(); } }); } return reject(new Error('Unknown permission')); }); } <script>
window.applicationServerKey = '<?= $yourPublicKeyFromServer ?>' </script> document.addEventListener('DOMContentLoaded', documentLoadHandler);
function documentLoadHandler() { checkNotificationSupported() .then(() => { setTimeout(() => { serviceWorkerRegistration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: urlBase64ToUint8Array(window.applicationServerKey), }) .then(successSubscriptionHandler, errorSubscriptionHandler) }, 10000); }, console.error ); } function urlBase64ToUint8Array(base64String) { const padding = '='.repeat((4 - (base64String.length % 4)) % 4); const base64 = (base64String + padding).replace(/\-/g, '+').replace(/_/g, '/'); const rawData = window.atob(base64); const outputArray = new Uint8Array(rawData.length); for (let i = 0; i < rawData.length; ++i) { outputArray[i] = rawData.charCodeAt(i); } return outputArray; } function errorSubscriptionHandler(err) { if (Notification.permission === 'denied') { console.warn('Notifications are denied by the user.'); } else { console.error('Impossible to subscribe to push notifications', err); } } function successSubscriptionHandler(subscriptionData) {
const key = subscription.getKey('p256dh'); const token = subscription.getKey('auth'); const contentEncoding = (PushManager.supportedContentEncodings || ['aesgcm'])[0]; const body = new FormData(); body.set('endpoint', subscription.endpoint); body.set('publicKey', key ? btoa(String.fromCharCode.apply(null, new Uint8Array(key))) : null); body.set('authToken', token ? btoa(String.fromCharCode.apply(null, new Uint8Array(token))) : null); body.set('contentEncoding', contentEncoding); return fetch('src/push_subscription.php', { method, body, }).then(() => subscription); } Вы можете манипулировать данными уведомления при помощи Post Message API
self.addEventListener('push', function (event) {
if (!(self.Notification && self.Notification.permission === 'granted')) { return; } const sendNotification = body => { const title = "Заголовок уведомления"; return self.registration.showNotification(title, { body, }); }; if (event.data) { const message = event.data.text(); event.waitUntil(sendNotification(message)); } }); Необходим php версии 7+
<?php
$subscription = $_POST; if (!isset($subscription['endpoint'])) { echo 'Error: not a subscription'; return; } // save subscription from => $subscription <?php
use Minishlink\WebPush\WebPush; use Minishlink\WebPush\Subscription; $subscription = Subscription::create($subscriptionData); <?php
$auth = array( 'VAPID' => array( 'subject' => 'https://your-project-domain.com', 'publicKey' => file_get_contents(__DIR__ . '/your_project/keys/public_key.txt'), 'privateKey' => file_get_contents(__DIR__ . '/your_project/keys/private_key.txt'), ) ); <?php
$webPush = new WebPush($auth); <?php
$report = $webPush->sendOneNotification( $subscription, "Тело пуш уведомления, оно поступило в тело sw.js" ); Важное замечаниеДля отправки уведомлений в итерации, стоит использовать функцию, с теми же параметрами, что и в функции выше:$webPush->queueNotification
=========== Источник: habr.com =========== Похожие новости:
|
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 18:48
Часовой пояс: UTC + 5