Z tego ćwiczenia dowiesz się, jak stworzyć serwer powiadomień push. Serwer będzie zarządzać listą subskrypcji push i wysyłać do nich powiadomienia.
Kod klienta jest już gotowy – w ramach tego ćwiczenia w Codelabs będziesz pracować nad funkcjami po stronie serwera.
Zremiksuj przykładową aplikację i wyświetl ją w nowej karcie
Powiadomienia z umieszczonej aplikacji Glitch są automatycznie blokowane, więc nie możesz wyświetlić podglądu aplikacji na tej stronie. Oto co musisz zrobić:
- Aby udostępnić projekt do edycji, kliknij Remiksuj, aby edytować.
- Aby wyświetlić podgląd strony, kliknij Wyświetl aplikację. Następnie naciśnij Pełny ekran
Aktywna aplikacja otworzy się w nowej karcie Chrome. Aby ponownie wyświetlić kod, kliknij Wyświetl źródło w osadzonym kodzie.
Podczas wykonywania tych ćwiczeń w programie wprowadź zmiany w kodzie w umieszczonym na tej stronie glitchu. Aby zobaczyć zmiany, odśwież nową kartę w aplikacji, która jest aktywna w sieci.
Zapoznaj się z aplikacją początkową i jej kodem
Zacznij od przyjrzenia się interfejsowi klienta aplikacji.
Na nowej karcie Chrome:
Naciśnij „Control + Shift + J” (lub „Command + Option + J” na Macu), aby otworzyć Narzędzia deweloperskie. Kliknij kartę Konsola.
Spróbuj kliknąć przyciski w interfejsie (wynik znajdziesz w konsoli deweloperskiej Chrome).
Zarejestruj skrypt service worker, aby zarejestrować skrypt service worker dla zakresu adresu URL projektu Glitch. Wyrejestruj skrypt service worker powoduje usunięcie go. Jeśli jest do niego podłączona subskrypcja push, to taka subskrypcja również zostanie dezaktywowana.
Subskrypcja do push tworzy subskrypcję push. Jest ona dostępna tylko wtedy, gdy skrypt service worker został zarejestrowany, a w kodzie klienta znajduje się stała
VAPID_PUBLIC_KEY
(więcej informacji na ten temat znajdziesz w dalszej części tego artykułu), dlatego nie można jeszcze jej kliknąć.Gdy masz aktywną subskrypcję push, opcja Powiadom bieżącą subskrypcję wysyła żądanie, aby serwer wysłał powiadomienie do swojego punktu końcowego.
Powiadom wszystkie subskrypcje informuje serwer, że ma wysłać powiadomienie do wszystkich punktów końcowych subskrypcji w jego bazie danych.
Pamiętaj, że niektóre z tych punktów końcowych mogą być nieaktywne. Może się zdarzyć, że subskrypcja zniknie przed wysłaniem powiadomienia przez serwer.
Spójrzmy, co dzieje się po stronie serwera. Komunikaty z kodu serwera możesz wyświetlić w dzienniku Node.js w interfejsie Glitch.
W aplikacji Glitch kliknij Narzędzia -> Dzienniki.
Prawdopodobnie zobaczysz komunikat podobny do tego:
Listening on port 3000
.Jeśli w interfejsie opublikowanej aplikacji klikniesz Powiadom bieżącą subskrypcję lub Powiadom wszystkie subskrypcje, zobaczysz też ten komunikat:
TODO: Implement sendNotifications() Endpoints to send to: []
Spójrzmy teraz na kod.
public/index.js
zawiera kompletny kod klienta. Pozwala wykrywać funkcje, rejestrować i wyrejestrowywać skrypt service worker oraz kontrolować subskrypcję powiadomień push użytkownika. Wysyła też do serwera informacje o nowych i usuniętych subskrypcjach.Ponieważ będziesz pracować tylko nad funkcjami serwera, nie będziesz edytować tego pliku (oprócz wypełnienia stałej
VAPID_PUBLIC_KEY
).public/service-worker.js
to prosty skrypt service worker, który rejestruje zdarzenia push i wyświetla powiadomienia./views/index.html
zawiera interfejs aplikacji..env
zawiera zmienne środowiskowe, które Glitch wczytuje na serwer aplikacji podczas uruchamiania. W polu.env
będą podawane dane uwierzytelniające na potrzeby wysyłania powiadomień.server.js
to plik, w którym będziesz wykonywać większość czynności w ramach tego ćwiczenia z programowania.Kod początkowy tworzy prosty serwer WWW Express. Masz dla Ciebie 4 elementy DO ZROBIENIA oznaczone w komentarzach do kodu znacznikiem
TODO:
. Czynności, które musisz wykonać:W ramach tego ćwiczenia w Codelabs będziesz po kolei wykonywać zadania związane z zadaniami do wykonania.
Wygeneruj i wczytaj szczegóły VAPID
Pierwszym elementem do wykonania jest wygenerowanie szczegółów VAPID, dodanie ich do zmiennych środowiskowych Node.js oraz zaktualizowanie kodu klienta i serwera o nowe wartości.
Tło
Gdy użytkownicy subskrybują powiadomienia, muszą mieć zaufanie do tożsamości aplikacji i jej serwera. Użytkownicy muszą też mieć pewność, że otrzymane powiadomienie pochodzi z tej samej aplikacji, w której skonfigurowano subskrypcję. Muszą też mieć pewność, że nikt inny nie może odczytać treści powiadomień.
Protokół, który zapewnia bezpieczeństwo i prywatność powiadomień push, nosi nazwę VAPID (Voluntary Application Server Identification for Web Push). VAPID używa kryptografii klucza publicznego do weryfikowania tożsamości aplikacji, serwerów i punktów końcowych subskrypcji oraz do szyfrowania treści powiadomień.
W tej aplikacji użyjesz pakietu npm web-push do generowania kluczy VAPID oraz szyfrowania i wysyłania powiadomień.
Implementacja
W tym kroku wygeneruj parę kluczy VAPID dla swojej aplikacji i dodaj je do zmiennych środowiskowych. Wczytaj zmienne środowiskowe na serwerze i dodaj klucz publiczny jako stałą w kodzie klienta.
Użyj funkcji
generateVAPIDKeys
bibliotekiweb-push
, aby utworzyć parę kluczy VAPID.W pliku server.js usuń komentarze z tych wierszy kodu:
server.js
// Generate VAPID keys (only do this once). /* * const vapidKeys = webpush.generateVAPIDKeys(); * console.log(vapidKeys); */ const vapidKeys = webpush.generateVAPIDKeys(); console.log(vapidKeys);
Gdy Glitch ponownie uruchomi aplikację, przesyła wygenerowane klucze do dziennika Node.js w interfejsie glitcha (nie do konsoli Chrome). Aby wyświetlić klucze VAPID, wybierz Narzędzia -> dzienniki w interfejsie zakłócenia.
Pamiętaj, aby skopiować klucz publiczny i prywatny z tej samej pary kluczy.
Usterka uruchamia ponownie aplikację za każdym razem, gdy edytujesz kod, więc pierwsza para wygenerowanych kluczy może zniknąć z widoku w miarę pojawiania się kolejnych danych wyjściowych.
Skopiuj klucze VAPID i wklej je w pliku .env. Klucze umieść w cudzysłowach podwójnych (
"..."
).W polu
VAPID_SUBJECT
możesz wpisać"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"
W pliku server.js jeszcze raz zmień w komentarz te 2 wiersze kodu, ponieważ klucze VAPID musisz wygenerować tylko raz.
server.js
// Generate VAPID keys (only do this once). /* const vapidKeys = webpush.generateVAPIDKeys(); console.log(vapidKeys); */ const vapidKeys = webpush.generateVAPIDKeys(); console.log(vapidKeys);
W pliku server.js wczytaj szczegóły VAPID ze zmiennych środowiskowych.
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 }
Skopiuj klucz public i wklej go również do kodu klienta.
W pliku public/index.js wpisz tę samą wartość parametru
VAPID_PUBLIC_KEY
, która została skopiowana do pliku .env:public/index.js
// Copy from .env const VAPID_PUBLIC_KEY = ''; const VAPID_PUBLIC_KEY = 'BN3tWzHp3L3rBh03lGLlLlsq...'; ````
Wdróż funkcję wysyłania powiadomień
Tło
W tej aplikacji do wysyłania powiadomień będziesz używać pakietu npm web-push.
Ten pakiet automatycznie szyfruje powiadomienia podczas wywoływania funkcji webpush.sendNotification()
, więc nie musisz się tym przejmować.
Web-push akceptuje wiele opcji powiadomień – na przykład możesz dołączyć nagłówki do wiadomości i określić kodowanie treści.
W ramach tego ćwiczenia w Codelabs będziesz używać tylko 2 opcji, zdefiniowanych za pomocą tych wierszy kodu:
let options = {
TTL: 10000; // Time-to-live. Notifications expire after this.
vapidDetails: vapidDetails; // VAPID keys from .env
};
Opcja TTL
(czas życia) ustawia limit czasu wygaśnięcia powiadomienia. Dzięki temu serwer nie wysyła powiadomienia do użytkownika, gdy nie jest już ono potrzebne.
Opcja vapidDetails
zawiera klucze VAPID wczytane ze zmiennych środowiskowych.
Implementacja
W pliku server.js zmodyfikuj funkcję sendNotifications
w ten sposób:
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);
});
}
Ponieważ webpush.sendNotification()
zwraca obietnicę, możesz łatwo dodać obsługę błędów.
W pliku server.js ponownie zmodyfikuj funkcję 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} `);
});
});
}
Obsługa nowych subskrypcji
Tło
Oto co się dzieje, gdy użytkownik zasubskrybuje powiadomienia push:
Użytkownik klika Subskrybuj do push.
Klient używa stałej
VAPID_PUBLIC_KEY
(publicznego klucza VAPID serwera) do generowania unikalnego obiektusubscription
specyficznego dla serwera. Obiektsubscription
wygląda tak:{ "endpoint": "https://fcm.googleapis.com/fcm/send/cpqAgzGzkzQ:APA9...", "expirationTime": null, "keys": { "p256dh": "BNYDjQL9d5PSoeBurHy2e4d4GY0sGJXBN...", "auth": "0IyyvUGNJ9RxJc83poo3bA" } }
Klient wysyła żądanie
POST
na adres URL/add-subscription
, w tym subskrypcję w postaci ciągu JSON w treści.Serwer pobiera ciąg
subscription
z treści żądania POST, analizuje go z powrotem do formatu JSON i dodaje do bazy danych subskrypcji.Baza danych przechowuje subskrypcje przy użyciu własnych punktów końcowych jako klucza:
{
"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: { ... }
},
}
Teraz serwer może wysyłać powiadomienia dzięki nowej subskrypcji.
Implementacja
Żądania nowych subskrypcji trafiają do trasy /add-subscription
, która jest adresem URL POST. W pliku server.js zobaczysz fragment obsługi trasy:
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);
});
W Twojej implementacji ten moduł obsługi musi:
- Pobierz nową subskrypcję z treści żądania.
- uzyskać dostęp do bazy danych aktywnych subskrypcji;
- Dodaj nową subskrypcję do listy aktywnych subskrypcji.
Aby obsługiwać nowe subskrypcje:
Zmodyfikuj w pliku server.js moduł obsługi trasy
/add-subscription
w ten sposób: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);
});
Obsługiwanie anulowania subskrypcji
Tło
Serwer nie zawsze wie, kiedy subskrypcja jest nieaktywna – może na przykład zostać wyczyszczona po wyłączeniu skryptu service worker w przeglądarce.
Serwer może jednak dowiedzieć się o subskrypcjach anulowanych przez interfejs aplikacji. W tym kroku wdrożysz funkcję usuwania subskrypcji z bazy danych.
Dzięki temu serwer nie wysyła wielu powiadomień do nieistniejących punktów końcowych. Oczywiście w przypadku prostej aplikacji testowej nie ma to znaczenia, ale na większą skalę staje się to ważne.
Implementacja
Prośby o anulowanie subskrypcji będą wysyłane na adres URL POST /remove-subscription
.
Moduł obsługi odcinka trasy w pliku server.js wygląda tak:
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);
});
W Twojej implementacji ten moduł obsługi musi:
- Pobierz punkt końcowy anulowanej subskrypcji z treści żądania.
- uzyskać dostęp do bazy danych aktywnych subskrypcji;
- Usuń anulowaną subskrypcję z listy aktywnych subskrypcji.
Treść żądania POST od klienta zawiera punkt końcowy, który musisz usunąć:
{
"endpoint": "https://fcm.googleapis.com/fcm/send/cpqAgzGzkzQ:APA9..."
}
Aby postępować w przypadku anulowania subskrypcji:
Zmodyfikuj w pliku server.js moduł obsługi trasy
/remove-subscription
w ten sposób: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);
});