bpeldi2oerkd8の開発日誌

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

デザイン改善と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実装について取り組みたいと思います。