ב-Codelab הזה תבנה שרת התראות. השרת ינהל רשימה של מינויי Push וישלח אליהם התראות.
קוד הלקוח כבר הושלם – ב-Codelab הזה עובדים על הפונקציונליות בצד השרת.
יצירת רמיקס של אפליקציה לדוגמה והצגתה בכרטיסייה חדשה
ההתראות חסומות באופן אוטומטי באפליקציית Glitch המוטמעת, כך שלא ניתן להציג תצוגה מקדימה של האפליקציה בדף הזה. במקום זאת:
- לוחצים על רמיקס לעריכה כדי לערוך את הפרויקט.
- כדי לראות תצוגה מקדימה של האתר, לוחצים על הצגת האפליקציה. לאחר מכן לוחצים על
מסך מלא
.
האפליקציה בשידור חי תיפתח בכרטיסייה חדשה של Chrome. בגרסה המוטמעת של תקלה, לוחצים על הצגת מקור כדי להציג את הקוד שוב.
במהלך העבודה ב-Codelab הזה, יש לבצע שינויים בקוד ב-Glitch שמוטמע בדף הזה. כדאי לרענן את הכרטיסייה החדשה עם האפליקציה הפעילה כדי לראות את השינויים.
כדאי להכיר את אפליקציית הפתיחה ואת הקוד שלה
כדי להתחיל, בודקים את ממשק המשתמש של האפליקציה.
בכרטיסייה החדשה של Chrome:
מקישים על 'Control+Shift+J' (או על 'Command+Option+J' ב-Mac) כדי לפתוח את כלי הפיתוח. לוחצים על הכרטיסייה מסוף.
כדאי לנסות ללחוץ על לחצנים בממשק המשתמש (אפשר לבדוק את הפלט במסוף הפיתוח של Chrome).
באמצעות רישום קובץ שירות (service worker) מתבצע רישום של קובץ שירות (service worker) בהיקף של כתובת ה-URL של פרויקט Glitch. ביטול הרישום של Service Worker מסיר את ה-Service Worker. אם מוצמד אליו מינוי Push, גם מינוי ה-Push יופעל.
האפשרות הרשמה ל-Push יוצרת מינוי Push. האפשרות הזו זמינה רק לאחר רישום של Service Worker וקבוע
VAPID_PUBLIC_KEY
בקוד הלקוח (נרחיב בנושא זה בהמשך), ולכן עדיין אי אפשר ללחוץ עליו.כשיש מינוי פעיל לדחיפה, התראה למינוי הנוכחי מבקש מהשרת לשלוח התראה לנקודת הקצה שלו.
האפשרות התראות לכל המינויים מורה לשרת לשלוח התראה לכל נקודות הקצה של המינויים במסד הנתונים שלו.
שימו לב: יכול להיות שחלק מנקודות הקצה האלה לא יהיו פעילות. תמיד יש סיכוי שמינוי ייעלם לפני שהשרת שולח אליו התראה.
בואו נראה מה קורה בצד השרת. כדי לראות את ההודעות מקוד השרת, צריך לעיין ביומן Node.js בממשק של Glitch.
באפליקציית Glitch, לוחצים על Tools -> (כלים ->) יומנים.
סביר להניח שתוצג לך הודעה כמו
Listening on port 3000
.אם ניסיתם ללחוץ על שליחת הודעה למינוי הנוכחי או על שליחת התראה לכל המינויים בממשק המשתמש של האפליקציה בשידור חי, תופיע גם ההודעה הבאה:
TODO: Implement sendNotifications() Endpoints to send to: []
עכשיו נסתכל על קוד.
public/index.js
מכיל את קוד הלקוח שהושלם. השירות מבצע זיהוי תכונות, רושם את קובץ השירות (service worker) ומבטל את הרישום שלו, ושולט במינוי של המשתמש להתראות. הוא גם שולח לשרת מידע על מינויים חדשים ונמחקו.מאחר שאתם עובדים רק על פונקציונליות השרת, אין אפשרות לערוך את הקובץ הזה (מלבד אכלוס הקבוע
VAPID_PUBLIC_KEY
).public/service-worker.js
הוא קובץ שירות (service worker) פשוט שמתעד אירועי Push ומציג התראות./views/index.html
מכיל את ממשק המשתמש של האפליקציה..env
מכיל את משתני הסביבה ש-Glitch נטענת לשרת האפליקציות שלך כשהיא מופעלת. המערכת תאוכלס ב-.env
בפרטי האימות לשליחת התראות.server.js
הוא הקובץ שבו תבצעו את רוב העבודה במהלך ה-Codelab הזה.קוד ההתחלה יוצר שרת אינטרנט פשוט מסוג Express. יש עבורך ארבעה פריטי TODO, שמסומנים בתגובות קוד באמצעות
TODO:
. מה צריך לעשות?ב-codelab הזה, תעבוד על פריטי TODO האלה אחד אחרי השני.
יצירה וטעינה של פרטי VAPID
פריט ה-TODO הראשון הוא ליצור פרטי VAPID, להוסיף אותם למשתני הסביבה Node.js ולעדכן את קוד הלקוח והשרת בערכים החדשים.
רקע
כשמשתמשים נרשמים לקבלת התראות, הם צריכים לתת אמון בזהות של האפליקציה ושל השרת שלה. המשתמשים גם צריכים להיות בטוחים שכשהם מקבלים התראה, הם מגיעים מאותה האפליקציה שהגדירה את המינוי. חשוב גם לוודא שאף אחד אחר לא יוכל לקרוא את תוכן ההתראה.
הפרוטוקול שהופך את ההתראות לבטוחות ופרטיות נקרא 'זיהוי שרת אפליקציות מרצון' ל'דחיפת רשת' (VAPID). VAPID משתמש בקריפטוגרפיה של מפתח ציבורי כדי לאמת את הזהות של האפליקציות, השרתים ונקודות הקצה של המינויים, וכדי להצפין את תוכן ההתראות.
באפליקציה הזו משתמשים בחבילת ה-NPM של דחיפה באינטרנט כדי ליצור מפתחות VAPID וכדי להצפין ולשלוח התראות.
הטמעה
בשלב הזה יוצרים זוג מפתחות VAPID לאפליקציה ומוסיפים אותם למשתני הסביבה. טוענים את משתני הסביבה בשרת ומוסיפים את המפתח הציבורי כקבוע בקוד הלקוח.
כדי ליצור זוג מפתחות VAPID, צריך להשתמש בפונקציה
generateVAPIDKeys
בספרייהweb-push
.ב-server.js, מסירים את התגובות משורות הקוד הבאות:
server.js
// Generate VAPID keys (only do this once). /* * const vapidKeys = webpush.generateVAPIDKeys(); * console.log(vapidKeys); */ const vapidKeys = webpush.generateVAPIDKeys(); console.log(vapidKeys);
אחרי ש-Glitch מפעילה מחדש את האפליקציה, היא מפיקה את המפתחות שנוצרו ליומן Node.js בתוך ממשק Glitch (לא אל מסוף Chrome). כדי לראות את מקשי VAPID, בוחרים באפשרות Tools -> (כלים ->) יומנים בממשק של Glitch.
חשוב לוודא שמעתיקים את המפתח הציבורי והמפתח הפרטי מאותו זוג מפתחות.
Glitch מופעלת מחדש את האפליקציה בכל פעם שעורכים את הקוד, כך שזוג המקשים הראשון שתיצרו עשוי לגלול מחוץ לתצוגה ככל שייראה פלט נוסף.
ב-.env, מעתיקים ומדביקים את מפתחות ה-VAPID. צריך לתחום את המפתחות במירכאות כפולות (
"..."
).בשביל
VAPID_SUBJECT
, אפשר להזין"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"
ב-server.js, צריך להוסיף שוב את שתי שורות הקוד האלה, כי צריך ליצור מפתחות VAPID רק פעם אחת.
server.js
// Generate VAPID keys (only do this once). /* const vapidKeys = webpush.generateVAPIDKeys(); console.log(vapidKeys); */ const vapidKeys = webpush.generateVAPIDKeys(); console.log(vapidKeys);
ב-server.js, טוענים את פרטי ה-VAPID ממשתני הסביבה.
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 }
מעתיקים את המפתח הציבורי ומדביקים אותו גם בקוד הלקוח.
בקובץ public/index.js, מזינים את אותו ערך בשביל
VAPID_PUBLIC_KEY
שהעתקת לקובץ ה- .env:public/index.js
// Copy from .env const VAPID_PUBLIC_KEY = ''; const VAPID_PUBLIC_KEY = 'BN3tWzHp3L3rBh03lGLlLlsq...'; ````
הטמעת פונקציונליות לשליחת התראות
רקע
באפליקציה הזו צריך להשתמש בחבילת NPM של דחיפה באינטרנט כדי לשלוח התראות.
החבילה הזו מצפינה באופן אוטומטי התראות כשמתבצעת שיחה אל webpush.sendNotification()
, כך שאין לך צורך לדאוג.
הפרמטר web-push מקבל מספר אפשרויות להתראות - לדוגמה, אפשר לצרף להודעה כותרות ולבחור קידוד תוכן.
ב-Codelab הזה משתמשים רק בשתי אפשרויות, שמוגדרות באמצעות שורות הקוד הבאות:
let options = {
TTL: 10000; // Time-to-live. Notifications expire after this.
vapidDetails: vapidDetails; // VAPID keys from .env
};
האפשרות TTL
(משך חיים) מגדירה זמן קצוב לתפוגה של התראה. כך השרת יכול למנוע שליחה של התראה למשתמש אחרי שהיא כבר לא רלוונטית.
האפשרות vapidDetails
מכילה את מפתחות ה-VAPID שטענתם ממשתני הסביבה.
הטמעה
ב-server.js, משנים את הפונקציה sendNotifications
באופן הבא:
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);
});
}
מכיוון ש-webpush.sendNotification()
מחזיר הבטחה, אפשר להוסיף בקלות טיפול בשגיאות.
ב-server.js, משנים שוב את הפונקציה 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} `);
});
});
}
טיפול במינויים חדשים
רקע
כשמשתמש נרשם לקבלת התראות:
המשתמש לוחץ על הרשמה ל-Push.
הלקוח משתמש בקבוע
VAPID_PUBLIC_KEY
(מפתח ה-VAPID הציבורי של השרת) כדי ליצור אובייקטsubscription
ייחודי וספציפי לשרת. האובייקטsubscription
נראה כך:{ "endpoint": "https://fcm.googleapis.com/fcm/send/cpqAgzGzkzQ:APA9...", "expirationTime": null, "keys": { "p256dh": "BNYDjQL9d5PSoeBurHy2e4d4GY0sGJXBN...", "auth": "0IyyvUGNJ9RxJc83poo3bA" } }
הלקוח שולח בקשת
POST
לכתובת ה-URL/add-subscription
, כולל המינוי כמחרוזת JSON בגוף ההודעה.השרת מאחזר את המחרוזת
subscription
מהגוף של בקשת ה-POST, מנתח אותו בחזרה ל-JSON ומוסיף אותו למסד הנתונים של המינויים.מינויים במסד הנתונים מאוחסנים באמצעות נקודות קצה משלהם כמפתח:
{
"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: { ... }
},
}
עכשיו המינוי החדש זמין לשרת לשליחת התראות.
הטמעה
בקשות למינויים חדשים מגיעות למסלול /add-subscription
, שהוא כתובת URL של POST. ה-handler של נתיב stub יופיע ב-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);
});
בהטמעה, ה-handler הזה צריך:
- מאחזרים את המינוי החדש מגוף הבקשה.
- גישה למסד הנתונים של המינויים הפעילים.
- מוסיפים את המינוי החדש לרשימת המינויים הפעילים.
כדי לטפל במינויים חדשים:
ב-server.js, משנים את ה-handler של הנתיב עבור
/add-subscription
באופן הבא: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);
});
טיפול בביטולי מינויים
רקע
השרת לא תמיד יידע מתי מינוי הופך ללא פעיל. לדוגמה, ייתכן שמינוי נמחק כשהדפדפן מכבה את Service Worker.
עם זאת, השרת יכול לקבל מידע על מינויים שבוטלו דרך ממשק המשתמש של האפליקציה. בשלב הזה תטמיעו פונקציונליות להסרת מינוי ממסד הנתונים.
כך השרת נמנע משליחת כמות גדולה של התראות לנקודות קצה (endpoint) שלא קיימות. כמובן שזה לא משנה באמת באפליקציית בדיקה פשוטה, אבל היא הופכת לחשובה בקנה מידה רחב.
הטמעה
בקשות לביטול מינויים מגיעות לכתובת ה-URL של /remove-subscription
של POST.
ה-handler של נתיב ה-stub ב-server.js נראה כך:
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);
});
בהטמעה, ה-handler הזה צריך:
- מאחזרים את נקודת הקצה של המינוי שבוטל מגוף הבקשה.
- גישה למסד הנתונים של המינויים הפעילים.
- להסיר את המינוי שבוטל מרשימת המינויים הפעילים.
הגוף של בקשת ה-POST מהלקוח מכיל את נקודת הקצה שצריך להסיר:
{
"endpoint": "https://fcm.googleapis.com/fcm/send/cpqAgzGzkzQ:APA9..."
}
כדי לטפל בביטולי מינויים:
ב-server.js, משנים את ה-handler של הנתיב עבור
/remove-subscription
באופן הבא: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);
});