GAミント至上主義

Web Monomaniacal Developer@ビズオーシャン

続vue-router + Firebaseで404ページをちゃんとやる方法を考える

夏アニメが終わってしまい気持ちが落ち込む季節。以前の記事で、Firebaseで使われる404.htmlにリダイレクトして満足していたが、404.htmlへのアクセスはFirebase Hostingはステータスコード200を返してしまい、Googleにインデックスされてしまっていた。

uyamazak.hatenablog.com

404.htmlがなぜ200を返すか

Firebaseではファイルが無いときに404.htmlの内容を返してくれるが、404.htmlに直接アクセスした場合、404.htmlというファイルは存在するので200を返すのは当然といえば当然。

Hosting 動作をカスタマイズする  |  Firebase

しかし、Vue等でSPAにする場合、下記のような設定の必要があり、ファイルが存在しないはすべてindex.htmlにリライトしてしまう。
firebase.json

// 省略
    "rewrites": [
      {
        "source": "**",
        "destination": "/index.html"
      }
    ]
// 省略

そのため、上記404.htmlの機能が動くことは無いのだった。

検索エンジンのインデックスを防ぐ

そもそも404.htmlが表示されることはないので、カスタム404.htmlを置く必要もないのかもしれない。
でも念のため、デフォルトよりはいいので置いておきたい。
リライト先のindex.htmlが無いと出るのかな?(未確認)

で、404.htmlがインデックスされてしまう問題はシンプルにmetaタグでなんとかする。
404.html

<meta name="robots" content="noindex">

Firebase Functionsで404のレスポンスを返す

いろいろ試したけどfirebase.jsonの設定ではステータスコードを変えられなかった。
そのため大がかりだけど、Functionsを使う。
そんなアクセスは無いだろうから料金もほとんど発生しないはず。

とりあえずcontentは直書きして404で返すfunctionを作る。

index.js

exports.notFound = functions.https.onRequest((req, res) => {
  res.status(404).send(`<!DOCTYPE html><!-- functions -->
  <html lang="ja">
    <head>
      <meta charset="utf-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="robots" content="noindex">
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <title>404 - ページが見つかりません - yagish</title>
    </head>
    <body>
        <h1>404</h1>
        <p>お探しのページが見つかりませんでした。</p>
        <p><a href="/">トップページ</a>から再度お試しください。</p>
    </body>
  </html>
`);
})

/404に来たら、Functionsに繋がるように設定する。

firebase.json

    "rewrites": [
      {
        "source": "/404",
        "function": "notFound"
      },
      {
        "source": "**",
        "destination": "/index.html"
      }
    ]


vue-router側の設定は前回と同じで、設定が無いパスは404にリダイレクトしておく。
stackoverflow.com

これらの設定でvue-routerで存在しないページに行ったら/404にリダイレクトされ、Vue側で404を表示、直接/404にアクセスした場合はFunctionsで404エラーを返すことができた。
SPAやFirebaseのSEO周りはまだまだ情報が少ないので自分でなんとかしていくしかなさそう。

Vue.js入門 基礎から実践アプリケーション開発まで

Vue.js入門 基礎から実践アプリケーション開発まで

WEB+DB PRESS Vol.105

WEB+DB PRESS Vol.105

  • 作者: 小笠原みつき,西村公宏,柳佳音,志甫侑紀,池田友洋,木村涼平,?橋優介,大塚雅和,飯塚直,吉川竜太,末永恭正,久保田祐史,浜田真成,穴井宏幸,大島一将,桑原仁雄,牧大輔,池田拓司,はまちや2,竹原,WEB+DB PRESS編集部
  • 出版社/メーカー: 技術評論社
  • 発売日: 2018/06/23
  • メディア: 単行本
  • この商品を含むブログを見る

Vue CLI 3でビルド時に古いファイルを消さずにとっておく

沢城みゆきといえばギャラクシーエンジェルのミントの声優ですが、ServiceWorkerを使ったPWAを運用していると、強力なキャッシュに困ります。

ServiceWorkerのライフサイクルは通常のページとは違い、普通の再読み込みでは更新されないこともあったりと複雑です。

developers.google.com

qiita.com

多々あるキャッシュ問題の一つとして、デプロイ後、ServiceWorkerのキャッシュが残ったブラウザでアクセスすると、古いjsファイルにアクセスしようとして404エラーを出すことがあった。

当然jsが読めないとエラーが起きて実行できなくなるためvue-routerによるページ遷移等もできず、再読み込みでも変わらない時もあるので、ブラウザ再起動しかなく、結構やばい状況。

これは

vue-cli-service build

を実行すると、デフォでは古いファイルをすべて削除してから書き出しされるため。

当然それをFirebaseにデプロイするとサーバー上からも古いファイルが消えてしまう。

cli.vuejs.org

これは--no-cleanオプションをつけることで回避できた。うまい検索キーワードが見つからず時間がかかった。

--no-clean    do not remove the dist directory before building the project

でもこれをつけてビルドしまくると、どんどんファイルが増えていくので、アプリによって違うけど、期間とか、世代数とか何らかの指標で古いファイルを消していく必要がある。

とりあえずyagishは本番デプロイは現状多くないので、しばらくは手動で2,3世代以降を消していけばいいかなと思う。

基礎から学ぶ Vue.js

基礎から学ぶ Vue.js

Google Hangouts ChatでAPIからスレッドを指定して書き込む

以前のこの記事の続き。チャットから返信できるようになったけど、ユーザーの書き込みが毎回新スレッドになるため、同一ユーザーの書き込みは同じスレッドにしたかった。

uyamazak.hatenablog.com

要約としては、書き込み成功のレスポンスにthread.nameが入っているので、それを保存し、2回目以降はそれをリクエストに追加すればおk。

メッセージ全体流れ

大きく分けて1.Firestore→Chat、2.Chat→Firestoreの流れとなる。

前者はWebhook、後者はbotを使うため大きく異なり自分でもよく分からなくなるので整理する。

1. ユーザーがチャットに書き込む(Firestore→Chat)

ユーザーがメッセージをFirestoreに書き込み、Cloud Functionsでそれを受け取りChatのWebhookにポストする

ユーザー側の画面はこんな感じで、メッセージフォームと自分と管理者を合わせたメッセージ履歴(Firestoreのデータ)がある
f:id:uyamazak:20181001115202p:plain
※画面は開発中のものです

2.管理者からユーザーへの返信(Chat→Firestore)

Chatに招待したCloud Functionsのbotに@指定でユーザーIDとメッセージを送る。
botでFirebase-adminを用い、ユーザーIDを使ってメッセージを書き込む。

Chatでのやりとり画面はこんな感じになる。
f:id:uyamazak:20181001115230p:plain
指定されたIDがなかった場合エラーを返す機能も付けた。

2回目以降にスレッドを指定して書き込む

1のWebhookのスクリプトで書き込みが成功すると、Messageオブジェクトが返ってきて、その中にthread.nameがあるのでそれをFirestoreに保存しておく。
こんな感じで取れる。awaitを使いたかったのでrequest-promise-nativeを使用した

const rp = require('request-promise-native');
const resultBody = await rp.post(options)
threadName = resultBody.thread.name

これをFirestoreの該当ユーザーのドキュメントに書き込んでおく。
今回は/usedata/{uId}/chatmetaみたいなドキュメントパスを使用した。

2回目以降はChatに書き込む前にこのドキュメントの有無を見て、あったら

if(threadName){
  data['thread'] =  {name:threadName}
}

のような感じでPOSTするdataに追加する。

全体のソースを貼りたかったけど汚すぎたので止めた。デザインができるまでには整理したい。
Chat側で指定したスレッドやそもそもルーム自体を消してしまった場合は処理できてない。

スレッド指定の書き込み方法はドキュメントが見つからなかったけど、Messageオブジェクト通りにやれば他のオプションもいろいろできそう。

REST Resource: spaces.messages  |  Hangouts Chat API  |  Google Developers