kalisio / feathers-webpush

Manage Web Push notifications with FeathersJS

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

feathers-webpush

Latest Release ci Code Climate Test Coverage Download Status

feathers-webpush simplifies the sending of web push notifications in a FeathersJS application.

Principle

The feathers-webpush module provides a simplified way to send web push notifications in a FeathersJS application. It leverages the web-push package to interact with the Web Push protocol.

feathers-webpush principle

Usage

Installation

npm install @kalisio/feathers-webpush --save

or

yarn add @kalisio/feathers-webpush

Example

The provided example illustrates how to setup:

  • a server app

import { feathers } from '@feathersjs/feathers'
import express from '@feathersjs/express'
import socketio from '@feathersjs/socketio'
import { MemoryService } from '@feathersjs/memory'
import { Service } from '../lib/service.js'
import _ from 'lodash'
const port = process.env.PORT || 8081
// Create the Feathers app
const app = express(feathers())
// Configure express
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
// Configure Socket.io
app.configure(socketio({
cors: { origin: '*' },
maxHttpBufferSize: 1e8
}))
// Configure user service
class UserService extends MemoryService {}
app.use('users', new UserService({ multi: ['remove'] }))
// Define the options used to instanciate the webpush service
const options = {
vapidDetails: {
subject: process.env.VAPID_SUBJECT,
publicKey: process.env.VAPID_PUBLIC_KEY,
privateKey: process.env.VAPID_PRIVATE_KEY
},
app: app
}
// Register webpush service on the Feathers application
// /!\ do not forget to declare the custom methods
app.use('push', new Service(options), {
methods: ['create']
})
// Start the server
app.listen(port).then(() => {
console.log(`Feathers server listening on localhost:${port}`)
})

  • a browser client app

<!DOCTYPE html>
<html lang="en">
<head>
<title>feathers-webpush example</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box
}
html {
height: 100%
}
body {
min-height: 100%;
display: flex;
align-items: center
}
#main {
margin:4rem auto;
text-align:center
}
.hide {
display:none
}
button {
border:1px solid #eaeaea;
border-radius:5px;
padding:5px;
color:white
}
#btnSubscribe, #btnPush {
background: green
}
#btnUnsubscribe {
background: red
}
#isSubscribed {
font-size:.8rem;
margin-bottom: 1rem
}
</style>
</head>
<body>
<main id="main">
<h2>Welcome to feathers-webpush</h2>
<div id="isSubscribed"></div>
<button onclick="sendNotification()" id="btnPush" class="hide">Push</button>
<button onclick="subscribe()" id="btnSubscribe" class="hide">Subscribe</button>
<button onclick="unsubscribe()" id="btnUnsubscribe" class="hide">Unsubscribe</button>
</main>
</body>
<script src="./bundle.js"></script>
</html>

import socketio from '@feathersjs/socketio-client'
import { feathers } from '@feathersjs/feathers'
import io from 'socket.io-client'
import {
checkPrerequisites,
getPushSubscription,
subscribePushNotifications,
unsubscribePushNotifications,
requestNotificationPermission
} from '../../lib/client'
// Vapid public key
const publicVapidKey = process.env.VAPID_PUBLIC_KEY || ''
// Create the client Feathers app
const api = feathers()
// Configure the transport using socket.io
const domain = process.env.PORT ? 'http://localhost:' + process.env.PORT : 'http://localhost:8081'
const socket = io(domain)
const transport = socketio(socket)
api.configure(transport)
// Functions
async function registerServiceWorker () {
// Check for serviceWorker on navigator
if ('serviceWorker' in navigator) {
try {
await navigator.serviceWorker.register('/service-worker.js')
console.log('Service worker successfully registered')
} catch(err) {
new Error('ServiceWorker registration failed: ', err)
}
}
}
async function isSubscribed () {
try {
await checkPrerequisites()
console.log('All prerequisites are valid')
} catch (err) {
console.log(err)
}
// Check subscription to web push notifications
const subscription = getPushSubscription()
if (subscription) {
// Check if the subscription is in database
const users = await api.service('users').find({ query: { endpoint: subscription.endpoint }})
if (users.length === 0) {
document.getElementById('isSubscribed').innerHTML = 'You must subscribe to receive notifications !'
document.getElementById('btnSubscribe').className = ''
document.getElementById('btnPush').className = 'hide'
document.getElementById('btnUnsubscribe').className = 'hide'
} else {
console.log ( 'You are subscribed :' , subscription )
document.getElementById('isSubscribed').innerHTML = 'You are subscribed !'
document.getElementById('btnSubscribe').className = 'hide'
document.getElementById('btnPush').className = ''
document.getElementById('btnUnsubscribe').className = ''
}
} else {
document.getElementById('isSubscribed').innerHTML = 'You must subscribe to receive notifications !'
document.getElementById('btnSubscribe').className = ''
document.getElementById('btnPush').className = 'hide'
document.getElementById('btnUnsubscribe').className = 'hide'
}
}
window.subscribe = async () => {
// Check notification permission
try {
await requestNotificationPermission()
} catch (err) {
console.log(err)
}
// Subscribe to web webpush notifications
const subscription = await subscribePushNotifications(publicVapidKey)
// Create subscription
api.service('users').create({
subscriptions: [{
endpoint: subscription.endpoint,
keys: {
auth: subscription.keys.auth,
p256dh: subscription.keys.p256dh
}
}]
})
// Update page
document.getElementById('isSubscribed').innerHTML = 'You are subscribed !'
document.getElementById('btnSubscribe').className = 'hide'
document.getElementById('btnPush').className = ''
document.getElementById('btnUnsubscribe').className = ''
console.log('Webpush subscription registered: ', subscription)
}
window.unsubscribe = async () => {
// Unsubscribe from web webpush notifications
const subscription = unsubscribePushNotifications()
// Remove subscription
api.service('users').remove(null, { query: { endpoint: subscription.endpoint }})
// Update page
document.getElementById('isSubscribed').innerHTML = 'You must subscribe to receive notifications !'
document.getElementById('btnSubscribe').className = ''
document.getElementById('btnPush').className = 'hide'
document.getElementById('btnUnsubscribe').className = 'hide'
console.log('Unsubscribing to web push notifications')
}
window.sendNotification = async () => {
// Setup notification params
const notification = {
title: 'feathers-webpush example title',
body: 'feathers-webpush example body',
icon: 'https://s3.eu-central-1.amazonaws.com/kalisioscope/kalisio/kalisio-icon-256x256.png',
url: 'https://kalisio.com/'
}
// Send webpush notification
api.service('push').create({
notification,
subscriptionService: 'users',
subscriptionProperty: 'subscriptions'
})
}
// Immediate
registerServiceWorker()
isSubscribed()

API

feathers-webpush consists of two parts:

  • Service that provides create method for sending notifications
  • Hooks, which provide function for deleting expired subscriptions

Service

Service (options)

Create an instance of the service with the given options:

Parameter Description Required
vapidDetails The vapidDetails configuration. Refer to the web-push package documentation for more information. yes
app The feathers app instance. yes

create (data, params)

The create method is used to send web push notifications. The data payload must contain the following properties:

Property Description Required
notification The data payload for the push notification. yes
subscriptionService The name of the service where subscriptions are registered. yes
subscriptionProperty The name of the key where subscriptions are regitered. It can be an array of subscriptions or a single subscription object yes
subscriptionFilter The filter you wish to apply when retrieving subscriptions. no

Note: Subscription should be registered in the following format: { endpoint: 'url_google', keys: { auth: 'xxxx', p256dh: 'xxxx' }}

Hooks

The feathers-webpush module provides a hook for managing web push subscriptions.

deleteExpiredSubscriptions ()

The deleteExpiredSubscriptions is an after hook that deletes expired subscriptions. It should be used after the create method.

To use this hook, you need to import it and include it in your FeathersJS application.

Exemple usage:

import { deleteExpiredSubscriptions } from '@kalisio/feathers-webpush'

app.service('push-notifications').hooks({
  after: {
    create: [deleteExpiredSubscriptions()]
  }
})

Client

The client.js file provides a utility to manage client-side web push notifications with the following functions:

Checking prerequisites

Before using web push notifications, you need to check if the necessary prerequisites are met. The checkPrerequisites function provided by feathers-webpush can be used for this purpose. It checks whether the browser supports notifications and returns error message 498 if it does not.

Requesting notification permission

To ask the user for permission to send notifications, you can use the requestNotificationPermission function provided by feathers-webpush. This function requests permission from the user and returns code 499 if permission is denied, or the status of the permission.

Get push subscription

The getPushSubscription function retrieves the current push subscription, if it exists. It returns the subscription object or null if there is no active subscription.

Subscribe to push notifications

To subscribe to push notifications, use the subscribePushNotifications function. It requires a public VAPID key as a parameter and returns the subscription object.

Unsubscribe from push notifications

To unsubscribe from push notifications, use the unsubscribePushNotifications function. It retrieves the registration from the service worker and unsubscribes the current push subscription. It returns the unsubscribed subscription object.

Adding and removing subscriptions

You can add or remove subscriptions from a user object using the addSubscription and removeSubscription functions provided by feathers-webpush. These functions require the user object, the current subscription, and the subscription property.

Tests

To run the tests for feathers-webpush, install the required node modules and run npm run test or yarn test.

To perform the tests, you need to create web push subscription and set the following environment variables:

  • SUBSCRIPTION_TEST_ENDPOINT
  • SUBSCRIPTION_TEST_KEY_AUTH
  • SUBSCRIPTION_TEST_KEY_P256DH

License

Copyright (c) 2017-20xx Kalisio

Licensed under the MIT license.

Authors

This project is sponsored by

Kalisio

About

Manage Web Push notifications with FeathersJS

License:MIT License


Languages

Language:JavaScript 82.1%Language:Shell 11.8%Language:HTML 6.1%