Z tego ćwiczenia w programie utworzysz serwer powiadomień push. Serwer będzie zarządzać listą subskrypcji push i wysyłać do nich powiadomienia.
Kod klienta jest już gotowy – w tym ćwiczeniu w programie będziesz pracować nad funkcjami po stronie serwera.
Zremiksuj przykładową aplikację i wyświetl ją w nowej karcie
W umieszczonej aplikacji Glitch powiadomienia są automatycznie blokowane, więc na tej stronie nie będzie można wyświetlić podglądu aplikacji. Zamiast tego wykonaj te czynności:
- Aby umożliwić edytowanie projektu, kliknij Zremiksuj do edycji.
- Aby wyświetlić podgląd strony, kliknij Wyświetl aplikację, a potem Pełny ekran
.
Aktywna aplikacja otworzy się w nowej karcie Chrome. W umieszczonym w nim błędzie kliknij Wyświetl źródło, aby ponownie wyświetlić kod.
Podczas wykonywania ćwiczeń z programowania wprowadzaj zmiany w kodzie zawartym w umieszczonym na tej stronie glitchu. Odśwież nową kartę, używając aktywnej aplikacji, aby zobaczyć zmiany.
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.
Kliknij przyciski w interfejsie (sprawdź dane w konsoli programisty Chrome).
Zarejestruj skrypt service worker, by zarejestrować skrypt service worker w zakresie adresu URL projektu Glitch. Wyrejestruj skrypt service worker, usuwając go. Jeśli jest do niej połączona subskrypcja push, ona też zostanie dezaktywowana.
Subskrybuj, aby przesłać push, tworzy subskrypcję push. Jest on dostępny tylko wtedy, gdy skrypt service worker został zarejestrowany, a kod klienta zawiera stałą
VAPID_PUBLIC_KEY
(więcej informacji na ten temat znajdziesz później), więc nie możesz jeszcze jej kliknąć.Gdy masz aktywną subskrypcję push, funkcja Powiadom bieżącą subskrypcję żąda, aby serwer wysłał powiadomienie do swojego punktu końcowego.
Powiadamiaj wszystkich o subskrypcjach informuje serwer, że ma wysłać powiadomienie do wszystkich punktów końcowych subskrypcji w swojej bazie danych.
Pamiętaj, że niektóre z tych punktów końcowych mogą być nieaktywne. Subskrypcja może zawsze zniknąć, gdy serwer wyśle do niej powiadomienie.
Zobaczmy, co dzieje się po stronie serwera. Aby zobaczyć komunikaty z kodu serwera, zajrzyj do dziennika Node.js w interfejsie Glitch.
W aplikacji Glitch kliknij Narzędzia -> Logi.
Prawdopodobnie zobaczysz komunikat taki jak
Listening on port 3000
.Jeśli w interfejsie opublikowanej aplikacji klikniesz Powiadom bieżącą subskrypcję lub Powiadom wszystkie subskrypcje, pojawi się też ten komunikat:
TODO: Implement sendNotifications() Endpoints to send to: []
Spójrzmy teraz na kod.
public/index.js
zawiera kompletny kod klienta. Wykonuje wykrywanie funkcji, rejestruje i wyrejestrowuje skrypt service worker oraz kontroluje subskrypcję użytkownika na powiadomienia push. Wysyła też na serwer informacje o nowych i usuniętych subskrypcjach.Ponieważ będziesz pracować tylko nad funkcją 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
podasz dane uwierzytelniające na potrzeby wysyłania powiadomień.server.js
to plik, w którym będziesz wykonywać większość pracy w trakcie tych ćwiczeń z programowania.Kod początkowy tworzy prosty serwer WWW Express. Masz dla Ciebie 4 elementy DO ZROBIENIA oznaczone w komentarzach do kodu znakiem
TODO:
. Czynności, które musisz wykonać:W tym ćwiczeniu w programie będziesz osobno wykonywać zadania z tych zadań.
Generowanie i wczytywanie szczegółów VAPID
Pierwszym elementem TODO jest wygenerowanie szczegółów VAPID, dodanie ich do zmiennych środowiskowych Node.js oraz zaktualizowanie kodu klienta i serwera za pomocą nowych wartości.
Wprowadzenie
Gdy użytkownicy subskrybują powiadomienia, muszą zaufać tożsamości aplikacji i jej serwera. Użytkownicy muszą też mieć pewność, że gdy otrzymają powiadomienie, pochodzi z tej samej aplikacji, w której skonfigurowano subskrypcję. Muszą też mieć pewność, że nikt inny nie będzie mógł odczytać ich treści.
Protokół, który zapewnia bezpieczeństwo i prywatność powiadomień push, nosi nazwę Voluntary Application Server Identification for Web Push (VAPID). VAPID używa kryptografii klucza publicznego do weryfikacji tożsamości aplikacji, serwerów i punktów końcowych subskrypcji oraz szyfrowania treści powiadomień.
W tej aplikacji będziesz używać pakietu npm web-push do generowania kluczy VAPID oraz szyfrowania i wysyłania powiadomień.
Implementacja
W tym kroku wygeneruj dla swojej aplikacji parę kluczy VAPID i dodaj je do zmiennych środowiskowych. Wczytaj zmienne środowiskowe na serwerze i dodaj klucz publiczny jako stałą w kodzie klienta.
Użyj funkcji
generateVAPIDKeys
w biblioteceweb-push
, aby utworzyć parę kluczy VAPID.W kodzie 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);
Po ponownym uruchomieniu aplikacji Glitch przesyła wygenerowane klucze do dziennika Node.js w interfejsie Glitch (nie do konsoli Chrome). Aby zobaczyć klucze VAPID, w interfejsie Glitch wybierz Narzędzia -> Logi.
Pamiętaj, aby skopiować klucze publiczne i prywatne z tej samej pary kluczy.
Zakłócenie powoduje ponowne uruchomienie aplikacji za każdym razem, gdy edytujesz kod, więc pierwsza para kluczy może zniknąć, gdy pojawi się więcej danych wyjściowych.
W pliku .env skopiuj i wklej klucze VAPID. Klucze umieść w cudzysłowie prostym (
"..."
).Jako
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 umieść jeszcze raz komentarz w tych 2 wierszach kodu, ponieważ klucze VAPID wystarczy 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 publiczny i wklej go do kodu klienta.
W pliku public/index.js wpisz tę samą wartość dla
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...'; ````
Wdrażanie funkcji wysyłania powiadomień
Wprowadzenie
Do wysyłania powiadomień w tej aplikacji będziesz używać pakietu npm web-push.
Ten pakiet automatycznie szyfruje powiadomienia, gdy pojawia się webpush.sendNotification()
, więc nie musisz się tym przejmować.
Web-push akceptuje różne opcje powiadomień, na przykład możesz dołączać nagłówki do wiadomości i określić kodowanie treści.
W tym ćwiczeniu w programowaniu użyjesz 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 czas wygaśnięcia powiadomienia. Dzięki temu serwer uniknie wysyłania do użytkownika powiadomień, które przestały być istotne.
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);
});
}
Funkcja webpush.sendNotification()
zwraca obietnicę, więc 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ługuj nowe subskrypcje
Wprowadzenie
Co się stanie, gdy użytkownik zasubskrybuje powiadomienia push:
Użytkownik klika Subskrybuj, aby przesłać dane push.
Klient używa stałej
VAPID_PUBLIC_KEY
(publicznego klucza VAPID serwera) do wygenerowania unikalnego, dotyczącego serwera obiektusubscription
. Obiektsubscription
wygląda tak:{ "endpoint": "https://fcm.googleapis.com/fcm/send/cpqAgzGzkzQ:APA9...", "expirationTime": null, "keys": { "p256dh": "BNYDjQL9d5PSoeBurHy2e4d4GY0sGJXBN...", "auth": "0IyyvUGNJ9RxJc83poo3bA" } }
Klient wysyła na adres URL
/add-subscription
żądaniePOST
, uwzględniając subskrypcję w postaci ciągu tekstowego JSON w treści.Serwer pobiera ciąg znaków
subscription
z treści żądania POST, analizuje go z powrotem do formatu JSON i dodaje do bazy danych subskrypcji.Baza danych przechowuje subskrypcje za pomocą 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 nowa subskrypcja jest dostępna dla serwera do wysyłania powiadomień.
Implementacja
Żądania nowych subskrypcji są kierowane na trasę /add-subscription
, która jest adresem URL POST. W pliku server.js zobaczysz moduł obsługi wycinka 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.
- Dostęp do bazy danych aktywnych subskrypcji.
- Dodaj nową subskrypcję do listy aktywnych subskrypcji.
Aby obsługiwać nowe subskrypcje:
W pliku server.js zmodyfikuj 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ługa anulowania subskrypcji
Wprowadzenie
Serwer nie zawsze wie, że subskrypcja staje się nieaktywna – na przykład subskrypcja może zostać wyczyszczona, gdy przeglądarka wyłączy skrypt service worker.
Serwer może jednak dowiedzieć się o subskrypcjach, które zostały anulowane za pomocą interfejsu aplikacji. W tym kroku wdrożysz funkcję usuwania subskrypcji z bazy danych.
Dzięki temu serwer uniknie wysyłania mnóstwa powiadomień do nieistniejących punktów końcowych. Oczywiście nie ma to znaczenia w przypadku prostej aplikacji testowej, ale staje się to ważne na większą skalę.
Implementacja
Prośby o anulowanie subskrypcji są wysyłane na adres URL POST /remove-subscription
.
Moduł obsługi wycinka kodu 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.
- Dostęp do bazy danych aktywnych subskrypcji.
- Usuń anulowaną subskrypcję z listy aktywnych.
Treść żądania POST z klienta zawiera punkt końcowy, który musisz usunąć:
{
"endpoint": "https://fcm.googleapis.com/fcm/send/cpqAgzGzkzQ:APA9..."
}
Co zrobić w przypadku anulowania subskrypcji:
W pliku server.js zmodyfikuj 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);
});