Node.js
Heroku
Express
PostgreSQL

ExpressとPostgresqlを使ってHerokuでログインページを作る

今回の内容

前回、HerokuでExpressアプリを作る---基本設定、の続き。
今回はPostgreSQLを設定しユーザーを設定した簡単なテーブルを使ったログインページを作る。

環境整備

データーベースにはクレジットカード登録なしで使えるPostgreSQLを選んだ。
Postgresqlをnode.jsで使うためのライブラリとしてPromise形式になっているpg-promiseを使う。
リクエストにログイン情報を載せるためにbodyを使うのでbody-parser、cookieによるログインも使うのでcookie-parserを使う。
これらは以下のコマンドでできる

$ yarn add body-parser cookie-parser pg-promise
$ heroku addons:create heroku-postgresql:hobby-dev

PostgreSQLの整備

ログインユーザー用のデータとしてloginUserを作っておく。
herokuのpostgreSQLにはheroku pg:psqlでログインできるので以下のようにしてデータを作っておく。

$ heroku pg:psql
> create table loginUser ( text name, text password);
> insert into member( name, password ) values ( 'admin', 'hogehoge' );
> select * from loginUser;
//*** ユーザー情報の表示 ***//
> \q

index.js

インストールしたパッケージとログインページの作成をする。ログインページはprivate/ディレクトリに置いておく(公開ページはpublic/)
ログインはPOSTで受付、パスワードとユーザー名が一致したらステータス200で一致しない場合、401で返す1
user_only.htmlにアクセスがあった場合、クッキーを見てクッキーのユーザー名とパスワードが一致すればprivate/user_only.htmlを一致しなければログインページを返すようにしてある。注意点としてsendStatusをifの中書いてsendを外に書くとindex.js自体が死んだ。

index.js
const path=require('path');

const express=require('express');
const app=express();
const cookieParser=require('cookie-parser');
const bodyParser=require('body-parser');

// noWarning: true の設定がないと落ちる。databaseURLはherokuでは環境変数に入っている
const pgPromise=require('pg-promise')({ noWarnings: true });
const databaseURL=process.env.DATABASE_URL;

const database=pgPromise(databaseURL);

app.set('port', process.env.PORT || 5050);
app.use(express.static(path.join(__dirname, 'public')));
app.use(log.access.add);
app.use(cookieParser());
// bodyParserの設定 (Json形式用)
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

app.post('/login', async (req, res)=>{
    const user= await database.any('select * from loginUser').filter(a=> a.name===req.body.name);

    if( user.length!==1 || user[0].password!==req.body.password ) res.sendStatus(401);
    else res.send('OK');
});

app.get('/status.html', async (req, res)=>{
    const user= await database.any('select * from loginUser').filter(a=> a.name===req.body.name);

    if( user.length!==1 || user[0].password!==req.cookies.password ){
        res.sendFile(path.join(__dirname, '/private/login.html')); 
    }
    else{ res.sendFile(path.join(__dirname, '/private/user_only.html')); }
});

クライアント側

private/login.js
<!doctype html>
<html>
  <head>
     <script src="js/password.js"></script>
  </head>
  <body>
     ユーザー名<input type="text" id="name"></input>
     パスワード<input type="password" id="password"></input>
     <button type="button" id="send">送信</button>
  </body>
</html>

クライアント側では送信ボタンが押されるとinputの情報をとってきてResponseを見て成功なら確認してクッキーに書き込んでページ再読込でサーバー側でhtmlファイルを切り替えている

js/private/js/login.js
window.addEventListener('DOMContentLoaded', ()=>{
    document.getElementById('send').addEventListener('click', ()=>{
        const name=document.getElementById('name').value;
        const password=document.getElementById('password').value;

        const method='POST';
        const headers={ 'Accept': 'application/json', 'Content-Type': 'application/json' };
        const body=JSON.stringify({ 'name': name, 'password': password });
        fetch('login', { method, headers, body }).then(res=>{
            if( res.ok ){
                if( getCookie('name')==null && getCookie('password')==null &&
                    confirm('パスワードを保存しますか?\n(クッキーを使用します)') ){
                    console.log('パスワードを保存しました');
                    document.cookie=`name=${name}`
                    document.cookie=`password=${password};
                }
                location.reload();
            }
            else alert('ログインに失敗しました');
        });
    });
});

最後に

今回は簡単なデータベースを使ってサーバー側でhtmlファイルを切り替えた。色々やるためにはSQLの知識などは全く足りない。
クライアント側のUIの整備などもほとんどしていないので工夫が必要であるが簡単な使い方を紹介しました。