ב-codelab הזה תלמדו איך ליצור שרת התראות דחיפה. השרת ינהל רשימה של מינויים לקבלת התראות וישלח אליהן התראות.
קוד הלקוח כבר הושלם – ב-codelab הזה תעבדו על הפונקציונליות בצד השרת.
יצירת רמיקס של האפליקציה לדוגמה והצגה שלה בכרטיסייה חדשה
ההתראות נחסמות באופן אוטומטי מאפליקציית Glitch המוטמעת, כך שלא תוכלו לראות תצוגה מקדימה של האפליקציה בדף הזה. במקום זאת:
- לוחצים על Remix to Edit כדי לאפשר עריכה של הפרויקט.
- כדי לראות תצוגה מקדימה של האתר, לוחצים על View App (הצגת האפליקציה) ואז על Fullscreen (מסך מלא).
האפליקציה בשידור חי תיפתח בכרטיסייה חדשה של Chrome. ב-Glitch המוטמע, לוחצים על View Source (הצגת המקור) כדי להציג שוב את הקוד.
במהלך העבודה ב-codelab הזה, תוכלו לבצע שינויים בקוד ב-Glitch המוטמע בדף הזה. כדאי לרענן את הכרטיסייה החדשה עם האפליקציה הפעילה כדי לראות את השינויים.
מעיינים באפליקציה המקורית ובקוד שלה
כדי להתחיל, כדאי לבדוק את ממשק המשתמש של הלקוח באפליקציה.
בכרטיסייה החדשה ב-Chrome:
מקישים על Control+Shift+J (או על Command+Option+J ב-Mac) כדי לפתוח את DevTools. לוחצים על הכרטיסייה מסוף.
נסו ללחוץ על לחצנים בממשק המשתמש (בודקים את הפלט במסוף הפיתוח של Chrome).
במסגרת רישום קובץ שירות (service worker) מתבצע רישום של קובץ שירות (service worker) בהיקף של כתובת ה-URL של פרויקט Glitch. ביטול הרישום של קובץ השירות (service worker) מסיר את קובץ השירות. אם למינוי מצורף מינוי לקבלת התראות, גם המינוי לקבלת התראות יושבת.
הרשמה לקבלת הודעות שנשלחות מהאפליקציה יוצרת מינוי לקבלת התראות. הוא זמין רק כשנרשם עובד שירות ויש קבוע
VAPID_PUBLIC_KEY
בקוד הלקוח (מידע נוסף על כך בהמשך), כך שעדיין אי אפשר ללחוץ עליו.כשיש מינוי Push פעיל, שליחת התראה למינוי הנוכחי מבקשת מהשרת לשלוח התראה לנקודת הקצה שלו.
ההגדרה Notify all subscriptions מורה לשרת לשלוח התראה לכל נקודות הקצה של המינויים במסד הנתונים שלו.
שימו לב שחלק מנקודות הקצה האלה עשויות להיות לא פעילות. תמיד יכול להיות שהמינוי ייעלם עד שהשרת ישלח אליו התראה.
בואו נראה מה קורה בצד השרת. כדי לראות הודעות מקוד השרת, אפשר לעיין ביומן Node.js בממשק של Glitch.
באפליקציית Glitch, לוחצים על Tools -> Logs.
סביר להניח שתוצג לך הודעה כמו
Listening on port 3000
.אם ניסיתם ללחוץ על Notify current subscription (עדכון המינויים הנוכחיים) או על Notify all subscriptions (עדכון כל המינויים) בממשק המשתמש של האפליקציה הפעילה, תוצג לכם גם ההודעה הבאה:
TODO: Implement sendNotifications()
Endpoints to send to: []
עכשיו נסתכל על קוד.
public/index.js
מכיל את קוד הלקוח המלא. הוא מבצע זיהוי תכונות, רושם את קובץ השירות ומבטל את הרישום שלו, ושולט במינוי של המשתמש להתראות דחיפה. הוא גם שולח לשרת מידע על מינויים חדשים ומנויים שנמחקו.מאחר שאתם עובדים רק על פונקציונליות השרת, אין אפשרות לערוך את הקובץ הזה (מלבד אכלוס הקבוע
VAPID_PUBLIC_KEY
).public/service-worker.js
הוא שירות פשוט לעבודה ברקע שמתעד אירועי דחיפה ומציג התראות./views/index.html
מכיל את ממשק המשתמש של האפליקציה..env
מכיל את משתני הסביבה ש-Glitch טוען לשרת האפליקציות בזמן ההפעלה. צריך לאכלס את השדה.env
בפרטי אימות לשליחת התראות.server.js
הוא הקובץ שבו תבצעו את רוב העבודה במהלך ה-Codelab הזה.הקוד ההתחלתי יוצר שרת אינטרנט פשוט של Express. יש עבורך ארבעה פריטי TODO, שמסומנים בתגובות קוד באמצעות
TODO:
. מה צריך לעשות?ב-codelab הזה נעבד על כל אחד מהפריטים ברשימה הזו.
יצירה וטעינה של פרטי VAPID
הפריט הראשון ברשימה הוא יצירת פרטי VAPID, הוספה שלהם למשתני הסביבה של Node.js ועדכון הקוד של הלקוח והשרת בעזרת הערכים החדשים.
רקע
כשמשתמשים נרשמים לקבלת התראות, הם צריכים לסמוך על הזהות של האפליקציה והשרת שלה. בנוסף, המשתמשים צריכים להיות בטוחים שההתראות שהם מקבלים מגיעות מאותה אפליקציה שבה הם הגדרו את המינוי. הם גם צריכים לסמוך על כך שאף אחד אחר לא יכול לקרוא את תוכן ההתראות.
הפרוטוקול שמבטיח את האבטחה והפרטיות של התראות ה-push נקרא Voluntary Application Server Identification for Web Push (VAPID). ב-VAPID נעשה שימוש בקריפטוגרפיה של מפתח ציבורי כדי לאמת את הזהות של אפליקציות, שרתי צד לקוח ונקודות קצה של מינויים, ולהצפין את תוכן ההתראות.
באפליקציה הזו תשתמשו בחבילת ה-npm של web-push כדי ליצור מפתחות 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 -> Logs בממשק של 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 של web-push כדי לשלוח התראות.
החבילה הזו מצפינה את ההתראות באופן אוטומטי כשמתבצעת קריאה ל-webpush.sendNotification()
, כך שאין צורך לדאוג בקשר לזה.
הפרמטר web-push מקבל כמה אפשרויות להתראות - לדוגמה, אפשר לצרף להודעה כותרות ולבחור קידוד תוכן.
בקודלאב הזה נשתמש רק בשתי אפשרויות, שמוגדרות באמצעות שורות הקוד הבאות:
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} `);
});
});
}
טיפול במינויים חדשים
רקע
זה מה שקורה כשהמשתמש נרשם לקבלת התראות:
המשתמש לוחץ על הרשמה לקבלת התראות.
הלקוח משתמש בקבוע
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. 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, משנים את בורר הנתיב של
/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.
עם זאת, השרת יכול לדעת על מינויים שבוטלו דרך ממשק המשתמש של האפליקציה. בשלב הזה תטמיעו פונקציונליות להסרת מינוי ממסד הנתונים.
כך השרת לא שולח המון התראות לנקודות קצה לא קיימות. ברור שזה לא ממש משנה באפליקציית בדיקה פשוטה, אבל זה הופך להיות חשוב בקנה מידה גדול יותר.
הטמעה
בקשות לביטול מינויים מגיעות לכתובת ה-POST /remove-subscription
.
קוד ה-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);
});
בהטמעה, הטיפול הזה צריך:
- מאחזרים את נקודת הקצה של המינוי שהתבטל מתוך גוף הבקשה.
- גישה למסד הנתונים של המינויים הפעילים.
- מסירים את המינוי המבוטל מרשימת המינויים הפעילים.
גוף בקשת ה-POST מהלקוח מכיל את נקודת הקצה שצריך להסיר:
{
"endpoint": "https://fcm.googleapis.com/fcm/send/cpqAgzGzkzQ:APA9..."
}
כדי לטפל בביטולי מינויים:
ב-server.js, משנים את בורר הנתיב של
/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);
});