Kata pengantar
Dalam upaya menemukan artikel bagus tentang menyiapkan notifikasi di browser, saya hanya menerima artikel yang terutama menjelaskan penggunaan bersama dengan Firebase, tetapi opsi ini tidak terlalu cocok untuk saya.
Dalam artikel ini, prinsip pengoperasian dan seluk-beluk pemberitahuan Push tidak akan "berkeringat", hanya kodenya, hanya hardcorenya.
Catatan penting
Pemberitahuan push hanya berfungsi dengan HTTPS .
Omong-omong, selain HTTPS, sertifikat SSL yang valid harus ada, Let's Encrypt akan melakukannya.
Localhost baik-baik saja untuk pengembangan. Seharusnya tidak ada masalah, tetapi jika Anda muncul, artikel ini akan membantu Anda mengatasinya.
Biar ada kode
Otorisasi (VAPID)
Pertama, ada baiknya menginstal pustaka WebPush ke dalam proyek php Anda:
$ composer require minishlink/web-push
Selanjutnya, untuk mengotorisasi server Anda dengan browser (VAPID), Anda perlu membuat kunci ssh publik dan pribadi. Kunci-kunci ini akan diperlukan baik di server maupun di klien (kecuali bahwa hanya kunci publik yang diperlukan di klien) .
Untuk menghasilkan kunci publik dan pribadi yang disandikan Base64 yang tidak dikompresi, masukkan yang berikut ke dalam bash Linux Anda:
$ 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
Juga, penulis perpustakaan menyediakan pembuatan kunci vapid menggunakan metode bawaan:
$vapidKeysInBase64 = VAPID::createVapidKeys();
Berlangganan
Tahap 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);
}
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"
);
Catatan penting
Untuk mengirim notifikasi dalam iterasi, Anda harus menggunakan fungsi dengan parameter yang sama seperti pada fungsi di atas:
$webPush->queueNotification