bpeldi2oerkd8の開発日誌

とあるひよっこエンジニアの成長の記録。

自作ブログの2022年6月のアクセス状況を振り返る

毎月恒例の自作ブログのアクセス記録の記事です。

私は自作のブログをGatsby.jsで作っており、そのアクセス状況を毎月振り返っています。

4月
developer-bpeldi2oerkd8.hatenablog.com

5月
developer-bpeldi2oerkd8.hatenablog.com

今回は6月の分をまとめます。

6月に投稿した記事

6月に出した記事は3つです。

6/5 ノーコード・ローコードでのアプリ開発の記事
6/12 Netlifyでブログランキングサイトに自動で最新記事を反映する方法を紹介する記事
6/30 システム内製化事例についてまとめた記事

1週間に1度のペースで記事投稿しようと考えていたのですが、3つ目の記事に時間がかかり、結果的に3記事になりました。

結果

6月分の結果は以下のようになりました。

表示回数 約11000回(+6600)
クリック数 約1200回(+600)
初めて表示されたページ 6(-3)

表示回数・クリック数ともに先月の2倍以上になりました。

先月と同様、1つの記事が検索上位をキープし続けているほか、もう1つの記事の検索順位・アクセス数ともに上昇しているのがアクセス数増加の主な要因です。
先月はアクセス数が3桁の記事が1つだったところが、今月は2つとなりました。
6月は好調でしたが、7月にこの勢いが継続できるのか心配なところです。

感想

アクセス数の上昇が続いており、良い結果だったと思います。
アクセス数が1つの記事頼みだった先月に比べ全体的に増加傾向で、このまま全体のアクセス数が増加してほしいと思っています。

検索エンジンからの信頼を高めるためにも、定期的に記事を投稿していき、検索上位をとれる記事を増やしていきたいと思います。
そのためにも、今月こそは1週間に1度のペースで記事投稿できるようにしたいです。

最後に、自作のブログを載せておくので、もし興味がある方は見ていただけると嬉しいです!
bel-itigo.com

自作ブログの2022年5月のアクセス状況を振り返る

私は今年の2月に自作ブログを立ち上げました。
Gatsbyで作り、Netlifyにデプロイしています。

bel-itigo.com

今回は5月分のアクセス数が出たので、分析します。

5月に出した記事

5月に出した記事は2つです。

1. Vue3とReactの書き方の違いを比較した記事
2. TOEIC勉強におすすめの単語アプリを紹介した記事

結果

5月分の結果は以下のようになりました。

表示回数 約4400回(+2400)
クリック数 約600回(+400)
初めて表示されたページ 9(+7)

緩やかなペースではありますが、順調に増えています。

内訳としては前回と同じく1つの記事のアクセス数が大きい状況です。
その記事は多くの検索ワードで上位5位以内には入れているようです。

そのほかの記事についてもこの記事に引っ張られる形で、徐々にではありますがアクセス数が増えてきています。
ただ、1番大きくアクセスを集めている記事と比べると、アクセス数はようやく2桁になる記事が増えてきたというレベルです。

感想

アクセス数が緩やかなペースではありますが、増え続けているのはいい傾向だと思いました。
ただ、1つの記事に頼っている状況が続いており、やはりある程度記事を出して新たなヒット記事を出す必要があると感じました。
現在2週間に1度のペースで投稿を続けていますが、頑張って1週間に1度のペースまで上げていきたいと思います。

自作ブログの2022年4月のアクセス状況を振り返る

5月も半ばになりましたが、先月の自作ブログのアクセス数が出たので振り返ります。

私は今年の2月に自作のブログを作りました。
bel-itigo.com

しかし、3月のときはほとんどアクセスが集まらない状態でした。
その時の記事がこちらです。
developer-bpeldi2oerkd8.hatenablog.com

今回はそれから1か月後の4月分のアクセス状況がGoogleから送られてきたので、その結果を紹介します。

結果

結果をまとめると以下のようになりました。

表示回数 約2000回
クリック数 約200回
初めて表示されたページ 2

まだまだ少ないですが、3月の時に比べるとだいぶ増えてきました。
細かく見ると一つの記事が非常に伸びていて、複数ワードで検索上位に来ているようです。
しかし、それ以外の記事はアクセス数がそれぞれ10以下で記事によってかなり差が出る結果になりました。
地道に投稿しながらヒット記事を出していくしかないようです。

ちなみにドメインパワーも測定してみましたがまだ0に近いです。
被リンクを獲得するために、より質の高い記事を書く必要がありそうです。

感想

やはりこの結果を見て思ったのが、量よりも質の高い、読者に求められている記事を投稿することが大事だと思いました。
また、被リンクが一つあるだけでも検索上位に来やすくなるため、質の高い被リンクの重要性を感じました。
目標に届くまでまだ時間がかかりそうですが、引き続き頑張っていきたいと思います。

自作のブログのアクセスを集めるのが難しすぎるという話

自作ブログの引っ越しをするはずがうまくいかなかったという話です。
ブログの引っ越しを決めた経緯は前回の記事に書いてあります。
developer-bpeldi2oerkd8.hatenablog.com

恥ずかしい話ですが、自作ブログ開設から1か月が経ってもほとんどアクセスが集まりません。
今回はなぜアクセスが集まらないのかを書いていきます。

アクセスが集まらない原因

端的に言うと、ドメインパワーが弱すぎることが理由です。
ドメインとは、URLにあるhttps://example.comexample.comの部分です。
例えば、はてなブログだとhatenablog.comです。

ドメインパワーとは、ドメインの信頼度みたいなものです。
これは、被リンク数・更新頻度・サイトの滞在時間・取得してからの期間など様々な要素を考慮して決まります。
このスコアが高いほど、検索エンジンの上位に来やすく、サイトにアクセスしてもらえることが多いというわけです。

今回、新しくブログを作るにあたって新しい独自ドメインを取りました。
bel-itigo.com

このドメインは取得してから1か月しか経っていないため、ドメインスコアが0.1とほぼ0です。
つまり、記事を公開しても検索結果の上位に表示されず、そもそも知ってもらえる可能性が低いというわけです。

それにたいして、developer-bpeldi2oerkd8.hatenablog.comのドメインパワーは21.0です。
f:id:bpeldi2oerkd8:20220319172734j:plain
このくらいのスコアがあると、検索結果の1ページ目に入ることは多くなります。
なので、アクセス数に違いがあるというわけです。

ドメインパワーを上げるには

では、ドメインパワーを上げるにはどうすればよいかという話ですが、これはドメインパワーの高いサイトから多くの被リンクをもらうしかありません。
それか地道に更新を続けて検索エンジンからの評価を高めていくしかないと思います。
そのためには、やはり質のいい記事を書き続けていくしかありません。
長い道のりになりそうですが、くじけずに頑張っていきます。
こちらのブログにも定期的に成果を載せたいと思います。

はてなブログからお引越しします

しばらくブログを更新していませんでした。
それなのにいきなりですが、はてなブログから自作のブログにお引越しをすることにしました。
更新していない間何があったのか、そしてはてなブログからの引っ越しを決めた経緯などについて書いていきます。

更新していない間何があったのか

前回の記事は半年以上前です。
developer-bpeldi2oerkd8.hatenablog.com

前回の記事の内容は、自作のサービスを完成させ、インターンシップに参加しようと思うというところで終わっていました。
結論から言うと、インターンシップ先が無事決まり、現在も働かせてもらっています。
フレームワークを使った開発をし、バックエンドを中心に商用サービスを開発しています。

また、趣味で開発も継続しています。
新たにReactに手を出し、モダンなフロントエンドの勉強も始めました。
その中で、Reactを使って技術記事チェッカーを完成させました。
github.com

そうした中で、自作のブログを開発したいと思うようになりました。
GatsbyとReactを使ってブログが作れると知り、勉強のために自作のブログを作りたいと思うようになりました。
このため、今月から地道に開発を続けてきました。
完成したサイトがこちらです。
ベル15の開発ブログ

デザインや機能がまだまだですが、少しずつ改善したいと思っています。

引っ越しを決めた経緯

引っ越しの細かい経緯はこちらに載せました。
bel-itigo.com

要点だけ伝えさせていただくと、技術力の向上ともっと自由にサイトをカスタマイズしたいということです。
はてなブログは簡単にブログが始められ、機能も豊富で便利なサービスです。
ですが、簡単に使える分、(仕方のないことですが)制約があり、もっとブログをカスタマイズしたいと思い、引っ越しを決めました。
また、せっかくReactを勉強したのでその知識を活かしたいということもありました。

自作のブログを作っていて改めて思ったこと

実際にブログを作っていて思ったことが、はてなブログで当たり前に使えている機能を実装するのが大変だったことです。
タグ機能も実装に思ったよりも時間がかかりました。
また、まだ実装できていない機能も多くあります。
これらの機能で欲しい機能はこれから少しずつ追加していこうと思います。


というわけで、このブログはこの投稿が最後になる予定です。
これからは、ベル15の開発ブログで更新していく予定です。
このブログにある記事も引っ越すかはまだ未定です。

Herokuへのデプロイ - 自作サービスづくり11

今回もWeb開発に関する記事です。
前回はAPIにJWT認証を追加しました。
developer-bpeldi2oerkd8.hatenablog.com

今回は今までの変更内容を本番環境にデプロイしたいと思います。
本番環境としては前のデプロイでも用いたHerokuを使います。
developer-bpeldi2oerkd8.hatenablog.com

Herokuへのデプロイ

手順については、Heroku CLIのインストール後、以下のページに従ってデプロイするのみです。
devcenter.heroku.com
これを2つのシステムについて行いました。
どちらも問題なく動作しました。

完成したもの

Lattendance (Webサイト)

Lattendance-bot (ボットシステム(Slack用))

READMEの編集

実行手順をわかりやすくするため、それぞれのシステムのREADMEを編集しました。
実行手順のみでなく、使用した技術・機能・デモ動画についても掲載しています。

github.com

github.com

感想

今回でN予備校の教材をベースにしたシステムに、独自機能であるJWT認証付きのAPIを用いたbotによる出欠更新機能を追加することができました。
これでひとまず自己学習を終え、インターンへの参加を目指したいと思います。

今回までで以下のことが学べ、Web開発の基本となる知識が吸収できました。

今後はさらに次のような技術についても勉強していきたいと思っています。

JWT認証を導入したAPI実装 - 自作サービスづくり10

今回もWeb開発に関する記事です。
前回はデザイン改善とSlackとの連携機能の実装を行いました。
developer-bpeldi2oerkd8.hatenablog.com

ここでは、ロゴの作成とSlack連携登録・編集機能を作ったため、
今回はいよいよJWT認証を導入したAPIの実装に移ります。

構成

構成は以下のようになっています。
f:id:bpeldi2oerkd8:20210710205316j:plain

手順としては、

  1. /api/v1/login にアクセスし、lattendanceにJWTをリクエス
  2. lattendance上でJWTを発行し、JSONでJWTを返す
  3. /api/v1/schedulesにアクセス時(以前実装したAPIの利用時)にヘッダーに発行したJWTを追加しリクエス
  4. 送られてきたJWTをもとに認証し、OKの場合のみ結果を返す(NGの時はエラー内容を返す)

順番に実装していきます。

JWTの発行

JWTの発行・認証時に jsonwebtoken を用います。
ドキュメントを見ると、JWTを発行するために jwt.sign() 、JWTを検証する ためにjwt.verify()を用いることが書いてあります。

これに従って、まずJWTの発行を実装します。
まず、JWTを使用できるようにするため、以下のようにapp.jsを変更します。

app.use(express.urlencoded({ extended: true }));

また、APIのログイン部分は以下の通りです。
事前に発行したチャンネルトークンとチャンネルIDをPOSTし、JWTを返します。

'use strict';
const express = require('express');
const router = express.Router();
const jwt = require('jsonwebtoken');
const Room = require('../../models/room');
//APIのシークレットキー
const serverAPISecret = process.env.SERVER_API_SECRET || require('../../secret_info/server_api_info');

router.post('/', (req, res) => {
  //roomIDとroomTokenを取得
  const roomId = req.body.roomId;
  const roomToken = req.body.roomToken;

  Room.findByPk(roomId)
  .then((room) => {
    if(room && room.roomToken === roomToken) {
      //JWTを生成
      const token = jwt.sign({roomId: roomId}, serverAPISecret, {expiresIn: '1h'});
      res.json({
        status: 'OK',
        data: {
          token: token
        }
      });
    }
    else {
      res.json({
        status: 'NG',
        error: {
          messages: ['認証エラー']
        }
      });
    }
  });
});

module.exports = router;

JWTの認証

今度はJWTの認証を入れます。
認証を行う関数をverifyToken()とし、ミドルウェアとして引数に入れます。
verifyTokenの実装はこちらです。

'use strict';
const jwt = require('jsonwebtoken');

//APIのシークレットキー
const serverAPISecret = process.env.SERVER_API_SECRET || require('../../secret_info/server_api_info');

function verifyToken(req, res, next) {
  const authHeader = req.headers["authorization"];

  //HeaderにAuthorizationがあるかチェック
  if (authHeader) {
    //Bearerのチェック
    if (authHeader.split(" ")[0] === "Bearer") {
      try {
        const decoded = jwt.verify(authHeader.split(" ")[1], serverAPISecret);
        const roomId = req.params.roomId;

        //roomIdの検証
        if (decoded.roomId === roomId) {
          next();
        }
        else {
          res.json({
            status: 'NG',
            error: {
              messages: ['検証エラー']
            }
          });
        }
      } catch (e) {
        res.json({
          status: 'NG',
          error: {
            messages: ['Tokenエラー']
          }
        });
      }
    }
    else {
      res.json({
        status: 'NG',
        error: {
          messages: ['ヘッダー形式エラー']
        }
      });
    }
  }
  else {
    res.json({
      status: 'NG',
      error: {
        messages: ['ヘッダーエラー']
      }
    });
  }
}

module.exports = verifyToken;

これを以下のようにミドルウェアとして挟むことで認証を入れています。

router.post('/:roomId/users/:slackId/dates/:dateString',
  verifyToken,
  (req, res, next) => {

APIのテスト

APIが正しく実装されているかテストします。
今回はJestではなく、VS Code上で手軽にREST APIのテストができるREST Clientを使います。
使い方はこちらのページが参考になります。
qiita.com

では、実際にリクエストを書いていきます。

# JWT発行テスト
POST http://localhost:8000/api/v1/login
Content-Type: application/json

{
  "roomId": "TEST0001",
  "roomToken": "事前に発行したチャンネルトークン"
}

###
# 認証がない場合の出欠確認テスト(認証エラー)
GET http://localhost:8000/api/v1/schedules/TEST0001/users/{ユーザーID}/dates/2021-07-02

###
# 認証がある場合の出欠確認テスト
GET http://localhost:8000/api/v1/schedules/TEST0001/users/{ユーザーID}/dates/2021-07-02
Authorization: Bearer {先ほど返ってきたJWT}

###
# 認証がある場合の出欠確認テスト
GET http://localhost:8000/api/v1/schedules/TEST0001/users/{ユーザーID}/dates/2021-07-03
Authorization: Bearer {先ほど返ってきたJWT}

###
# 認証がある場合の出欠更新テスト
POST http://localhost:8000/api/v1/schedules/TEST0001/users/{ユーザーID}/dates/2021-07-02
Authorization: Bearer {先ほど返ってきたJWT}
Content-Type: application/json

{
  "availability": 2
}

正しいJSONが返ってきました。

botの実装の変更と動作確認

APIにJWT認証が追加されたため、botの実装を変更しました。
まず、/api/v1/loginにアクセスし、JWTが返ってきてからヘッダーに追加しリクエストしています。
github.com

動作確認をします。
正しく結果が返ってくることが確認できました。
f:id:bpeldi2oerkd8:20210712234141j:plain


以上でJWT認証付きのAPIの実装が完了しました。
次回は、Herokuへのデプロイをしたいと思います。

デザイン改善とSlackとの連携機能の実装 - 自作サービスづくり9

今回もWeb開発に関する記事です。
前回は認証付きAPIを作る前準備を行いました。
developer-bpeldi2oerkd8.hatenablog.com

ここでは設計を行ったため、今回はAPIの実装の前にSlackとの連携に必要な情報を登録する機能の実装をします。

デザイン改善

その前にデザインの改善をします。
今のままでは味気ないので、トップページやマイページのデザインの改善に取り組みます。

まず、このサービスのロゴを考えました。
そのあと、自分の中で良いと思ったデザインを画像にしました。
画像にするために使ったものは、Inkscapeです。
点を結んでいくことで簡単に作成できるためおすすめです。

完成したロゴはこちらです。
f:id:bpeldi2oerkd8:20210711220023p:plain
サービス名であるLattendanceの頭文字Lと出席を意味するチェックマークを組み合わせたものです。

加えて、トップページはスライド式でサービスの名前と特徴が交互に入れ替わるものにしました。
これには、Bootstrapの「Carousel」を用いています。
f:id:bpeldi2oerkd8:20210711220517j:plain
f:id:bpeldi2oerkd8:20210711220539j:plain

つまづいた点は、文字を好きな位置に配置する方法です。
最初は「Carousel」のcaptionを用いる方法を考えたのですがうまくいかず、
「Carousel」に「Card」を入れることができることに気付いたため、最終的にはCardのImage overlaysを利用しました。

Slackとの連携登録機能の実装

Slackとの連携に必要なチャンネルIDとチャンネルのトークンを予定に紐づけます。
Roomテーブルを作成し、そこにroomIdカラムとroomTokenカラムを追加します。
さらに、ScheduleテーブルにroomIdカラムを追加し、roomIdを外部キーを設定します。

チャンネルIDを入力し、トークンを発行するように実装します。

router.post('/:scheduleId/new2', authenticationEnsurer, csrfProtection, (req, res, next) => {
  const scheduleId = req.body.scheduleId;
  const roomId = req.body.roomId.trim();

  //scheduleIdに紐づくscheduleにroomIdが登録されていないかチェック
  const noRoomId = (scheduleId) => {
    return new Promise((resolve, reject) => {
      Schedule.findByPk(scheduleId)
      .then((schedule) => {
        if(schedule)
          console.log('exist');
        else
          console.log('not exist');
        if(schedule && schedule.roomId === null) {
          resolve(schedule);
        }
        else {
          reject('This schedule is already linked to room ID.');
        }
      });
    });
  };

  //同じRoomIDが登録されていないかチェック
  const checkRoomId = (roomId) => {
    return new Promise((resolve, reject) => {
      Room.findByPk(roomId)
      .then((room) => {
        if(room) {
          reject('Room ID is registered.');
        }
        else {
          resolve();
        }
      });
    });
  };

  Promise.all([noRoomId(scheduleId), checkRoomId(roomId)])
  .then((result) => {
    let schedule = result[0];
    if(isMine(req, schedule)) {
      //roomTokenの生成
      const N = 40;
      const roomToken = crypto.randomBytes(N).toString('base64');

      //チャンネル情報の登録
      Room.create({
        roomId: roomId,
        roomToken: roomToken
      })
      .then(() => {
        //roomIdの登録
        schedule.roomId = roomId;
        schedule.save();

        res.render('slack-channel-linker-new2', { 
          schedule: schedule,
          roomToken: roomToken, 
          csrfToken: req.csrfToken()
        });
      });
    }
    else {
      const err = new Error('指定された予定がない、または、予定する権限がありません');
      err.status = 404;
      next(err);
    }
  })
  .catch((err) => {
    console.log(err);
    res.redirect(`/schedules/slack-channel-linker/${scheduleId}/new?alert=1`);
  });
});

細かい実装は以下の通りです。
github.com

実装後はこのようになります。
f:id:bpeldi2oerkd8:20210711224841j:plain
f:id:bpeldi2oerkd8:20210711224916j:plain

つまづいた点は、トークンを表示するページを無関係の方に表示させない方法です。
最終的には、isMineという自身が作った予定かを確認する関数に加えて、
1ページ目からpostした情報をもとに処理し、そのページのままレンダリングをすることで解決しました。
フォームにはCSRF対策も施してあるため、無関係のユーザーがトークンを発行するページにアクセスできないと思われます。

Slackとの連携変更機能の実装

次に、Slackとの連携設定の変更機能を実装します。
必要な機能としては、以下の3つです。

  • Slack連携の解除
  • チャンネルIDの変更
  • トークンの再発行

この機能それぞれについて実装しました。
細かい実装については以下の通りです。
実装は登録の際に使ったものを再利用してます。
github.com

また、予定の削除機能についてもRoomテーブルを加えたことで削除するデータが増えたため、変更しています。
github.com

実装後はこのような画面になります。
f:id:bpeldi2oerkd8:20210711230857j:plain
ここで変更したい項目のボタンを押すと、変更されます。


以上で、Lattendance上にSlack連携に必要な情報を登録することができるようになりました。
次回は、JWT認証を導入したAPI実装について取り組みたいと思います。

認証付きAPIを作る前準備 - 自作サービスづくり8

今回もWeb開発に関する記事です。
前回はbotを使った出欠確認・更新機能の実装を行いました。
developer-bpeldi2oerkd8.hatenablog.com

しかし、作成したAPIには認証がありません。
このため、URLさえわかってしまえば、APIを無関係の方が叩けてしまいます。
これは問題であるため、APIに認証機能を付け、
ユーザーが許可したシステムからしAPIを叩けないように変更します。

APIの認証方法

APIの認証について全く知らなかったため、調べてみました。
調べると、いくつか種類があります。
Basic認証やDigest認証、OAuthなど有名なものも多いですが、
今回はこちらのページにもあるように、
より一般的な手法であるJWT認証を取り入れることにしました。

JWTとは

JWTはJSON Web Tokenの略で、JSON形式で書かれたデータをBase64urlでエンコードした認証用のトークンです。
署名があるため、改ざん防止ができます。
詳しい説明はこちらのサイトが非常に参考になりました。
scgajge12.hatenablog.com

構成の考案

現在のAPI周りの構成は以下のようになっています。
f:id:bpeldi2oerkd8:20210610162127j:plain

ここに認証を付けます。
今回は jsonwebtoken というパッケージを使います。
このドキュメントを見ると、JWTを発行する jwt.sign() と、JWTを検証する jwt.verify() が必要なことがわかりました。

このため、まずJWTを発行する /api/v1/login にアクセスし、JWTを発行した後そのJWTを返し、
そのJWTをヘッダーに追加してAPIを利用するときに検証するという流れを考えました。
これを図にしたのが以下の図です。
f:id:bpeldi2oerkd8:20210710205316j:plain

ここで、JWTを発行する際に通常はユーザー名とパスワードを使うそうですが、
今回はSlack上のチャンネルが対応しているので、
チャンネルIDとそのチャンネルのトークンを利用することにしました。

チャンネルのトークンはSlackの連携設定時に発行するものとし、
セキュリティ上の問題が発生した際に再発行できるようにします。

Slackとの連携機能の実装の際のDBと機能の整理

Slackとの連携機能を実装するうえで、DBと機能の整理をします。

データベースは以下のような構成にします。
(sequelizeというORMを利用)

scheduleテーブル

const Schedule = loader.database.define(
  'schedules',
  {
    scheduleId: {
      type: Sequelize.UUID,
      primaryKey: true,
      allowNull: false
    },
    scheduleName: {
      type: Sequelize.STRING,
      allowNull: false
    },
    description: {
      type: Sequelize.TEXT,
      allowNull: false
    },
    createdBy: {
      type: Sequelize.INTEGER,
      allowNull: false
    },
    updatedAt: {
      type: Sequelize.DATE,
      allowNull: false
    },
    roomId: {
      type: Sequelize.STRING
    }
  },
  {
    freezeTableName: true,
    timestamps: false,
    indexes: [
      {
        fields: ['createdBy', 'roomId']
      }
    ]
  }
);

roomテーブル

const Room = loader.database.define(
  'rooms',
  {
    roomId: {
      type: Sequelize.STRING,
      primaryKey: true,
      allowNull: true
    },
    roomToken: {
      type: Sequelize.STRING,
      allowNull: true
    }
  },
  {
    freezeTableName: true,
    timestamps: false
  }
);

次に、追加する機能を整理します。
追加したい機能は以下の通りです。

  • Slackとの連携がされていない場合、slackとの連携ボタンを表示
  • Slackとの連携がされている場合、slackとの連携設定変更ボタンの表示
  • チャンネルIDの登録とトークン発行機能
  • slackとの連係解除機能
  • チャンネルIDの変更機能
  • トークンの再発行機能


これらの機能をまず実装していきます。
ページの関係で次回に続きます。

Botを使った出欠確認機能の実装 - 自作サービスづくり7

今回の記事もWeb開発に関する記事です。
前回はslack上のbotから前々回実装した出欠更新APIを叩きました。
developer-bpeldi2oerkd8.hatenablog.com

このままでは、現在の出欠情報を確認する際にその都度出欠を更新する必要があります。
このため、出欠情報の確認のみをする機能も必要だと考えました。
そこで、今回は出欠確認APIを実装し、botからこのAPIを叩いてみたいと思います。

出欠確認APIの実装

出欠更新APIと同じように実装していきます。
基本的には同じで、変更部分はAvailabilityの更新部分をfindOneに変えるだけです。

    getData()
    .then(([schedule, user, date]) => {
      Availability.findOne({
        where: {
          scheduleId: schedule.scheduleId,
          userId: user.userId,
          dateId: date.dateId
        }
      })
      .then((a) => {
        if(a) {
          res.json({
            status: 'OK',
            data: {
              slackId: user.slackId,
              date: date.date,
              availability: a.availability
            }
          });
        }
        //出欠登録情報がない場合は欠席
        else {
          res.json({
            status: 'OK',
            data: {
              slackId: user.slackId,
              date: date.date,
              availability: 0
            }
          });
        }
      });
    })
    .catch(([scheduleMessage, userMessage, dateMessage]) => {
      res.json({
        status: 'NG',
        error: {
          messages: [scheduleMessage, userMessage, dateMessage]
        } 
      });
    });

データベースに出欠情報が登録されていない場合は欠席として扱っているため、その処理が入っています。

そのほかのコードについてはこちらです。
github.com

エラーメッセージが正しく出力されるように修正

テストコードを書き、正常時は登録が可能になったことが確認できたのですが、予定に存在しない日付を入れるとエラーメッセージが正常に返ってこない問題がありました。

私はエラーが出た場合Promiseのrejectでエラーメッセージを返していたのですが、Javascriptでは複数のPromiseを非同期処理する場合、1つrejectでエラーを返されると、そこでcatch内の処理に入ってしまうそうです。
今回はエラーが発生した場合、そのすべてのエラーメッセージを出力したかったため、エラーが起きた場合でもresolveでエラー状態であることを返し、その後エラーメッセージを配列にしてJSONで返す情報に入れました。

具体的には、以下のようにエラーの場合'error'という文字列を返し、

  if(d) {
    resolve(d);
  }
  else {
    resolve('error');
  }

エラーの場合は、そのエラーメッセージをmessagesという配列に入れるようにしました。

  schedule = schedule === 'error' ? 'このルームIDはシステムに登録されていません' : '';
  user = user === 'error' ? 'このSlackIDはシステムに登録されていません' : '';
  date = date === 'error' ? '入力した日付はこの予定に存在しません' : '';
  const messages = [];
  if(schedule) {
    messages.push(schedule);
  }
  if(user) {
    messages.push(user);
  }
  if(date) {
    messages.push(date);
  }

もう少しきれいに書けるような気もしますが、ひとまずこちらで行きます。

コードの詳細については以下のようになっています。
github.com

Jestでテストコードを書き、問題がないか確認しました。
結果がこちらです。
f:id:bpeldi2oerkd8:20210621205255p:plain
問題なくテストが通っています。

BotからAPIを叩く

APIが完成したので、今度はbotのほうを実装していきます。
出欠更新APIの時と同じように実装します。
違いは、今回はPOSTではなく、GETを用いる点です。

function confirmAvailability(msg, roomId, slackId, dateString){
  const confirm_url = api_url + '/' + roomId + '/users/' + slackId + '/dates/' + dateString;

  msg.http(confirm_url)
  .get() ((err, res, body) => {
    if(err) {
      msg.send(err);
      return;
    }

    const data = JSON.parse(body);
    const availabilityStatus = ['欠席', '不明', '出席'];
    if(data.status === 'OK'){
      msg.send('出欠確認成功:' + '<@' + data.data.slackId + '> さんの' 
        + data.data.date + 'の予定は ' + availabilityStatus[data.data.availability] + ' です');
    } 
    else {
      msg.send('出欠確認失敗:' + '\n' + data.error.messages.join('\n'));
    }
  });
} 

そのほかのコードについてはこちらです。
github.com

動作確認

どちらも完成したため、動作確認をします。
Slack上のbotから決められた形式でbotにメンションすると、結果を返してくれます。
出欠を確認する場合は、

@bot名 確認 日付(月/日の形式)

でメッセージを送ります。

実際に動かした結果がこちらです。
f:id:bpeldi2oerkd8:20210621211353j:plain
エラーの場合はエラーメッセージを返しています。
はじめ6/15の予定が欠席だったのが、出欠更新後出席に変わっていることがわかります。

Webサイト(Lattendance)の表示も出席に更新されています。
f:id:bpeldi2oerkd8:20210621211755j:plain


今回は、出欠確認機能の実装に取り組みました。
次回こそは、APIの認証に取り組みたいと思います。

Hubotから出欠更新APIを叩く-自作サービス作り6

今回の記事もWeb開発の勉強に関する記事です。
前回は出欠更新APIの実装をしました。
developer-bpeldi2oerkd8.hatenablog.com

今回はbotからAPIを叩くことでbot経由で出欠更新ができるようにします。

構成

今回は2つのシステムを開発しています。
図にすると以下の通りです。
f:id:bpeldi2oerkd8:20210610162127j:plain

1つは予定の作成・削除・編集と自らの出欠が更新できるLattendanceです。
ここには、APIとDBが存在しており、外部からAPIを叩くことでも出欠を更新できます。

もう1つは外部から出欠登録を行うbotであるLattendance Botです。
今回はHubotを用いてSlack用のbotを作成しています。
LattendanceのAPIを叩くことで出欠の更新が行えるため、
これをbotを入れたいツールに応じて作り変えることで、
別のコミュニケーションツール(Twitter・ChatWorkなど)に対応することが可能です。

RoomIdの追加

今回は1つの予定につき1つのSlackのチャンネルを作成し、
そこに1つの出欠更新用のbotを入れることを想定しています。
DBのScheduleテーブルにroomIdというカラムを追加します。
RoomIdはチャンネルを右クリックし、「リンクをコピー」でコピーしたリンクの

https://***.slack.com/archives/〇〇〇〇〇〇〇

の〇〇〇〇〇〇〇の部分です。

まずは、RoomIdを登録できるように実装を変更しました。
下の図のようにし、roomIdが重複した場合にはアラートを出すようにしました。
f:id:bpeldi2oerkd8:20210610164804j:plain

f:id:bpeldi2oerkd8:20210610164817j:plain

コードについては以下のリンクを確認してください。
github.com

HubotからAPIを叩く

このようにSlackのチャンネルとLattendance上の予定を1:1で結びつけることができたら、
APIを叩くbotのほうを作成していきます。

Slack上のbot上でわかる情報は以下のようになっています。

情報 意味
roomId SlackのチャンネルID
(Lattendance上の予定と1:1で紐づいている)
SlackId SlackでのユーザーID
(Lattendance上のユーザーと1:1で紐づいている)
dateString 予定の日付
(フォーマットはYYYY-MM-DD)
availability 出欠情報
(欠席:0, 不明:1, 出席:2でLattendance上のと同じ)
(この情報のみPOSTデータとして送る)

この情報をもとにAPI側でDBの出欠情報を更新し、
以下のようにJSONとしてstatus(OK: 成功, NG: 失敗)と関連データが返ってきます。

成功した場合

res.json({
  status: 'OK',
  data: {
    slackId: user.slackId,
    date: date.date,
    availability: availability
  }
});

失敗した場合

res.json({
  status: 'NG',
  error: {
    messages: [scheduleMessage, userMessage, dateMessage]
  } 
});

このJSONをパースし、返ってきた結果に応じてメッセージを返します。
その部分を実装したコードは以下の通りです。

function updateAvailability(msg, roomId, slackId, dateString, availability){
  const update_url = api_url + '/' + roomId + '/users/' + slackId + '/dates/' + dateString;
  const param = JSON.stringify({
    availability: availability
  });
    
  msg.http(update_url)
  .header('Content-Type', 'application/json')
  .post(param) ((err, res, body) => {
    if(err) {
      msg.send("Error :( " + err);
      return;
    }

    const data = JSON.parse(body);
    const availabilityStatus = ['欠席', '不明', '出席'];
    if(data.status === 'OK'){
      msg.send('出欠更新完了:' + '<@' + data.data.slackId + '> さんの' 
        + data.data.date + 'の予定は ' + availabilityStatus[data.data.availability] + ' です');
    } 
    else {
      msg.send('出欠更新失敗:' + '\n' + data.error.messages.join('\n'));
    }
  });
}

動作確認

ここまで作ったものが実際に動作するか確認します。
今まで作った2つのシステムを起動し、bot経由で出欠が更新できるか確認します。

変更前はこのようになっています。
f:id:bpeldi2oerkd8:20210612132902p:plain

ここで、Slackで出欠情報を更新します。
f:id:bpeldi2oerkd8:20210612132933p:plain

すると、出欠情報が更新されているのが確認できます。
f:id:bpeldi2oerkd8:20210612132943p:plain


今回はHubotからAPIを叩き、出欠情報を更新できるようにしました。
次回は、APIに認証をつけることでセキュリティを高めたいと思います。

Web開発の勉強12- 自作サービス作り5

今回の記事もWeb開発の勉強の記事です。
前回の記事では、GitHubアカウントでログインして予定の作成・削除・編集と出欠登録ができるシステムを完成させました。
developer-bpeldi2oerkd8.hatenablog.com

とはいっても前回までの内容はN予備校の教材の内容に近かったため、オリジナルの機能である「Slackのbotを使って出欠登録をする」ことを目指したいと思います。

準備

どのように機能を実現するかを考えます。
本体の出欠登録システムであるlattendance (1)と、slackのbotとしてのlattendance-bot (2)の2つを用意しています。
アプローチとしては、次の2つを思いつきました。

  • 2つのシステムでデータベースを共有し、(2)で直接データベース内の出欠情報を更新する。
  • (2)から(1)の出欠更新APIを叩き、出欠情報を更新する。(データベースは(1)に存在する。)

今後、botをSlack以外にも拡張するかもしれないことを考えると、外部からAPIを叩けばよいだけの2つ目を採用することにしました。

出欠更新APIの実装

早速実装に移ります。
今回は、APIにアクセス制限はつけず、まずは正しく動作するAPIを実装することに重きを置きます。

routesディレクトリ下にv1というディレクトリを作り、そこにAPIに関するファイルを入れることにします。
APIのURLは /api/v1/schedules/:roomId/users/:slackId/dates/:dateString としました。
dateStringのフォーマットは、YYYY-MM-DDとします。

完成したものがこちらです。
github.com

Promiseやasync/awaitを使い、実装しました。
日付の先頭が0のとき、うまく動かなかったため修正しました。

Jestを使ってテストコードも書きました。
テストもしっかり通り、今のところは問題ないようです。
github.com
f:id:bpeldi2oerkd8:20210609001313p:plain


今回はここで終わります。
次回はbotの実装と、それに伴うデータベースの変更に取り組みます。

Web開発の勉強11- 自作サービス作り4

およそ半年ぶりとなってしまいました。
今回はWeb開発の勉強の記事です。
前回は出席登録機能以外を完成させました。
developer-bpeldi2oerkd8.hatenablog.com

今回は出席登録機能とURL共有機能の追加、Herokuへのデプロイまで完成させました。

完成したもの

GitHubにも載せましたが、念のためデモ動画を載せます。

経過

経過の詳細は以下のコミット履歴を確認してください。
github.com

まずは概要を説明します。
前回からの変更点は以下の3つです。

  • 出欠登録機能の追加
  • Herokuへデプロイするためにパッケージ等のアップグレード

それぞれ簡単に説明します。

出欠登録機能の追加

前回完成していなかった出欠登録機能を完成させました。
前回はbotを使ってAPIを叩いて出欠登録ができるようにしようと思っていたのですが、時間の関係でいったん諦めました。
その代わりにN予備校の教材にあったAjaxを使った登録を実装しました。
この部分については、今後時間があればbotを使って出欠登録ができるように修正したいと思います。

URL共有機能の追加

予定ページにそのページのURLを表示し、Copyボタンを押すとクリップボードにコピーされるように変更しました。
実装はjQueryを用いて実装しています。
jQueryのclickメソッドとselectメソッドは非推奨らしいので、onメソッドとtriggerメソッドを代わりに使いました。
Bootstrap4がjQueryを必要とするため、jQueryを使いましたが、今のトレンドはReactですかね...

Herokuへのデプロイ

基本的には今までやったようにデプロイするだけでした。
Node.jsのバージョンを上げるとなぜか動かなかったのですが、pgモジュールのバージョンを上げることで解決しました。

感想

事情があって時間がかなり空きましたが、ひとまずサービスとして動くように完成できたと思います。
botとの連携については時間がかかりそうなので今回はパスしましたが、時間があるときに追加したいと思っています。
あと、今年からN予備校の入門コースの教材がDocker対応になったのでそれも時間があれば試したいと思います。

【メモ】Windows10で画面録画をするとき「録画が動作していません エラー:0x82323007」とエラーが出て録画できない場合の対処法

Windows10で画面録画をしようと思ったのですが、「録画が動作していません エラー: 0x82323007」というエラーメッセージが出て、録画できなかったので対処法を探しました。

対処法

Windows10のバージョンを1909から20H2にバージョンアップすることで解決。

原因

おそらくDirectX 12 UltimateをサポートしているバージョンでないとXbox Game Barが録画できないようになったため。
なので、バージョン2004以上に上げれば動作するようになるはず。

Web開発の勉強10- 自作サービス作り3

今回もWeb開発に関する記事です。
前回は出欠登録に使うbotを側だけ作りました。
developer-bpeldi2oerkd8.hatenablog.com

今回は予定の作成・編集・削除ページとその処理を書き、デザイン面も決めました。
後はメインのbotを使って出席登録をできるようにするだけです。
(おそらくここが大変だと思いますが)

作ったもの

削除確認ダイアログはBootstrapのModalを使って実装しました。

slackIDはユーザー一人につき1回のみslackのIDを登録することになっています。
ここのあたりの仕様はまだ詰め切れてないので、残りの部分を実装しながら修正します。

各フォームはcsurfモジュールを用いてCSRF対策を実装済みです。

  • 予定一覧ページ

f:id:bpeldi2oerkd8:20201110232303p:plain

  • 予定確認ページ

f:id:bpeldi2oerkd8:20201110232354p:plain

  • 予定作成ページ

f:id:bpeldi2oerkd8:20201110232609p:plain

  • 予定編集ページ

f:id:bpeldi2oerkd8:20201110232501p:plain

  • 予定削除確認ダイアログ

f:id:bpeldi2oerkd8:20201110232543p:plain

  • slackID登録ページ

f:id:bpeldi2oerkd8:20201110232744p:plain

経過

細かな経過はこちらのコミット履歴をご覧ください。
github.com

工夫した点は削除ボタンを押した際、確認ダイアログを作った点です。
また、苦労した点はHerokuにデプロイする際、ローカルで出なかったエラーが出て苦戦した点です。

原因はいくつかありました。
1つ目は、bootstrapとjQueryをバージョンアップさせた際、yarn installをし直してyarn.lockファイルを更新していなかったことです。
2つ目は、不要なrequireやrequireの位置がおかしかったなどrequire関係のエラーです。

ひとまず出席登録以外の機能は完成したと思います。

まだまだ改善の余地はあると思いますが(日程の追加・編集方法など)、今後はbotを使った出欠登録を完成させることを優先させたいと思います。

感想

時間はかかりましたが、出席登録機能以外を完成させました。

残りはbotを使った出席登録ですが、以前作ったlattendance-botからlattenndanceのAPIを叩き、出欠情報をデータベースに登録させたいと考えています。

うまくいくかはわかりませんが、何とか完成させたいと思います。