Dans cet atelier de programmation, vous allez créer un serveur de notifications push. Le serveur gérera la liste des abonnements push et leur enverra des notifications.
Le code client est déjà terminé. Dans cet atelier de programmation, vous allez travailler sur la fonctionnalité côté serveur.
Remixer l'application exemple et l'afficher dans un nouvel onglet
Les notifications de l'application Glitch intégrée sont automatiquement bloquées. Vous ne pourrez donc pas prévisualiser l'application sur cette page. Voici la marche à suivre:
- Cliquez sur Remix to Edit (Remixer pour modifier) pour pouvoir modifier le projet.
- Pour prévisualiser le site, appuyez sur Afficher l'application. Appuyez ensuite sur Plein écran
L'application en ligne s'ouvre dans un nouvel onglet Chrome. Dans le Glitch intégré, cliquez sur View Source (Afficher la source) pour afficher à nouveau le code.
Tout au long de cet atelier de programmation, modifiez le code dans le Glitch intégré sur cette page. Actualisez le nouvel onglet contenant votre application en ligne pour voir les modifications.
Se familiariser avec l'application de démarrage et son code
Commencez par examiner l'UI cliente de l'application.
Dans le nouvel onglet Chrome:
Appuyez sur Ctrl+Maj+J (ou Cmd+Option+J sur Mac) pour ouvrir les outils de développement. Cliquez sur l'onglet Console.
Essayez de cliquer sur les boutons de l'interface utilisateur (consultez la console pour les développeurs Chrome pour obtenir le résultat).
L'option Enregistrer un service worker enregistre un service worker pour le champ d'application de l'URL de votre projet Glitch. L'option Annuler l'enregistrement d'un service worker supprime le service worker. Si un abonnement push lui est associé, il sera également désactivé.
S'abonner pour envoyer des notifications push crée un abonnement push. Elle n'est disponible que lorsqu'un service worker a été enregistré et qu'une constante
VAPID_PUBLIC_KEY
est présente dans le code client (nous reviendrons sur ce point ultérieurement). Vous ne pouvez donc pas cliquer dessus tout de suite.Lorsque vous disposez d'un abonnement push actif, la fonctionnalité Notifier de l'abonnement en cours demande au serveur d'envoyer une notification à son point de terminaison.
Notifier tous les abonnements indique au serveur d'envoyer une notification à tous les points de terminaison d'abonnement de sa base de données.
Notez que certains de ces points de terminaison peuvent être inactifs. Il est possible qu'un abonnement disparaisse au moment où le serveur lui envoie une notification.
Voyons ce qui se passe côté serveur. Pour afficher les messages à partir du code du serveur, consultez le journal Node.js dans l'interface Glitch.
Dans l'application Glitch, cliquez sur Outils -> journaux.
Vous verrez probablement un message tel que
Listening on port 3000
.Si vous avez essayé de cliquer sur Envoyer une notification pour l'abonnement en cours ou Envoyer une notification à tous les abonnements dans l'interface utilisateur de l'application en ligne, le message suivant s'affiche également:
TODO: Implement sendNotifications() Endpoints to send to: []
Examinons maintenant un peu de code.
public/index.js
contient le code client finalisé. Il détecte les fonctionnalités, enregistre et annule l'enregistrement du service worker, et contrôle l'abonnement des utilisateurs aux notifications push. Il envoie également au serveur des informations sur les abonnements nouveaux et supprimés.Étant donné que vous travaillerez uniquement sur les fonctionnalités du serveur, vous ne modifierez pas ce fichier (à part remplir la constante
VAPID_PUBLIC_KEY
).public/service-worker.js
est un service worker simple qui capture les événements push et affiche des notifications./views/index.html
contient l'UI de l'application..env
contient les variables d'environnement que Glitch charge sur votre serveur d'applications au démarrage. Vous allez renseigner.env
avec les informations d'authentification pour l'envoi de notifications.server.js
est le fichier dans lequel vous allez effectuer la plupart de vos tâches au cours de cet atelier de programmation.Le code de démarrage crée un serveur Web Express simple. Quatre éléments TODO sont à votre disposition, marqués dans les commentaires du code avec
TODO:
. Vos tâches sont les suivantes :Chargez les détails VAPID à partir de variables d'environnement.
Implémentez une fonctionnalité permettant d'envoyer des notifications.
Dans cet atelier de programmation, vous allez examiner ces éléments TODO un par un.
Générer et charger les détails VAPID
Votre premier élément TODO consiste à générer les détails VAPID, à les ajouter aux variables d'environnement Node.js, et à mettre à jour le code client et le code du serveur avec les nouvelles valeurs.
Contexte
Lorsque les utilisateurs s'abonnent aux notifications, ils doivent faire confiance à l'identité de l'application et de son serveur. Les utilisateurs doivent également s'assurer que lorsqu'ils reçoivent une notification, celle-ci provient de l'application qui a configuré l'abonnement. Ils doivent également s'assurer que personne d'autre ne peut lire le contenu des notifications.
Le protocole qui assure la sécurité et la confidentialité des notifications push est appelé Voluntary Application Server Identification for Web Push (VAPID). VAPID utilise la cryptographie à clé publique pour valider l'identité des applications, des serveurs et des points de terminaison d'abonnement, ainsi que pour chiffrer le contenu des notifications.
Dans cette application, vous utiliserez le package npm web-push pour générer des clés VAPID, ainsi que pour chiffrer et envoyer des notifications.
Implémentation
Au cours de cette étape, vous allez générer une paire de clés VAPID pour votre application et les ajouter aux variables d'environnement. Chargez les variables d'environnement sur le serveur et ajoutez la clé publique en tant que constante dans le code client.
Utilisez la fonction
generateVAPIDKeys
de la bibliothèqueweb-push
pour créer une paire de clés VAPID.Dans server.js, supprimez les commentaires autour des lignes de code suivantes:
server.js
// Generate VAPID keys (only do this once). /* * const vapidKeys = webpush.generateVAPIDKeys(); * console.log(vapidKeys); */ const vapidKeys = webpush.generateVAPIDKeys(); console.log(vapidKeys);
Une fois que Glitch a redémarré votre application, les clés générées sont consignées dans le journal Node.js de l'interface Glitch (et non dans la console Chrome). Pour afficher les clés VAPID, sélectionnez Tools -> (Outils -> Journaux dans l'interface Glitch.
Veillez à copier vos clés publique et privée à partir de la même paire de clés.
Glitch redémarre votre application chaque fois que vous modifiez votre code. Par conséquent, la première paire de clés que vous générez peut disparaître de la vue comme suit.
Dans .env, copiez et collez les clés VAPID. Placez les clés entre guillemets doubles (
"..."
).Pour
VAPID_SUBJECT
, vous pouvez saisir"mailto:test@test.test"
..env
# process.env.SECRET VAPID_PUBLIC_KEY= VAPID_PRIVATE_KEY= VAPID_SUBJECT= VAPID_PUBLIC_KEY="BN3tWzHp3L3rBh03lGLlLlsq..." VAPID_PRIVATE_KEY="I_lM7JMIXRhOk6HN..." VAPID_SUBJECT="mailto:test@test.test"
Dans server.js, mettez de nouveau en commentaire ces deux lignes de code, car vous n'avez besoin de générer les clés VAPID qu'une seule fois.
server.js
// Generate VAPID keys (only do this once). /* const vapidKeys = webpush.generateVAPIDKeys(); console.log(vapidKeys); */ const vapidKeys = webpush.generateVAPIDKeys(); console.log(vapidKeys);
Dans server.js, chargez les informations sur la propriété VAPID à partir des variables d'environnement.
server.js
const vapidDetails = { // TODO: Load VAPID details from environment variables. publicKey: process.env.VAPID_PUBLIC_KEY, privateKey: process.env.VAPID_PRIVATE_KEY, subject: process.env.VAPID_SUBJECT }
Copiez et collez également la clé public dans le code client.
Dans public/index.js, saisissez pour
VAPID_PUBLIC_KEY
la même valeur que celle que vous avez copiée dans le fichier .env:public/index.js
// Copy from .env const VAPID_PUBLIC_KEY = ''; const VAPID_PUBLIC_KEY = 'BN3tWzHp3L3rBh03lGLlLlsq...'; ````
Implémenter une fonctionnalité pour envoyer des notifications
Contexte
Dans cette application, vous utiliserez le package npm web-push pour envoyer des notifications.
Ce package chiffre automatiquement les notifications lorsque webpush.sendNotification()
est appelé. Vous n'avez donc pas à vous en soucier.
Le paramètre web-push accepte plusieurs options de notification. Vous pouvez, par exemple, joindre des en-têtes au message et spécifier l'encodage du contenu.
Dans cet atelier de programmation, vous n'utiliserez que deux options, définies avec les lignes de code suivantes:
let options = {
TTL: 10000; // Time-to-live. Notifications expire after this.
vapidDetails: vapidDetails; // VAPID keys from .env
};
L'option TTL
(Time To Live) définit un délai d'expiration pour une notification. Cela permet au serveur d'éviter d'envoyer une notification à un utilisateur lorsqu'elle n'est plus pertinente.
L'option vapidDetails
contient les clés VAPID que vous avez chargées à partir des variables d'environnement.
Implémentation
Dans server.js, modifiez la fonction sendNotifications
comme suit:
server.js
function sendNotifications(database, endpoints) {
// TODO: Implement functionality to send notifications.
console.log('TODO: Implement sendNotifications()');
console.log('Endpoints to send to: ', endpoints);
let notification = JSON.stringify(createNotification());
let options = {
TTL: 10000, // Time-to-live. Notifications expire after this.
vapidDetails: vapidDetails // VAPID keys from .env
};
endpoints.map(endpoint => {
let subscription = database[endpoint];
webpush.sendNotification(subscription, notification, options);
});
}
Comme webpush.sendNotification()
renvoie une promesse, vous pouvez facilement ajouter une gestion des erreurs.
Dans server.js, modifiez à nouveau la fonction sendNotifications
:
server.js
function sendNotifications(database, endpoints) {
let notification = JSON.stringify(createNotification());
let options = {
TTL: 10000; // Time-to-live. Notifications expire after this.
vapidDetails: vapidDetails; // VAPID keys from .env
};
endpoints.map(endpoint => {
let subscription = database[endpoint];
webpush.sendNotification(subscription, notification, options);
let id = endpoint.substr((endpoint.length - 8), endpoint.length);
webpush.sendNotification(subscription, notification, options)
.then(result => {
console.log(`Endpoint ID: ${id}`);
console.log(`Result: ${result.statusCode} `);
})
.catch(error => {
console.log(`Endpoint ID: ${id}`);
console.log(`Error: ${error.body} `);
});
});
}
Gérer les nouveaux abonnements
Contexte
Voici ce qui se passe lorsque l'utilisateur s'abonne aux notifications push:
L'utilisateur clique sur S'abonner pour envoyer des messages push.
Le client utilise la constante
VAPID_PUBLIC_KEY
(la clé VAPID publique du serveur) pour générer un objetsubscription
unique et spécifique au serveur. L'objetsubscription
se présente comme suit:{ "endpoint": "https://fcm.googleapis.com/fcm/send/cpqAgzGzkzQ:APA9...", "expirationTime": null, "keys": { "p256dh": "BNYDjQL9d5PSoeBurHy2e4d4GY0sGJXBN...", "auth": "0IyyvUGNJ9RxJc83poo3bA" } }
Le client envoie une requête
POST
à l'URL/add-subscription
en incluant l'abonnement au format JSON concaténé dans le corps.Le serveur récupère la chaîne
subscription
dans le corps de la requête POST, l'analyse au format JSON et l'ajoute à la base de données des abonnements.La base de données stocke les abonnements en utilisant leurs propres points de terminaison en tant que clé:
{
"https://fcm...1234": {
endpoint: "https://fcm...1234",
expirationTime: ...,
keys: { ... }
},
"https://fcm...abcd": {
endpoint: "https://fcm...abcd",
expirationTime: ...,
keys: { ... }
},
"https://fcm...zxcv": {
endpoint: "https://fcm...zxcv",
expirationTime: ...,
keys: { ... }
},
}
Le nouvel abonnement est maintenant disponible sur le serveur pour l'envoi de notifications.
Implémentation
Les requêtes de nouveaux abonnements passent par la route /add-subscription
, qui est une URL POST. Un gestionnaire d'itinéraires bouchons s'affiche dans server.js:
server.js
app.post('/add-subscription', (request, response) => {
// TODO: implement handler for /add-subscription
console.log('TODO: Implement handler for /add-subscription');
console.log('Request body: ', request.body);
response.sendStatus(200);
});
Dans votre implémentation, ce gestionnaire doit:
- Récupérez le nouvel abonnement à partir du corps de la requête.
- Accédez à la base de données des abonnements actifs.
- Ajoutez le nouvel abonnement à la liste des abonnements actifs.
Pour gérer les nouveaux abonnements:
Dans server.js, modifiez le gestionnaire de routes pour
/add-subscription
comme suit:server.js
app.post('/add-subscription', (request, response) => {
// TODO: implement handler for /add-subscription
console.log('TODO: Implement handler for /add-subscription');
console.log('Request body: ', request.body);
let subscriptions = Object.assign({}, request.session.subscriptions);
subscriptions[request.body.endpoint] = request.body;
request.session.subscriptions = subscriptions;
response.sendStatus(200);
});
Gérer les résiliations d'abonnements
Contexte
Le serveur ne saura pas toujours quand un abonnement devient inactif. Par exemple, un abonnement peut être effacé à l'arrêt du service worker par le navigateur.
Toutefois, le serveur peut être informé des abonnements résiliés via l'interface utilisateur de l'application. Au cours de cette étape, vous allez implémenter une fonctionnalité permettant de supprimer un abonnement de la base de données.
De cette façon, le serveur évite d'envoyer un grand nombre de notifications à des points de terminaison qui n'existent pas. Évidemment, cela n'a pas vraiment d'importance avec une simple application de test, mais cela devient important à plus grande échelle.
Implémentation
Les requêtes d'annulation d'abonnements sont envoyées à l'URL POST /remove-subscription
.
Le gestionnaire de routes bouchon dans server.js se présente comme suit:
server.js
app.post('/remove-subscription', (request, response) => {
// TODO: implement handler for /remove-subscription
console.log('TODO: Implement handler for /remove-subscription');
console.log('Request body: ', request.body);
response.sendStatus(200);
});
Dans votre implémentation, ce gestionnaire doit:
- Récupérez le point de terminaison de l'abonnement résilié dans le corps de la requête.
- Accédez à la base de données des abonnements actifs.
- Supprimez l'abonnement annulé de la liste des abonnements actifs.
Le corps de la requête POST du client contient le point de terminaison que vous devez supprimer:
{
"endpoint": "https://fcm.googleapis.com/fcm/send/cpqAgzGzkzQ:APA9..."
}
Pour gérer les résiliations d'abonnement:
Dans server.js, modifiez le gestionnaire de routes pour
/remove-subscription
comme suit:server.js
app.post('/remove-subscription', (request, response) => {
// TODO: implement handler for /remove-subscription
console.log('TODO: Implement handler for /remove-subscription');
console.log('Request body: ', request.body);
let subscriptions = Object.assign({}, request.session.subscriptions);
delete subscriptions[request.body.endpoint];
request.session.subscriptions = subscriptions;
response.sendStatus(200);
});