Node.jsでログインサーバーを作る - 2

# 初めに
今回は、前回のログインサーバーを改造します（？）
# 前回とちがうところ
前回は一つのユーザーまでしか認証ができませんでしたが、今回はユーザーデータをjson形式で管理して複数のユーザーの認証ができるようにします

# ファイル構成
今回のファイル構成は以下の通りです。
```
---|--public |
             |--main.html
             |--style.css
             |--script.js
    

   |--Server.js
   |--userdata.json
   
```

# 準備
まず、以下のコマンドをcmdで実行して、ログインサーバーのフォルダーに移動ください
```
cd C:\Users\<Your_Username>\Downloads\<Your_Project_FolderName>
```
※仮のpathです。実行するときはC:\~の部分を作成したフォルダのpathに変更してください

次に、以下のコマンドを入力します
```
npm install express cors fs
```
※node.jsをインストールしていない方は、[こちらから](https://nodejs.org/dist/v20.16.0/node-v20.16.0-x64.msi)node.jsをダウンロードしてください。

# コピペ
以下のコードをコピペしましょう
サーバー側のjavascript
```javascript:Server.js
const express = require('express');
const cors = require('cors');
const fs = require('fs');
const path = require('path');

const app = express();
app.use(express.static(path.join(__dirname, 'public')));
app.use(cors());
app.use(express.json());

app.post('/login', (req, res) => {
    const name = req.body.User;
    const pass = req.body.Password;

    if (!name || !pass) {
        return res.status(400).json({ error: 'ユーザー名またはパスワードが不足しています。' });
    }

    try {
        const userdata = fs.readFileSync('Userdata.json', 'utf-8');
        const ParseData = JSON.parse(userdata);

        const Fdata = ParseData.find(Udata => Udata.username === name && Udata.Password === pass);

        if (Fdata) {
            console.log('認証成功:', Fdata);
            res.json(Fdata);
        } else {
            res.status(401).json({ error: '認証に失敗しました。ユーザー名またはパスワードが正しくありません。' });
        }
    } catch (error) {
        console.error('エラーが発生しました:', error.message);
        res.status(500).json({ error: 'サーバーエラーが発生しました。' });
    }
});
app.post('/signup', (req, res) => {
    const name = req.body.User;
    const pass = req.body.Pass;
    const age = req.body.age;

    if (!name || !pass || !age) {
        return res.status(400).json({ error: 'ユーザー名またはパスワードまたは年齢等の情報が不足しています。' });
    }

    try {
        const Fdata = fs.readFileSync('Userdata.json', 'utf8');
        const ParseData = JSON.parse(Fdata);

        if (ParseData.find(Users => Users.username === name)) {
            return res.status(409).json({ error: "既に同じユーザー名が存在しています" });
        }

        // 新しいユーザー情報の作成
        const NewUserConfig = {
            username: name,
            Password: pass,
            age: age
        };

        // ユーザーデータに追加して保存
        ParseData.push(NewUserConfig);
        fs.writeFileSync('Userdata.json', JSON.stringify(ParseData, null, 2), 'utf8');

        res.json({ message: "サインアップが完了しました。" });
    } catch (error) {
        console.error('エラーが発生しました:', error.message);
        res.status(500).json({ error: 'サーバーエラーが発生しました。' });
    }
});

app.get('/', (req, res) => {
    res.sendFile(path.join(__dirname, 'public', 'main.html'));
});

app.listen(3000, () => {
    console.log('server running on port 3000.');
});
```
html
```html:main.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="load.css">
  <link rel="stylesheet" href="style.css">
  <title>ログイン</title>
 
</head>
<body>
<div id="Domcontainer"></div>
  <script src="script.js"></script>
</body>
</html>
```
クライアント側のjs
```javascript:script.js
function afterAddEvent(EType) {
    switch(EType) {
        case "login":
            document.getElementById("loginForm").addEventListener("submit", function(event) {
                event.preventDefault();
                fetch('/login', {
                    method: "POST",
                    headers: {
                        "Content-Type": "application/json"
                    }, 
                    body: JSON.stringify({
                        User: document.getElementById('email').value,
                        Password: document.getElementById('password').value
                                        })
                })
                .then(response => response.json())
                .then(data => {
                    if (data.error) {
                        ChangeHTML('error'); 
                        setTimeout(() => {
                            document.getElementById('ErrorContent').textContent = data.error;
                        }, 500);
                        return;
                    }
                    ChangeHTML('Home');
                    setTimeout(() => {
                        document.getElementById('Main-Content').textContent = `Welcome back ${data.username}`;
                        document.getElementById('HomeMain').innerHTML = `
                        <p>mailaddress: ${data.username}</p>
                        <p>age: ${data.age}</p>`;
                    }, 500);
                })
                .catch(error => {
                    console.error("Error:", error);
                    ChangeHTML('error');
                    setTimeout(() => {
                        document.getElementById('ErrorContent').textContent = "FetchError: サーバーとの通信に失敗しました。";
                    }, 500);
                });
            });

            document.getElementById("passShow").addEventListener("change", function() {
                const passwordInput = document.getElementById("password");
                passwordInput.type = this.checked ? "text" : "password";
            });
            break;
            case "signup":
                document.getElementById("signupForm").addEventListener("submit", function(event) {
                    event.preventDefault(); // デフォルトのフォーム送信を防ぐ
                    // サインアップのデータ送信
                    
                    fetch('/signup', {
                        method: "POST",
                        headers: {
                            "Content-Type": "application/json"
                        }, 
                        body: JSON.stringify({
                            User: document.getElementById('email').value,
                            Pass: document.getElementById('password').value,
                            age: document.getElementById('age').value
                        })
                    })
                    .then(response => response.json())
                    .then(data => {
                        if (data.error) {
                            ChangeHTML('error');
                            setTimeout(() => {
                                document.getElementById('ErrorContent').textContent = data.error;
                            }, 500);
                            return;
                        }
                        // サインアップ成功時の処理
                        ChangeHTML('login');
                        setTimeout(() => {
                            document.getElementById('Main-Content').textContent = "アカウント作成が完了しました。ログインしてください。";
                        }, 500);
                    })
                    .catch(error => {
                        console.error("Error:", error);
                        ChangeHTML('error');
                        setTimeout(() => {
                            document.getElementById('ErrorContent').textContent = "FetchError: サーバーとの通信に失敗しました。";
                        }, 500);
                    });
                });
                break;
        // 他のケース...
    }
}

window.onload = ChangeHTML('login');


function ChangeHTML(HTMLtype) {
    let NewHTML = "";
    switch(HTMLtype) {
        case "login":
            NewHTML = `
               <div class="login-container">
                <h1>ログイン</h1>
                <form id="loginForm">
                    <input type="text" id="email" placeholder="メールアドレス" required>
                    <input type="password" id="password" placeholder="パスワード" required>
                    <br><input type="checkbox" id="passShow"> パスワードを表示
                        <button class="login-button" type="submit">次へ</button>
                </form>
                <p>アカウントをお持ちでないですか？ <a href="#" onclick="ChangeHTML('signup')">作成する</a></p>
              </div>
            `;
            setTimeout(() => {
                afterAddEvent("login");
            }, 500);
            break;
            
        case "signup":
            NewHTML = `
              <div class="signup-container">
                <h1>サインアップ</h1>
                <form id="signupForm">
                <input type="text" id="email" placeholder="メールアドレス" required>
                    <input type="password" id="password" placeholder="パスワード" required>
                    <input type="number" id="age" placeholder="年齢を入力">
                        <button class="login-button" type="submit">次へ</button>
                </form>
                <p>既にアカウントをお持ちですか？ <a href="#" onclick="ChangeHTML('login')">ログインする</a></p>
              </div>
            `;
            setTimeout(() => {
                afterAddEvent("signup");
            }, 500);
            break;

        case "error":
            NewHTML = `
              <div class="login-container">
                <h1>Error</h1>
                <p id="ErrorContent"></p>
              </div>`;
            break;

        case "Home":
            NewHTML = `
              <div class="login-container">
                <h1>Home</h1>
                <h1 id="Main-Content"></h1>
                <div id="HomeMain"></div>
              </div> 
            `;
            break;

        default:
            NewHTML = `
              <div class="error-container">
                <h1>Error</h1>
                <p>エラー：ページが存在しないよ！</p>
                <p>error: page is not found!</p>
              </div>
            `;
    }
    document.getElementById('Domcontainer').innerHTML = NewHTML;
}
window.onload = ChangeHTML('login');
```
css
```css:style.css
body {
    font-family: Arial, sans-serif;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    margin: 0;
    background-color: #f5f5f5;
  }
  .login-container {
    width: 360px;
    padding: 40px;
    background-color: #fff;
    border: 1px solid #ddd;
    border-radius: 8px;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
    text-align: center;
  }
  .login-container img {
    width: 75px; 
    margin-bottom: 20px;
  }
  h1 {
    font-size: 24px;
    color: #202124; /* テキスト色 */
    margin: 0 0 20px;
  }
  p {
    font-size: 14px;
    color: #5f6368; /* 説明文の色 */
    margin: 0 0 20px;
  }
  input[type="email"],
  input[type="text"],
  input[type="password"],
  input[type="number"] {
    width: 100%;
    padding: 12px;
    margin: 8px 0;
    border: 1px solid #ddd;
    border-radius: 4px;
    box-sizing: border-box;
  }
  .button-container {
    margin-top: 20px;
  }
  .login-button {
    width: 100%;
    padding: 10px;
    background-color: #0078d4;
    color: #fff;
    border: none;
    border-radius: 4px;
    font-size: 16px;
    cursor: pointer;
  }
  .login-button:hover {
    background-color: #005ea1; /* ホバー時の色 */
  }
  .forgot-email,
  .create-account {
    font-size: 14px;
    color: #1a73e8;
    text-decoration: none;
    display: inline-block;
    margin-top: 20px;
  }
  .forgot-email:hover,
  .create-account:hover {
    text-decoration: underline;
  }
  .error-message {
    color: red;
    margin-top: 15px;
    font-size: 14px;
  }
  ```
  json（確認したら自動的に生成されませんでした）
  ```json:Userdata.json
  [
  
  ]
  ```
  ## 説明（？）
  今回はfsを使って、複数ユーザーのログインを実装しています
  ちなみに、Userdata.jsonを削除した状態でやると、サーバーでエラーが起きます

  # 最後に
  いかがだったでしょうか？今回はfsとexpressを使って、ログイン機能を作りました。ちなみに、今回もsignupの処理がうまくいかなかったので、ChagtGPTに修正してもらいました。
  それではまたお会いしましょう！