WebPush通知を試してみる

# 概要

最小構成でサイト作り、Firebase Hostingで公開してPush通知を飛ばしてみます。

# 前提

* Node.jsインストール済み

# プロジェクト作成

今回用に適当なディレクトリを作る＆ディレクトリに移動して以下を実行。

```
$ npm init -y
```

以下のファイルを作成。所々コメントアウトしたり`xxxx`と書いてありますが、こちらはFirebase側の用意ができたら書き換える箇所になります。

public/index.html

```html
<!doctype html>
<html>

<head>
    <meta charset="utf-8">
    <title>test</title>
    <link rel="manifest" href="manifest.json">
</head>

<body>
    <p>
        <input type="button" id="allow" value="allow">
        <input type="button" id="delete" value="delete token">
    </p>
    <!-- script src="https://www.gstatic.com/firebasejs/5.9.1/firebase.js"></script>
<script>
    var config = {
        apiKey: "xxxx",
        authDomain: "xxxx",
        databaseURL: "xxxx",
        projectId: "xxxx",
        storageBucket: "xxxx",
        messagingSenderId: "xxxx"
    };
    firebase.initializeApp(config);
</script -->
    <script src="./scripts/main.js"></script>
</body>

</html>
```

public/manifest.json

```json
{
    "gcm_sender_id": "103953800507"
}
```

public/firebase-messaging-sw.js

```js
importScripts('https://www.gstatic.com/firebasejs/5.5.4/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/5.5.4/firebase-messaging.js');

firebase.initializeApp({
    'messagingSenderId': 'xxxx'
});

const messaging = firebase.messaging();

messaging.setBackgroundMessageHandler(function(payload) {
    const notificationTitle = payload.notification.title;
    const notificationOptions = {
        body: payload.notification.body,
        icon: payload.notification.icon
    };
    return self.registration.showNotification(notificationTitle, notificationOptions);
});
```

public/scripts/main.js

```js
const btnAllow = document.getElementById('allow');
const btnDelete = document.getElementById('delete');
const messaging = firebase.messaging();
// messaging.usePublicVapidKey("xxxxxx");

requestPermission = () => {
    messaging.requestPermission().then(() => {
        messaging.getToken().then(token => {
            console.log(token);
        }).catch(err => { });
    }).catch(err => { });
}

deleteToken = () => {
    messaging.getToken().then(currentToken => {
        messaging.deleteToken(currentToken).then(() => {
            console.log('delete token.');
        }).catch(err => { });
    }).catch(err => { });
}

btnAllow.addEventListener('click', requestPermission);
btnDelete.addEventListener('click', deleteToken);
```

public/images/square.png
※96x96ぐらいの画像を配置

# Firebase側の用意

```
$ npm install firebase-tools
$ .\node_modules\.bin\firebase login
  -> y
```

ブラウザでGoogleアカウントを選択してアクセス権のリクエストを許可します。

> Woohoo!
Firebase CLI Login Successful

って表示されればOKです。Terminal上では、

> Success! Logged in as [your account]

と表示されていると思います。続いて初期化をしていきます。

```
$ .\node_modules\.bin\firebase init
```

```
Are you ready to proceed?
 -> y
Which Firebase CLI features do you want to setup for this folder? Press Space to select features, then Enter to confirm your choices.
 ->  ( ) Database: Deploy Firebase Realtime Database Rules
     ( ) Firestore: Deploy rules and create indexes for Firestore
     ( ) Functions: Configure and deploy Cloud Functions
     (*) Hosting: Configure and deploy Firebase Hosting sites
     ( ) Storage: Deploy Cloud Storage security rules
Select a default Firebase project for this directory:
 -> [create a new project]
What do you want to use as your public directory?
 -> public
Configure as a single-page app (rewrite all urls to /index.html)?
 -> n
File public/index.html already exists. Overwrite?
 -> n
```

`Please visit https://console.firebase.google.com to create a new project, then run firebase use --add`と表示されるので、アクセスして新しいプロジェクトを追加します。UIは非常にわかりやすいので特に困ることは無いと思います。今回は以下の通り設定しました。

> プロジェクト名：hoge
アナリティクスの場所：日本
Cloud Firestoreのロケーション：nam5(us-central)
チェックボックス：全部ON

作り終わったらTerminalに戻ります。

```
$ .\node_modules\.bin\firebase use --add
```

```
Which project do you want to add?
 -> 先程作成したプロジェクトのプロジェクトIDを選択
What alias do you want to use for this project? (e.g. staging)
 -> hoge
```

これでデプロイ準備はOKです。デプロイは後回しにしてPush通知の実装を進めていきます。

# Push通知の実装とデプロイ

ここからが本題です。
[JavaScript Firebase Cloud Messaging クライアント アプリを設定する | Firebase](https://firebase.google.com/docs/cloud-messaging/js/client?authuser=0)を参考にしながら進めていきます。

## 鍵ペアの生成

> 1. Firebase コンソールの [設定] ペインで [クラウド メッセージング] タブを開き、[ウェブ設定] セクションまでスクロールします。  
2. [ウェブプッシュ証明書]タブで、[鍵ペアを生成] をクリックします。鍵ペアが生成されたという通知がコンソールに表示され、追加した公開鍵の文字列と日付が表示されます。

ここは書いてある通りで問題なく進められますね。

## firebase.initializeApp

Firebaseのプロジェクトページでプロジェクトタイトルの近くにある「＋アプリを追加」をクリックしてWebをクリックすると以下のようなコードが表示されます（実際は全ての値がセットされている状態です）。

```html
<script src="https://www.gstatic.com/firebasejs/5.9.1/firebase.js"></script>
<script>
  // Initialize Firebase
  var config = {
    apiKey: "<API_KEY>",
    authDomain: "<PROJECT_ID>.firebaseapp.com",
    databaseURL: "https://<DATABASE_NAME>.firebaseio.com",
    projectId: "<PROJECT_ID>",
    storageBucket: "<BUCKET>.appspot.com",
    messagingSenderId: "<SENDER_ID>"
  };
  firebase.initializeApp(config);
</script>
```

これを作成済みのindex.htmlのコメントアウトしている部分と置き換えます。firebase-messaging-sw.jsのmessagingSenderIdも同じように書き換えます。

## messaging.usePublicVapidKey

鍵ペアの生成の項目で作成した鍵ペアをmain.jsのmessaging.usePublicVapidKeyのパラメータに設定します。

## Firebase Hostingにデプロイ

以下のコマンドでデプロイします。

```
$ .\node_modules\.bin\firebase deploy
```

`Hosting URL: https://hoge-fb2b0.firebaseapp.com`と表示されるのでアクセスしてみましょう。allowボタンをクリックすると通知許可が求められるので「許可」を選択してください。developer toolsで確認するとトークンがconsoleに表示されていると思うのでこれをコピーしておきます。delete tokenをクリックして一度トークンを削除して再度allowで払い出すとトークンは変わります。

# 通知を送る

`https://fcm.googleapis.com/fcm/send`にPOSTすることでターゲットに通知を送ることができます。先程デプロイしたページでトークンを払い出してタブを閉じておきましょう。以下の設定でPOSTすると通知が届きます。

```
URL:
https://fcm.googleapis.com/fcm/send

HTTP Method:
POST

Headers:
  Content-Type: application/json
  Authorization: key=xxxxx

Body:
  {
    "notification":
  {
    "title": "title",
    "body": "body",
    "icon": "https://push-test-f2b86.firebaseapp.com/images/square.png",
    "click_action": "https://push-test-f2b86.firebaseapp.com/"
  },
    "to": "xxxxx"
  }
```

AuthorizationのxxxxxはFirebaseのプロジェクト設定→クラウドメッセージングのプロジェクト認証情報のサーバーキーを設定します。Bodyのtoは先程Webページで払い出したトークンを設定します。これで通知が届くと思います。

# サイトの停止

デプロイしたサイトの停止は以下のコマンド。

```
$ .\node_modules\.bin\firebase hosting:disable
```

# 最後に

通知自体の実装は難しくはありませんでしたが、どう使うか（運用設計）が難しいやつですねこれ。通知許可を出したユーザ全員に送信する時は全員分API叩くわけにもいかないと思いますし...後日調べてみようと思います。