Push bildirimleri sunucusu oluşturma

Bu codelab'de bir push bildirimleri sunucusu oluşturacaksınız. Sunucu push aboneliklerinin listesini yönetir ve bu aboneliklere bildirim gönderir.

İstemci kodu zaten tamamlanmıştır. Bu codelab'de sunucu tarafı işlevselliği üzerinde çalışacaksınız.

Örnek uygulamayı remiks olarak derleme ve yeni sekmede görüntüleme

Yerleşik Glitch uygulamasından gelen bildirimler otomatik olarak engellendiği için uygulamayı bu sayfada önizleyemezsiniz. Bunun yerine aşağıdaki adımları uygulayın:

  1. Projeyi düzenlenebilir hale getirmek için Düzenlenecek remiks'i tıklayın.
  2. Siteyi önizlemek için Uygulamayı Göster'e, ardından Tam Ekran'a tam ekran basın.

Yayındaki uygulama yeni bir Chrome sekmesinde açılır. Kodun tekrar gösterilmesi için yerleştirilmiş Glitch'te Kaynağı Görüntüle'yi tıklayın.

Bu codelab'de çalışırken, bu sayfadaki yerleşik Glitch'te yer alan kodda değişiklikler yapın. Değişiklikleri görmek için canlı uygulamanızın bulunduğu yeni sekmeyi yenileyin.

Başlangıç uygulamasını ve kodunu öğrenin

İlk olarak, uygulamanın istemci kullanıcı arayüzüne göz atın.

Yeni Chrome sekmesinde:

  1. Geliştirici Araçları'nı açmak için "Control+Shift+J" (veya Mac'te "Command+Option+J") tuşlarına basın. Konsol sekmesini tıklayın.

  2. Kullanıcı arayüzündeki düğmeleri tıklamayı deneyin (çıkış için Chrome geliştirici konsolunu kontrol edin).

    • Service Worker'ı kaydetme, arıza proje URL'nizin kapsamı için bir hizmet çalışanı kaydeder. Hizmet çalışanının kaydını iptal et, hizmet çalışanının kaldırılmasına neden olur. Push aboneliği eklenmişse bu abonelik de devre dışı bırakılır.

    • Push'a abone ol, push aboneliği oluşturur. Yalnızca bir hizmet çalışanı kaydedildiğinde ve istemci kodunda VAPID_PUBLIC_KEY sabit değeri bulunduğunda kullanılabilir. Bu konuda daha sonra bilgi edineceğiz. Bu nedenle, bu değeri henüz tıklayamazsınız.

    • Etkin bir push aboneliğiniz olduğunda Mevcut aboneliği bilgilendir, sunucunun uç noktasına bildirim göndermesini ister.

    • Tüm abonelikleri bildir, sunucuya veritabanındaki tüm abonelik uç noktalarına bildirim göndermesini söyler.

      Bu uç noktalardan bazılarının etkin olmayabileceğini unutmayın. Sunucu bildirim gönderdiğinde aboneliğin kaybolması her zaman mümkündür.

Sunucu tarafında neler olduğuna bakalım. Sunucu kodundan gelen mesajları görmek için Glitch arayüzündeki Node.js günlüğüne bakın.

  • Glitch uygulamasında Araçlar -> Günlükler'i tıklayın.

    Büyük olasılıkla Listening on port 3000 gibi bir mesaj görürsünüz.

    Canlı uygulama kullanıcı arayüzünde Geçerli aboneliği bildir veya Tüm abonelikleri bildir'i tıklamayı denediyseniz şu mesajı da görürsünüz:

    TODO: Implement sendNotifications()
    Endpoints to send to:  []
    

Şimdi birkaç kod inceleyelim.

  • public/index.js, tamamlanmış müşteri kodunu içeriyor. Özellik algılama gerçekleştirir, hizmet çalışanını kaydedip iptal eder ve kullanıcının push bildirimleri aboneliğini kontrol eder. Ayrıca, yeni ve silinen aboneliklerle ilgili bilgileri de sunucuya gönderir.

    Yalnızca sunucu işlevselliği üzerinde çalışacağınızdan bu dosyayı düzenlemeyeceksiniz (VAPID_PUBLIC_KEY sabit değerini doldurmak dışında).

  • public/service-worker.js, push etkinliklerini yakalayan ve bildirimleri görüntüleyen basit bir hizmet çalışanıdır.

  • /views/index.html, uygulamanın kullanıcı arayüzünü içerir.

  • .env, başlatıldığında Glitch'in uygulama sunucunuza yüklediği ortam değişkenlerini içerir. Bildirim göndermek için .env alanını kimlik doğrulama ayrıntılarıyla dolduracaksınız.

  • Bu codelab sırasında çalışmanızın çoğunu yapacağınız dosya server.js olacaktır.

    Başlangıç kodu basit bir Express web sunucusu oluşturur. Sizin için, kod yorumlarında TODO: ile işaretlenmiş dört YAPILACAK öğe var. Şunları yapmanız gerekiyor:

    Bu codelab'de bu TODO öğelerini tek tek inceleyeceksiniz.

VAPID ayrıntılarını oluşturma ve yükleme

İlk TODO öğeniz, VAPID ayrıntılarını oluşturmak, bunları Node.js ortam değişkenlerine eklemek ve istemci ile sunucu kodunu yeni değerlerle güncellemektir.

Arka plan

Kullanıcılar bildirimlere abone olduğunda uygulamanın ve sunucusuna güvenmeleri gerekir. Ayrıca kullanıcıların, bir bildirim aldıklarında aboneliğin ayarlandığı uygulamadan bir bildirim aldığından da emin olması gerekir. Ayrıca, bu kullanıcıların, bildirim içeriğini sizden başka kimsenin okuyamayacağına güvenmesi gerekir.

Push bildirimlerini güvenli ve gizli hale getiren protokol, Web Push için Voluntary Application Server Identification (VAPID) olarak adlandırılır. VAPID; uygulamaların, sunucuların ve abonelik uç noktalarının kimliğini doğrulamak ve bildirim içeriğini şifrelemek için ortak anahtar kriptografisini kullanır.

Bu uygulamada, VAPID anahtarları oluşturmak ve bildirimleri şifreleyip göndermek için web-push npm paketini kullanacaksınız.

Uygulama

Bu adımda, uygulamanız için bir çift VAPID anahtarı oluşturun ve bunları ortam değişkenlerine ekleyin. Sunucuya ortam değişkenlerini yükleyin ve ortak anahtarı istemci koduna sabit değer olarak ekleyin.

  1. Bir çift VAPID anahtarı oluşturmak için web-push kitaplığının generateVAPIDKeys işlevini kullanın.

    server.js'de, aşağıdaki kod satırlarındaki yorumları kaldırın:

    server.js

    // Generate VAPID keys (only do this once).
    /*
     * const vapidKeys = webpush.generateVAPIDKeys();
     * console.log(vapidKeys);
     */
    const vapidKeys = webpush.generateVAPIDKeys();
    console.log(vapidKeys);
    
  2. Glitch uygulamanızı yeniden başlattıktan sonra, oluşturulan anahtarları Glitch arayüzündeki Node.js günlüğüne gönderir (Chrome konsoluna değil). VAPID anahtarlarını görmek için, Glitch arayüzünde Araçlar -> Günlükler'i seçin.

    Genel ve özel anahtarlarınızı aynı anahtar çiftinden kopyaladığınızdan emin olun.

    Glitch, kodunuzu her düzenlediğinizde uygulamanızı yeniden başlatır. Bu nedenle, oluşturduğunuz ilk anahtar çifti, arkasından daha fazla çıkış çıktıkça görünümün dışına kayabilir.

  3. .env dosyasındaki VAPID anahtarlarını kopyalayıp yapıştırın. Anahtarları çift tırnak ("...") içine alın.

    VAPID_SUBJECT için "mailto:test@test.test" girebilirsiniz.

    .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"
    
  4. VAPID anahtarlarını yalnızca bir kez oluşturmanız gerektiğinden, server.js'de bu iki kod satırını tekrar yorumlayın.

    server.js

    // Generate VAPID keys (only do this once).
    /*
    const vapidKeys = webpush.generateVAPIDKeys();
    console.log(vapidKeys);
    */
    const vapidKeys = webpush.generateVAPIDKeys();
    console.log(vapidKeys);
    
  5. server.js dosyasında, ortam değişkenlerindeki VAPID ayrıntılarını yükleyin.

    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
    }
    
  6. Ortak anahtarı kopyalayıp istemci koduna da yapıştırın.

    public/index.js dosyasında, .env dosyasına kopyaladığınız VAPID_PUBLIC_KEY değeri için aynı değeri girin:

    public/index.js

    // Copy from .env
    const VAPID_PUBLIC_KEY = '';
    const VAPID_PUBLIC_KEY = 'BN3tWzHp3L3rBh03lGLlLlsq...';
    ````
    

Bildirim gönderme işlevini uygulayın

Arka plan

Bu uygulamada bildirim göndermek için web-push npm paketini kullanacaksınız.

Bu paket, webpush.sendNotification() çağrıldığında bildirimleri otomatik olarak şifrelediği için endişelenmeniz gerekmez.

web-push, bildirimler için birden fazla seçeneği kabul eder. Örneğin, iletiye üstbilgi ekleyebilir ve içerik kodlamasını belirtebilirsiniz.

Bu codelab'de yalnızca aşağıdaki kod satırlarıyla tanımlanan iki seçenek kullanacaksınız:

let options = {
  TTL: 10000; // Time-to-live. Notifications expire after this.
  vapidDetails: vapidDetails; // VAPID keys from .env
};

TTL (geçerlilik süresi) seçeneği, bir bildirimde bir geçerlilik sonu zaman aşımı ayarlar. Bu, sunucunun artık alakalı olmamasının ardından kullanıcıya bildirim gönderilmesini önlemesinin bir yoludur.

vapidDetails seçeneği, ortam değişkenlerinden yüklediğiniz VAPID anahtarlarını içerir.

Uygulama

server.js'de, sendNotifications işlevini şu şekilde değiştirin:

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() bir söz verdiğinden hata işlemeyi kolayca ekleyebilirsiniz.

server.js'de, sendNotifications işlevini tekrar değiştirin:

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} `);
    });
  });
}

Yeni abonelikleri yönetme

Arka plan

Kullanıcı push bildirimlerine abone olduğunda şunlar gerçekleşir:

  1. Kullanıcı, Push için abone ol'u tıklar.

  2. İstemci, benzersiz, sunucuya özel bir subscription nesnesi oluşturmak için VAPID_PUBLIC_KEY sabitini (sunucunun genel VAPID anahtarı) kullanır. subscription nesnesi şöyle görünür:

       {
         "endpoint": "https://fcm.googleapis.com/fcm/send/cpqAgzGzkzQ:APA9...",
         "expirationTime": null,
         "keys":
         {
           "p256dh": "BNYDjQL9d5PSoeBurHy2e4d4GY0sGJXBN...",
           "auth": "0IyyvUGNJ9RxJc83poo3bA"
         }
       }
    
  3. İstemci, /add-subscription URL'sine bir POST isteği gönderir. Bu istek, abonelikte dizeleştirilmiş JSON olarak yer alır.

  4. Sunucu, dize haline getirilmiş subscription öğesini POST isteğinin gövdesinden alır, JSON'a geri ayrıştırır ve abonelik veritabanına ekler.

    Bu veritabanı, abonelikleri kendi uç noktalarını anahtar olarak kullanarak depolar:

    {
      "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: { ... }
      },
    }

Artık sunucu, yeni abonelik bildirim göndermek için kullanılabilir.

Uygulama

Yeni abonelik istekleri, bir YAYIN URL'si olan /add-subscription yoluna gider. server.js'de bir saplama yönlendirme işleyicisi görürsünüz:

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);
});

Uygulamanızda bu işleyicinin yapması gerekenler:

  • İsteğin gövdesinden yeni aboneliği alın.
  • Etkin abonelikler veritabanına erişme.
  • Yeni aboneliği etkin abonelikler listesine ekleyin.

Yeni abonelikleri işlemek için:

  • server.js'de, /add-subscription için rota işleyicisini aşağıdaki gibi değiştirin:

    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);
    });

Abonelik iptallerini işleme

Arka plan

Sunucu, bir aboneliğin ne zaman devre dışı bırakıldığını her zaman bilmez. Örneğin, tarayıcı Service Worker'ı kapattığında bir abonelik silinebilir.

Ancak sunucu, uygulama kullanıcı arayüzü üzerinden iptal edilen abonelikleri bulabilir. Bu adımda, veritabanından abonelik kaldırma işlevini uygulayacaksınız.

Bu şekilde sunucu, var olmayan uç noktalara çok sayıda bildirim göndermekten kaçınır. Basit bir test uygulamasında bunun gerçekten önemi olmadığı kesin, ancak daha büyük ölçekte önem kazanıyor.

Uygulama

Abonelik iptali istekleri /remove-subscription YAYIN URL'sine gelir.

server.js'deki saplama yönlendirme işleyicisi şuna benzer:

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);
});

Uygulamanızda bu işleyicinin yapması gerekenler:

  • İsteğin gövdesinden iptal edilen aboneliğin uç noktasını alın.
  • Etkin abonelikler veritabanına erişme.
  • İptal edilen aboneliği etkin abonelikler listesinden kaldırın.

İstemciden gelen POST isteğinin gövdesinde, kaldırmanız gereken uç nokta bulunur:

{
  "endpoint": "https://fcm.googleapis.com/fcm/send/cpqAgzGzkzQ:APA9..."
}

Abonelik iptalleriyle ilgilenmek için:

  • server.js'de, /remove-subscription için rota işleyicisini aşağıdaki gibi değiştirin:

    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);
  });