LoginSignup
0
0

Node.js(Express)×PostgressSQLを使ったチャットアプリ 3

Last updated at Posted at 2023-12-20

ユーザー機能の実装

postgressにユーザー情報を保存するテーブルを作成

CREATE TABLE users (
 user_id SERIAL NOT NULL,
 user_name VARCHAR(255) NOT NULL,
 user_email VARCHAR(255) NOT NULL,
 user_password VARCHAR(255) NOT NULL,
 created_at TIMESTAMP NOT NULL,
 PRIMARY KEY (user_id)
);

誰がchannelを投稿したのか見られるように、channelmessageのテーブルにuser_idのカラムを追加します。

/*channelテーブル*/
ALTER TABLE channel ADD COLUMN user_id INTEGER;

/*messagesテーブル*/
ALTER TABLE messages ADD COLUMN user_id INTEGER;

ユーザー登録機能

app.jsにコードを追加します。

// 中略
const message = require('./routes/message');
const signup = require('./routes/signup'); // 追加

const app = express();

// 中略
app.use('/message', message); 
app.use('/signup', signup); //追加

routes/signup.jsを作成します。

const express = require('express');
const router = express.Router();
const dayjs = require('dayjs');
const pool = require('../dbConnection');

router.get('/', (req, res, next) => {
  res.render('signup', {
    title: '新規会員登録'
  });
});

router.post('/', async (req, res, next) => {
  try {
    const userName = req.body.user_name;
    const email = req.body.email;
    const password = req.body.password;
    const createdAt = dayjs().format('YYYY-MM-DD HH:mm');

    const signupQuery = 
      'INSERT INTO users (user_name, user_email, user_password, created_at) VALUES ($1, $2, $3, $4)';

    // 既に登録されているメールアドレスかどうかを確認
    const emailExistsQuery = 'SELECT * FROM users WHERE user_email = $1 LIMIT 1';
    const emailExists = await pool.query(emailExistsQuery, [email]);

    if (emailExists.rows.length) {
      res.render('signup', {
        title: '新規会員登録',
        emailExists: '既に登録されているメールアドレスです'
      });
    } else {
      // ユーザーの新規登録
      await pool.query(signupQuery, [userName, email, password, createdAt]);
      res.redirect('/signup');
    }

  } catch (error) {
    console.error(error);
    res.status(500).send('内部サーバーエラー');
  }
});

module.exports = router;

views/signup.ejsを作成します。

<!DOCTYPE html>
<html lang="ja">

  <head>
    <title><%= title %></title>
    <link rel='stylesheet' href='/stylesheets/style.css' />
  </head>

<body>
  <div class="top_link">
    <a href="/" class="btn">← トップへもどる</a>
  </div>

  <p class="main-title"><%= title %></p>

  <% if (typeof emailExists !== 'undefined') { %>
    <p class="error"><%= emailExists %></p>
  <% } %>

  <form action="/signup" method="post" class="form">
    <div class="userName">
      <span class="label">userName</span>
      <input type="text" name="user_name" class="input" required>
    </div>

    <div class="email">
      <span class="label">Email</span>
      <input type="email" name="email" class="input" required>
    </div>

    <div class="password">
      <span class="label">password</span>
      <input type="password" name="password" class="input" required>
    </div>

    <div class="button-area">
      <button type="submit" class="submit">新規会員登録</button>
    </div>
  </form>
</body>
</html>

/signupにアクセスしてユーザー登録をしてみてください

スクリーンショット 2023-12-21 2.06.08.png

スクリーンショット 2023-12-21 2.12.48.png

すでに登録しているメールアドレスを登録してみてください
スクリーンショット 2023-12-21 2.14.34.png

エラーが表示されていればOKです

passwordをhush値にしてデータベースに保存する

データベースに入力したままのpasswordを保存するのは良く無いのでモジュールを使ってpasswordをhush値にしてデータベースに登録します

npm install --save bcrypt

routes/signup.jsにコード追加

const express = require('express');
const router = express.Router();
const dayjs = require('dayjs');
const bcrypt = require('bcrypt'); //追加
const pool = require('../dbConnection');

router.get('/', (req, res, next) => {
  res.render('signup', {
    title: '新規会員登録'
  });
});

router.post('/', async (req, res, next) => {
  try {
    const userName = req.body.user_name;
    const email = req.body.email;
    const password = req.body.password;
    const createdAt = dayjs().format('YYYY-MM-DD HH:mm:ss');

    const signupQuery = 'INSERT INTO users (user_name, user_email, user_password, created_at) VALUES ($1, $2, $3, $4)';

    // 既に登録されているメールアドレスかどうかを確認
    const emailExistsQuery = 'SELECT * FROM users WHERE user_email = $1 LIMIT 1';
    const emailExists = await pool.query(emailExistsQuery, [email]);

    if (emailExists.rows.length) {
      res.render('signup', {
        title: '新規会員登録',
        emailExists: '既に登録されているメールアドレスです'
      });
    } else {
      // パスワードのハッシュ化
      const hashedPassword = await bcrypt.hash(password, 10);

      // ユーザーの新規登録
      //password → hashedPasswordに変更
      await pool.query(signupQuery, [userName, email, hashedPassword, createdAt]);

      res.redirect('/signup');
    }

  } catch (error) {
    console.error(error);
    res.status(500).send('内部サーバーエラー');
  }
});

module.exports = router;

スクリーンショット 2023-12-21 2.23.56.png

こうなってればOK

ログイン機能の実装

ログイン情報を保持する為に必要なモジュールをインストールします

npm install --save express-session

app.jsを書き換えます

// 中略
const logger = require('morgan');
const session = require('express-session'); // 追加

const indexRouter = require('./routes/index');
const message = require('./routes/message');
const signup = require('./routes/signup');
const login = require('./routes/login'); // 追加

// 中略
app.use(express.static(path.join(__dirname, 'public')));

// 追加 session
app.use(session({
  secret: 'keyboard cat',
  resave: false,
  saveUninitialized: true
}));

app.use('/', indexRouter);
app.use('/message', message);
app.use('/signup', signup);
app.use('/login', login); // 追加
// 中略

routes/login.jsを作成します。

const express = require('express');
const router = express.Router();
const bcrypt = require('bcrypt');
const pool = require('../dbConnection');

router.get('/', function (req, res, next) {
  if (req.session.user_id) {
    res.redirect('/');
  } else {
    res.render('login', {
      title: 'ログイン'
    });
  }
});

router.post('/', async function (req, res, next) {
  try {
    const email = req.body.email;
    const password = req.body.password;

    const query = 'SELECT user_id, user_password FROM users WHERE user_email = $1 LIMIT 1';

    const result = await pool.query(query, [email]);

    if (result.rows.length > 0) {
      const user = result.rows[0];
      const userId = user.user_id;

      // パスワードの比較
      const isPasswordValid = await bcrypt.compare(password, user.user_password);

      if (isPasswordValid) {
        req.session.user_id = userId;
        res.redirect('/');
        return; // ログイン成功時に関数を終了
      }
    }

    // 行が見つからないか、パスワードが無効な場合のエラーメッセージ
    res.render('login', {
      title: 'ログイン',
      noUser: 'メールアドレスとパスワードが一致するユーザーはいません'
    });

  } catch (error) {
    console.error(error);
    res.status(500).send('内部サーバーエラー');
  }
});

module.exports = router;

views/login.ejsを作成します。

<!DOCTYPE html>
<html lang="ja">

  <head>
    <title><%= title %></title>
    <link rel='stylesheet' href='/stylesheets/style.css' />
  </head>
  
  <body>

    <div class="top_link">
      <a href="/" class="btn">← トップへもどる</a>
    </div>

    <p class="main-title"><%= title %></p>

    <% if (typeof noUser !== 'undefined') { %>
      <p class="error"><%= noUser %></p>
    <% } %>

    <form action="/login" method="post" class="form">

      <div>
        <span class="label">Eメール</span>
        <input type="email" name="email" class="input" required>
      </div>

      <div>
        <span class="label">パスワード</span>
        <input type="password" name="password" class="input" required>
      <div>

      <div class="button-area">
        <button type="submit" class="submit">ログイン</button>
      </div>

    </form>
  </body>
</html>

ユーザー登録画面で登録したユーザーでログインしてみてください。
エラーが出ずにログインできたらOKです

ログインしているユーザーを表示させる

app.jsを書き換える

// 中略
const login = require('./routes/login');
const setUser = require('./routes/setUser'); // 追加

const app = express();
// 中略

app.use('/', setUser, indexRouter); // 変更
app.use('/users', usersRouter);
app.use('/message', setUser, message); // 変更
app.use('/signup', signup);
app.use('/login', login);
// 中略

routes/setUser.jsを作成します

const pool = require('../dbConnection');

module.exports = async (req, res, next) => {
  const userId = req.session.user_id;

  if (userId) {
    try {
      const query = 'SELECT user_id, user_name FROM users WHERE user_id = $1';
      const result = await pool.query(query, [userId]);

      if (result.rows.length) {
        res.locals.user = result.rows[0];
      }
    } catch (error) {
      console.error(error);
    }
  }
  next();
};

views/index.ejsを書き換えます

<% if (typeof user !=='undefined' ) { %>
  <span class="login-user">
    <%= user.user_name %>さんとしてログインしています
  </span>
<% } %>

views/message.ejsを書き換えます

<% if (typeof user !=='undefined' ) { %>
  <span class="login-user">
    <%= user.user_name %>さんとしてログインしています
  </span>
<% } %>

ログアウト機能を実装

app.jsを変更します

// 中略
var login = require('./routes/login');
var logout = require('./routes/logout'); // 追加
var setUser = require('./routes/setUser');

const app = express();
// 中略

app.use('/login', login);
app.use('/logout', logout); // 追加

routes/logout.jsを作成します

const express = require('express');
const router = express.Router();

router.get('/', (req, res, next) => {
  req.session.destroy();
  res.redirect('/login');
});

module.exports = router;

ログアウトされていればOKです

ユーザー登録ボタン、ログインボタン、ログアウトボタンを作成する

自走課題です、難しければDiscordで質問してください。

条件
・ ログインしていない状態の時は、ユーザー登録ボタンログインボタンの表示
・ ログインしている状態の時は、ユーザー登録ボタンログアウトボタンの表示

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0